Skip to content

Conversation

@lu-yg
Copy link
Collaborator

@lu-yg lu-yg commented Dec 19, 2025

English | 简体中文

PR

PR Checklist

Please check if your PR fulfills the following requirements:

  • The commit message follows our Commit Message Guidelines
  • Tests for the changes have been added (for bug fixes / features)
  • Docs have been added / updated (for bug fixes / features)
  • Built its own designer, fully self-validated

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Documentation content changes
  • Other... Please describe:

Background and solution

What is the current behavior?

Issue Number: N/A

What is the new behavior?

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

Summary by CodeRabbit

  • Bug Fixes

    • Service exceptions now return 500 Internal Server Error instead of 200 OK.
  • New Features

    • Optional Bearer token support for API endpoints.
    • New encrypt-key endpoint and token DTO to provide SM4-encrypted API key tokens.
    • Service API extended with a token-generation method.
  • Improvements

    • Streaming responses include Cache-Control: no-cache and buffering disabled for better real-time delivery.

✏️ Tip: You can customize this high-level summary in your review settings.

lu-yg and others added 24 commits September 27, 2025 19:22
…into feat/rag

# Conflicts:
#	base/src/main/java/com/tinyengine/it/common/utils/ImageThumbnailGenerator.java
#	base/src/main/java/com/tinyengine/it/controller/VectorStorageController.java
#	base/src/main/java/com/tinyengine/it/rag/entity/EmbeddingMatchDto.java
#	base/src/main/java/com/tinyengine/it/rag/entity/VectorDocument.java
#	base/src/main/java/com/tinyengine/it/rag/service/StorageService.java
#	base/src/main/java/com/tinyengine/it/service/app/impl/v1/AiChatV1ServiceImpl.java
@coderabbitai
Copy link

coderabbitai bot commented Dec 19, 2025

Walkthrough

The changes add SM4 key utilities and a BouncyCastle dependency, introduce token generation/consumption using an EKEY_ prefix, accept Bearer tokens in controller endpoints and add /encrypt-key, adjust streaming headers, and change ServiceException handler response code from 200 to 500.

Changes

Cohort / File(s) Summary
Encryption Infrastructure
pom.xml, base/src/main/java/com/tinyengine/it/common/utils/SM4Utils.java
Added BouncyCastle dependency and new SM4Utils providing SM4 key generation and SM4/ECB/PKCS5Padding encrypt/decrypt helpers with Base64 input/output and provider registration.
Token Management & DTOs
base/src/main/java/com/tinyengine/it/model/dto/AiToken.java
Added AiToken DTO (Lombok @Data) with token field and constructors for token wrapping.
Service Layer
base/src/main/java/com/tinyengine/it/service/app/v1/AiChatV1Service.java, base/src/main/java/com/tinyengine/it/service/app/impl/v1/AiChatV1ServiceImpl.java
Added getToken(String apiKey) to interface and implementation; implementation encrypts/decrypts API keys (EKEY_ prefix), enhances response processing and streaming error/interrupt handling, normalizes URLs (including hash-terminated bases), and adds logging.
Controller Updates
base/src/main/java/com/tinyengine/it/controller/AiChatController.java
aiChat and completions now accept optional Authorization (Bearer) header and propagate it; added POST /encrypt-key endpoint returning Result<AiToken>; streaming responses set Cache-Control: no-cache and X-Accel-Buffering: no.
Error Handling
base/src/main/java/com/tinyengine/it/common/exception/GlobalExceptionAdvice.java
Changed ServiceException handler HTTP status from 200 OK to 500 Internal Server Error.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Controller as AiChatController
    participant Service as AiChatV1Service
    participant SM4 as SM4Utils
    participant Response

    rect rgb(220,240,220)
    Note over Client,Service: Token generation (/encrypt-key)
    Client->>Controller: POST /encrypt-key { apiKey }
    Controller->>Service: getToken(apiKey)
    Service->>SM4: encryptECB(apiKey)
    SM4-->>Service: encrypted(Base64)
    Service-->>Controller: "EKEY_<encrypted>"
    Controller-->>Response: Result<AiToken>
    end

    rect rgb(240,240,220)
    Note over Client,Response: Bearer usage in aiChat/completions
    Client->>Controller: POST /aiChat (Authorization: Bearer EKEY_<token>)
    Controller->>Service: aiChat(request, bearerToken)
    Service->>SM4: if startsWith("EKEY_") decryptECB(<encrypted part>)
    SM4-->>Service: decrypted apiKey
    Service->>Service: processRequest(apiKey)
    alt Stream Response
        Controller->>Response: Cache-Control:no-cache, X-Accel-Buffering:no, stream data
    else Standard Response
        Controller->>Response: standard response
    end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

  • Focus areas:
    • SM4Utils: algorithm, transformation, provider registration, key size and Base64 handling
    • AiChatV1ServiceImpl: encryption/decryption flow (EKEY_ prefix), streaming error/interrupt handling, and URL normalization
    • AiChatController: Authorization header parsing/propagation and /encrypt-key input validation/response wrapping
    • GlobalExceptionAdvice: verify change of ServiceException status to 500

Poem

🐰
I nibble keys in midnight code,
SM4 hops down a secret road.
Tokens wrapped with EKEY cheer,
Streams stay nimble, errors clear.
A tiny hop — encryption's here!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 61.90% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the three main changes: Bearer token support, SM4 encryption, streaming response improvements, and service error code fixes—all present in the changeset.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
pom.xml (1)

37-42: Remove duplicate property declaration.

bcprov-jdk18on.version is declared twice (lines 37 and 41). Remove one to avoid confusion.

🔎 Proposed fix
         <bcprov-jdk18on.version>1.79</bcprov-jdk18on.version>
         <jjwt.version>0.12.3</jjwt.version>
         <langchain4j.version>1.5.0</langchain4j.version>
         <langchain4j-rag.version>1.5.0-beta11</langchain4j-rag.version>
-        <bcprov-jdk18on.version>1.79</bcprov-jdk18on.version>
         <tiny-engine.version>1.0-SNAPSHOT</tiny-engine.version>
base/src/main/java/com/tinyengine/it/service/app/impl/v1/AiChatV1ServiceImpl.java (2)

137-140: temperature is set twice unconditionally then conditionally.

Line 137 sets temperature unconditionally (even if null), then line 138-140 sets it again conditionally. Remove the unconditional assignment.

🔎 Proposed fix
-        body.put("temperature", request.getTemperature());
         if (request.getTemperature() != null) {
             body.put("temperature", request.getTemperature());
         }

153-155: Incorrect null check—uses getMaxInputTokens() but puts getVlHighResolutionImages().

This appears to be a copy/paste error. The condition should check getVlHighResolutionImages().

🔎 Proposed fix
-        if (request.getMaxInputTokens() != null) {
+        if (request.getVlHighResolutionImages() != null) {
             body.put("vl_high_resolution_images", request.getVlHighResolutionImages());
         }
🧹 Nitpick comments (6)
base/src/main/java/com/tinyengine/it/model/dto/AiToken.java (1)

23-33: Field should be private; consider using Lombok constructor annotations.

The token field lacks an access modifier (defaulting to package-private). For proper encapsulation, declare it private. Also, the explicit constructors can be replaced with Lombok annotations.

🔎 Proposed refactor
 @Data
+@NoArgsConstructor
+@AllArgsConstructor
 public class AiToken {
-    String token;
-
-    public  AiToken(String token) {
-        this.token = token;
-    }
-    public AiToken(){
-
-    }
+    private String token;
 }

Add the imports:

import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
base/src/main/java/com/tinyengine/it/common/utils/SM4Utils.java (3)

1-11: Missing copyright header.

This file lacks the standard copyright header present in other files in the project.

🔎 Add copyright header
+/**
+ * Copyright (c) 2023 - present TinyEngine Authors.
+ * Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd.
+ *
+ * Use of this source code is governed by an MIT-style license.
+ *
+ * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
+ * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
+ * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
+ *
+ */
+
 package com.tinyengine.it.common.utils;

18-20: ECB mode lacks semantic security; consider CBC or GCM for stronger protection.

The static analysis warnings about DES are false positives (SM4 is China's national block cipher, not DES). However, the ECB mode concern is valid—ECB is deterministic and can leak patterns for repeated plaintext blocks.

For short, unique API keys, ECB may be acceptable, but CBC or GCM modes provide better security guarantees. If you decide to upgrade, use SM4/CBC/PKCS5Padding with a random IV or SM4/GCM/NoPadding.


40-54: Use StandardCharsets.UTF_8 instead of string literal.

Replace "UTF-8" with StandardCharsets.UTF_8 for type safety and to avoid potential UnsupportedEncodingException.

🔎 Proposed fix
     public static String encryptECB(String apiKey, String base64Key) throws Exception {
         byte[] key = Base64.getDecoder().decode(base64Key);
-        byte[] encrypted = encryptECB(apiKey.getBytes("UTF-8"), key);
+        byte[] encrypted = encryptECB(apiKey.getBytes(StandardCharsets.UTF_8), key);
         return Base64.getEncoder().encodeToString(encrypted);
     }

     // ...

     public static String decryptECB(String encryptedBase64, String base64Key) throws Exception {
         byte[] key = Base64.getDecoder().decode(base64Key);
         byte[] encrypted = Base64.getDecoder().decode(encryptedBase64);
         byte[] decrypted = decryptECB(encrypted, key);
-        return new String(decrypted, "UTF-8");
+        return new String(decrypted, StandardCharsets.UTF_8);
     }
base/src/main/java/com/tinyengine/it/controller/AiChatController.java (2)

112-130: Duplicate code with aiChat method.

Lines 114-129 duplicate the Bearer token extraction and streaming response logic from aiChat. Consider extracting common logic into private helper methods to improve maintainability.

🔎 Example refactor
private void extractBearerToken(String authorization, ChatRequest request) {
    if (authorization != null && authorization.startsWith("Bearer ")) {
        request.setApiKey(authorization.substring(7));
    }
}

private ResponseEntity<?> buildChatResponse(Object response, boolean isStream) {
    if (isStream) {
        return ResponseEntity.ok()
            .contentType(MediaType.TEXT_EVENT_STREAM)
            .header("Cache-Control", "no-cache")
            .header("X-Accel-Buffering", "no")
            .body((StreamingResponseBody) response);
    }
    return ResponseEntity.ok(response);
}

149-149: Minor formatting: missing space after if.

🔎 Proposed fix
-        if(apiKey == null || apiKey.isEmpty()) {
+        if (apiKey == null || apiKey.isEmpty()) {
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3603e8f and 21c5fe8.

📒 Files selected for processing (7)
  • base/src/main/java/com/tinyengine/it/common/exception/GlobalExceptionAdvice.java (1 hunks)
  • base/src/main/java/com/tinyengine/it/common/utils/SM4Utils.java (1 hunks)
  • base/src/main/java/com/tinyengine/it/controller/AiChatController.java (4 hunks)
  • base/src/main/java/com/tinyengine/it/model/dto/AiToken.java (1 hunks)
  • base/src/main/java/com/tinyengine/it/service/app/impl/v1/AiChatV1ServiceImpl.java (6 hunks)
  • base/src/main/java/com/tinyengine/it/service/app/v1/AiChatV1Service.java (1 hunks)
  • pom.xml (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
base/src/main/java/com/tinyengine/it/service/app/impl/v1/AiChatV1ServiceImpl.java (3)
base/src/main/java/com/tinyengine/it/common/utils/JsonUtils.java (1)
  • JsonUtils (45-349)
base/src/main/java/com/tinyengine/it/common/utils/SM4Utils.java (1)
  • SM4Utils (12-71)
base/src/main/java/com/tinyengine/it/common/utils/Utils.java (1)
  • Slf4j (59-517)
🪛 ast-grep (0.40.0)
base/src/main/java/com/tinyengine/it/common/utils/SM4Utils.java

[warning] 30-30: Triple DES (3DES or DESede) is considered deprecated. AES is the recommended cipher. Upgrade to use AES.
Context: KeyGenerator.getInstance(ALGORITHM, "BC")
Note: [CWE-326]: Inadequate Encryption Strength [OWASP A03:2017]: Sensitive Data Exposure [OWASP A02:2021]: Cryptographic Failures [REFERENCES]
- https://find-sec-bugs.github.io/bugs.htm#TDES_USAGE
- https://csrc.nist.gov/News/2017/Update-to-Current-Use-and-Deprecation-of-TDEA

(desede-is-deprecated-java)


[warning] 58-58: Triple DES (3DES or DESede) is considered deprecated. AES is the recommended cipher. Upgrade to use AES.
Context: Cipher.getInstance(TRANSFORMATION_ECB, "BC")
Note: [CWE-326]: Inadequate Encryption Strength [OWASP A03:2017]: Sensitive Data Exposure [OWASP A02:2021]: Cryptographic Failures [REFERENCES]
- https://find-sec-bugs.github.io/bugs.htm#TDES_USAGE
- https://csrc.nist.gov/News/2017/Update-to-Current-Use-and-Deprecation-of-TDEA

(desede-is-deprecated-java)


[warning] 65-65: Triple DES (3DES or DESede) is considered deprecated. AES is the recommended cipher. Upgrade to use AES.
Context: Cipher.getInstance(TRANSFORMATION_ECB, "BC")
Note: [CWE-326]: Inadequate Encryption Strength [OWASP A03:2017]: Sensitive Data Exposure [OWASP A02:2021]: Cryptographic Failures [REFERENCES]
- https://find-sec-bugs.github.io/bugs.htm#TDES_USAGE
- https://csrc.nist.gov/News/2017/Update-to-Current-Use-and-Deprecation-of-TDEA

(desede-is-deprecated-java)


[warning] 30-30: Use of AES with ECB mode detected. ECB doesn't provide message confidentiality and is not semantically secure so should not be used. Instead, use a strong, secure cipher: Cipher.getInstance("AES/CBC/PKCS7PADDING"). See https://owasp.org/www-community/Using_the_Java_Cryptographic_Extensions for more information.
Context: KeyGenerator.getInstance(ALGORITHM, "BC")
Note: [CWE-327]: Use of a Broken or Risky Cryptographic Algorithm [OWASP A03:2017]: Sensitive Data Exposure [OWASP A02:2021]: Cryptographic Failures [REFERENCES]
- https://owasp.org/Top10/A02_2021-Cryptographic_Failures
- https://googleprojectzero.blogspot.com/2022/10/rc4-is-still-considered-harmful.html

(use-of-aes-ecb-java)


[warning] 58-58: Use of AES with ECB mode detected. ECB doesn't provide message confidentiality and is not semantically secure so should not be used. Instead, use a strong, secure cipher: Cipher.getInstance("AES/CBC/PKCS7PADDING"). See https://owasp.org/www-community/Using_the_Java_Cryptographic_Extensions for more information.
Context: Cipher.getInstance(TRANSFORMATION_ECB, "BC")
Note: [CWE-327]: Use of a Broken or Risky Cryptographic Algorithm [OWASP A03:2017]: Sensitive Data Exposure [OWASP A02:2021]: Cryptographic Failures [REFERENCES]
- https://owasp.org/Top10/A02_2021-Cryptographic_Failures
- https://googleprojectzero.blogspot.com/2022/10/rc4-is-still-considered-harmful.html

(use-of-aes-ecb-java)


[warning] 65-65: Use of AES with ECB mode detected. ECB doesn't provide message confidentiality and is not semantically secure so should not be used. Instead, use a strong, secure cipher: Cipher.getInstance("AES/CBC/PKCS7PADDING"). See https://owasp.org/www-community/Using_the_Java_Cryptographic_Extensions for more information.
Context: Cipher.getInstance(TRANSFORMATION_ECB, "BC")
Note: [CWE-327]: Use of a Broken or Risky Cryptographic Algorithm [OWASP A03:2017]: Sensitive Data Exposure [OWASP A02:2021]: Cryptographic Failures [REFERENCES]
- https://owasp.org/Top10/A02_2021-Cryptographic_Failures
- https://googleprojectzero.blogspot.com/2022/10/rc4-is-still-considered-harmful.html

(use-of-aes-ecb-java)

🔇 Additional comments (5)
base/src/main/java/com/tinyengine/it/common/exception/GlobalExceptionAdvice.java (1)

72-78: Appropriate status code correction for ServiceException.

Returning 500 for service exceptions is correct. However, the NullPointerException handler (lines 58-64) still returns 200 OK, which is inconsistent—NPEs are typically server errors and should also return 500.

Consider aligning the NullPointerException handler to also return HttpStatus.INTERNAL_SERVER_ERROR for consistency.

base/src/main/java/com/tinyengine/it/service/app/v1/AiChatV1Service.java (1)

31-37: New interface method looks good.

The getToken method signature is consistent with the existing chatCompletion method. Consider using a more specific exception type in the future for better error handling, but this is acceptable for now given the existing pattern.

base/src/main/java/com/tinyengine/it/service/app/impl/v1/AiChatV1ServiceImpl.java (1)

110-114: Unclear semantics for URLs ending with #.

The special handling for URLs ending with # is not documented and the purpose is unclear. Consider adding a comment explaining this behavior or reconsider if this is the intended design.

base/src/main/java/com/tinyengine/it/controller/AiChatController.java (2)

146-153: Consider rate limiting for the encryption endpoint.

The /encrypt-key endpoint generates encrypted tokens from API keys. Without rate limiting, this could be abused for:

  • Enumeration attacks if error responses differ based on key validity
  • Resource exhaustion through repeated encryption operations

Consider adding rate limiting or authentication requirements to this endpoint.

Is this endpoint intended to be publicly accessible without authentication? If users can call this endpoint freely, verify that rate limiting is applied at the gateway/infrastructure level.


83-91: Good streaming response headers.

The Cache-Control: no-cache and X-Accel-Buffering: no headers are appropriately set for Server-Sent Events, ensuring proper streaming behavior with Nginx and preventing unwanted caching.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (4)
base/src/main/java/com/tinyengine/it/controller/AiChatController.java (4)

75-78: Use substring instead of replace for Bearer token extraction.

This issue was already flagged in a previous review. The replace("Bearer ", "") approach replaces all occurrences of "Bearer " in the string, not just the prefix. Use authorization.substring(7) instead.


82-90: Unsafe cast to StreamingResponseBody without type verification.

This issue was already flagged in a previous review. The response from chatCompletion is cast to StreamingResponseBody without runtime type verification, which can cause ClassCastException if the service doesn't honor the contract.


113-116: Use substring instead of replace for Bearer token extraction.

Same issue as in aiChat method. This was already flagged in a previous review for the aiChat method.


120-128: Unsafe cast to StreamingResponseBody without type verification.

Same issue as in aiChat method. This was already flagged in a previous review for the aiChat method.

🧹 Nitpick comments (4)
base/src/main/java/com/tinyengine/it/controller/AiChatController.java (4)

72-73: Avoid declaring generic Exception in throws clause.

The method signature declares throws Exception, which is too broad. Consider throwing specific exceptions (e.g., ServiceException, IllegalArgumentException) or using Spring's exception handling mechanisms to provide clearer error semantics.


111-112: Avoid declaring generic Exception in throws clause.

Same issue as in aiChat: the method declares throws Exception, which is too broad. Use specific exception types or Spring's exception handling.


146-146: Avoid declaring generic Exception in throws clause.

Same issue as the other methods: declaring throws Exception is too broad. Use specific exception types.


147-149: Consider using isBlank() or trimming the input.

The validation checks for null or isEmpty(), but doesn't handle whitespace-only strings. Consider using StringUtils.isBlank(apiKey) (if Apache Commons is available) or apiKey.trim().isEmpty() for more robust validation.

🔎 Proposed enhancement
-        if(apiKey == null || apiKey.isEmpty()) {
+        if(apiKey == null || apiKey.trim().isEmpty()) {
             return Result.failed(ExceptionEnum.CM320);
         }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a3217fa and eae4093.

📒 Files selected for processing (1)
  • base/src/main/java/com/tinyengine/it/controller/AiChatController.java (4 hunks)

@hexqi hexqi changed the title fix: modify AI API feat: Add Bearer Token Support with SM4 Encryption, Improve Streaming Responses and Fix Service Error Codes Dec 19, 2025
@hexqi hexqi merged commit 0afb11f into opentiny:develop Dec 19, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants