-
Notifications
You must be signed in to change notification settings - Fork 67
Lnx082/my branch #275
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Lnx082/my branch #275
Conversation
WalkthroughGoogle Gemini API integration is added via an adapter-based pattern. New GeminiApiAdapter class transforms OpenAI-style requests to Gemini format and vice versa. Three Gemini model variants are registered. AiChatV1ServiceImpl detects and routes Gemini models through the adapter. Configuration and multimodal content support (Object-typed message content) enable end-to-end functionality. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant AiChatV1ServiceImpl as AiChat Service
participant GeminiAdapter as Gemini Adapter
participant GeminiAPI as Gemini API
Client->>AiChatV1ServiceImpl: chatCompletion(ChatRequest with Gemini model)
activate AiChatV1ServiceImpl
AiChatV1ServiceImpl->>GeminiAdapter: isGeminiModel(modelName)
activate GeminiAdapter
GeminiAdapter-->>AiChatV1ServiceImpl: true
deactivate GeminiAdapter
AiChatV1ServiceImpl->>AiChatV1ServiceImpl: normalizeApiUrl(Gemini format)
AiChatV1ServiceImpl->>GeminiAdapter: convertRequestToGemini(requestBody)
activate GeminiAdapter
GeminiAdapter->>GeminiAdapter: convertMessagesToContents(roles, multimodal)
GeminiAdapter-->>AiChatV1ServiceImpl: Gemini formatted request
deactivate GeminiAdapter
AiChatV1ServiceImpl->>GeminiAPI: POST /v1beta/models/{model}:generateContent
activate GeminiAPI
GeminiAPI-->>AiChatV1ServiceImpl: Gemini API response
deactivate GeminiAPI
AiChatV1ServiceImpl->>GeminiAdapter: convertResponseFromGemini(response, model)
activate GeminiAdapter
GeminiAdapter->>GeminiAdapter: build choices, map finish_reason, parse parts
GeminiAdapter-->>AiChatV1ServiceImpl: OpenAI formatted response
deactivate GeminiAdapter
AiChatV1ServiceImpl-->>Client: OpenAI-compatible ChatResponse
deactivate AiChatV1ServiceImpl
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45–60 minutes Areas requiring extra attention:
Poem
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
pom.xml (1)
30-30: Remove unused MariaDB version property and clean up commented code.The
mariadb-java-client.versionproperty (line 30) is now unused after the database driver migration. Additionally, the commented-out MariaDB dependency block (lines 121-125) should be removed rather than left as stale comments to improve code maintainability.Apply this diff to clean up:
<properties> <java.version>17</java.version> <fastjson.version>2.0.50</fastjson.version> <hutool.version>5.8.27</hutool.version> <org.mapstruct.version>1.5.5.Final</org.mapstruct.version> <org.projectlombok.version>1.18.32</org.projectlombok.version> <druid-spring-boot-starter.version>1.2.21</druid-spring-boot-starter.version> <mybatis-plus.version>3.5.7</mybatis-plus.version> <jackson.version>2.17.2</jackson.version> - <mariadb-java-client.version>3.3.3</mariadb-java-client.version> <freemarker.version>2.3.32</freemarker.version> ... </properties> ... - <!-- <dependency>--> - <!-- <groupId>org.mariadb.jdbc</groupId>--> - <!-- <artifactId>mariadb-java-client</artifactId>--> - <!-- <version>${mariadb-java-client.version}</version>--> - <!-- </dependency>--> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency>Also applies to: 121-125
base/src/main/java/com/tinyengine/it/service/app/impl/v1/AiChatV1ServiceImpl.java (3)
180-183: Duplicate temperature assignment.Temperature is set unconditionally on line 180, then conditionally again on lines 181-183. The unconditional assignment should be removed.
- body.put("temperature", request.getTemperature()); if (request.getTemperature() != null) { body.put("temperature", request.getTemperature()); }
196-198: Incorrect condition check - copy-paste error.The condition checks
getMaxInputTokens()but the body setsvl_high_resolution_images. This should checkgetVlHighResolutionImages().- if (request.getMaxInputTokens() != null) { + if (request.getVlHighResolutionImages() != null) { body.put("vl_high_resolution_images", request.getVlHighResolutionImages()); }
291-322: Gemini streaming responses are not converted to OpenAI format.The
processStreamResponsemethod acceptsisGeminiandmodelparameters but never uses them. Gemini streaming responses will be returned in Gemini's native format instead of being converted to OpenAI-compatible format, breaking API consistency.This could cause client-side parsing issues when streaming from Gemini models.
Consider implementing streaming response conversion for Gemini, or at minimum, document this limitation. The non-streaming path correctly converts responses (lines 282-286).
🧹 Nitpick comments (16)
QUICKSTART_GEMINI.md (1)
14-22: Avoid hard‑coding a personal Windows path in quickstart commands
cd E:\tiny-engine-backend-javamakes the guide look environment‑specific and may confuse users on other OSes or with different checkout locations. Consider using a placeholder like<project-root>instead.-# 编译项目 -cd E:\tiny-engine-backend-java +# 编译项目(将 <project-root> 替换为你的项目根目录) +cd <project-root>documents/gemini-test-examples.http (1)
1-148: Examples look consistent with the unified chat schemaThe request bodies (model/apiKey/messages/temperature/stream/baseUrl) all align with the adapter’s OpenAI-style contract and cover the important Gemini cases (simple/streaming/multi-turn/custom baseUrl). You may optionally add a brief note explaining when to prefer
/app-center/api/chat/completionsvs/app-center/api/ai/chatto avoid confusion.base/src/main/java/com/tinyengine/it/service/app/impl/AiChatServiceImpl.java (1)
265-291: Multimodal-safe content handling looks correct; consider null behaviorSwitching to
Object contentObjand normalizing viaString.valueOfplusgetContentAsString()avoids ClassCastException afterAiMessages.contentbecameObject, and keeps the prompt-prefix logic intact. One small edge case: ifmessages.get(0).getContent()isnull, this will append the literal"null"string into the prompt. If that’s undesirable, you could special‑casenullto an empty string before concatenation.-Object contentObj = messages.get(0).getContent(); -// 确保content是字符串类型 -String content = contentObj instanceof String ? (String) contentObj : String.valueOf(contentObj); +Object contentObj = messages.get(0).getContent(); +// 确保content是字符串类型,并避免 null → "null" +String content; +if (contentObj == null) { + content = ""; +} else if (contentObj instanceof String) { + content = (String) contentObj; +} else { + content = String.valueOf(contentObj); +}base/src/main/java/com/tinyengine/it/model/dto/AiMessages.java (1)
25-65: Multimodalcontentdesign is reasonable; consider tightening Lombok usageSwitching
contenttoObjectplusgetContentAsString()and the overloadedsetContent(String/Object)methods is a clean way to support multimodal payloads while keeping existing text-only callers working.To reduce future confusion around setter generation, you might explicitly disable Lombok’s setter just for
contentand rely on the two manual setters:@Getter @Setter public class AiMessages { @Setter(lombok.AccessLevel.NONE) private Object content; private String role; private String name; // keep getContentAsString + both setContent overloads }This makes it obvious to readers that
contenthas custom semantics whilerole/nameremain standard Lombok properties.修复总结_Model_Not_Exist错误.md (1)
29-31: Add a language to this fenced code block to satisfy markdownlintThe URL example block uses plain ``` without a language, which triggers MD040 (
fenced-code-language). Declaring a language (e.g., `text`) will silence the linter and improve rendering.-``` +```text https://generativelanguage.googleapis.com/v1beta/models/models/gemini-1.5-pro:generateContentAs per static analysis hints. </blockquote></details> <details> <summary>修复总结_JSON反序列化错误.md (1)</summary><blockquote> `9-12`: **Minor markdown and example-alignment tweaks** Two small suggestions for this summary doc: 1. The error snippet fenced block (`Failed to deserialize...`) has no language; consider adding `text` to be consistent with other code fences and avoid markdownlint warnings. 2. The test payloads use models `gpt-4` / `gpt-4-vision`, while the current `FoundationModel` enum in this project focuses on `gpt-3.5-turbo`, local models, ERNIE, Moonshot, DeepSeek, and Gemini. If the runtime doesn’t actually support `gpt-4*` here, you may want to switch these examples to a model that is supported in this codebase to avoid misleading readers. Also applies to: 104-129 </blockquote></details> <details> <summary>问题分析_JSON反序列化错误.md (1)</summary><blockquote> `1-5`: **Internal debugging documentation should not be committed.** This file appears to be internal problem analysis/debugging notes rather than production documentation. Consider: 1. Moving this to a wiki or internal documentation system 2. Removing it from the codebase before merging If kept, add a language specifier to the fenced code block at line 4 per markdown lint rules. ```diff ## 错误信息 -``` +```text Failed to deserialize the JSON body into the target type:documents/gemini-integration.md (1)
159-159: Convert bare URL to markdown link format.Per markdown lint rules, bare URLs should use proper markdown link syntax.
-| baseUrl | string | 否 | API 基础 URL,默认为 https://generativelanguage.googleapis.com | +| baseUrl | string | 否 | API 基础 URL,默认为 `https://generativelanguage.googleapis.com` |base/src/test/java/com/tinyengine/it/service/app/adapter/GeminiApiAdapterTest.java (2)
216-232: Verify empty candidates behavior and add assertion for message content.The test verifies that an empty candidates array still produces 1 choice, but doesn't verify the state of that choice. Looking at the adapter implementation, when candidates is empty, the choice will lack a
messagefield, which could cause issues downstream.Consider adding assertions to verify the choice structure:
@SuppressWarnings("unchecked") List<Map<String, Object>> choices = (List<Map<String, Object>>) openAiResponse.get("choices"); assertEquals(1, choices.size()); + + // Verify the choice structure when no candidates exist + Map<String, Object> choice = choices.get(0); + assertEquals(0, choice.get("index")); + // Note: message may be null/missing when candidates is empty + assertNull(choice.get("message"), "Message should be null when candidates is empty"); }
28-43: Consider adding test for multimodal content conversion.The adapter supports converting
image_urlcontent types to Gemini'sinlineDataformat, but there's no test coverage for this path. This is important for validating the base64 image handling logic.Would you like me to generate a test case for multimodal content (text + image_url) conversion?
base/src/main/java/com/tinyengine/it/service/app/impl/v1/AiChatV1ServiceImpl.java (1)
224-227: Debug log may expose sensitive message content.The debug log outputs the full request body which may contain user messages with sensitive information. Ensure debug logging is disabled in production or consider sanitizing the output.
If this is intentional for debugging, consider:
- Truncating the body or logging only metadata
- Using a dedicated debug flag that's disabled by default
base/src/main/java/com/tinyengine/it/config/AiChatConfig.java (1)
53-60: Header may contain null API key value.When the model doesn't match any Gemini model,
geminiApiKeyremainsnull, and line 60 setsx-goog-api-keytonull. While this header map is only used for Gemini configs, having an explicit null value could be confusing.Map<String, String> geminiHeaders = new HashMap<>(); String geminiApiKey = null; if (Enums.FoundationModel.GEMINI_PRO.getValue().equals(model) || Enums.FoundationModel.GEMINI_1_5_PRO.getValue().equals(model) || Enums.FoundationModel.GEMINI_1_5_FLASH.getValue().equals(model)) { geminiApiKey = token; } - geminiHeaders.put("x-goog-api-key", geminiApiKey); + if (geminiApiKey != null) { + geminiHeaders.put("x-goog-api-key", geminiApiKey); + }base/src/main/java/com/tinyengine/it/service/app/adapter/GeminiApiAdapter.java (1)
37-76: Request/response mapping is coherent; consider future multi-candidate supportThe conversion of OpenAI-style parameters (
temperature,max_tokens,top_p,stop) into Gemini’sgenerationConfigand the mapping back fromusageMetadatainto OpenAI-styleusagelook consistent and safe for the current single-candidate use case.If you plan to expose
n > 1completions later, you’ll need to extendconvertResponseFromGeminito iterate allcandidatesinstead of hard-coding a singlechoiceat index 0. For the current behavior (single completion) this is fine as-is.Also applies to: 161-234
README_GEMINI.md (1)
61-67: Add languages to fenced code blocks to satisfy markdownlint (MD040)The test output block (Line 61) and the ASCII architecture diagram block (Line 106) use bare triple backticks, which markdownlint flags (MD040).
You can fix this by specifying a language, e.g.:
-``` +```text ------------------------------------------------------- T E S T S ... -``` +``` ```diff -``` +```text ┌─────────────────┐ │ 客户端请求 │ (OpenAI 格式) ... -``` +```This keeps rendering identical while making linters happy.
Also applies to: 106-135
Gemini集成完成总结.md (2)
70-82: Specify a language for the architecture code block (MD040)The architecture flow diagram block uses bare triple backticks, which triggers markdownlint MD040.
Consider marking it as plain text:
-``` +```text 客户端请求(OpenAI 格式) ↓ ... -``` +```This preserves the visual layout and satisfies the linter.
96-100: Align “工具调用消息处理” description with actual implementationThis section lists “工具调用消息处理” as part of the 内容格式转换功能. From the adapter code we see explicit handling for text and
image_urlcontent types; tool‑call specific shapes are not obviously mapped here.If tool‑call handling is implemented elsewhere (e.g., pre‑/post‑processing around
GeminiApiAdapter), consider clarifying that in the doc; otherwise, either add the missing conversion logic or adjust the wording so it doesn’t over‑claim current capabilities.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (18)
Gemini集成完成总结.md(1 hunks)QUICKSTART_GEMINI.md(1 hunks)README_GEMINI.md(1 hunks)app/src/main/resources/application-dev.yml(2 hunks)base/src/main/java/com/tinyengine/it/common/enums/Enums.java(1 hunks)base/src/main/java/com/tinyengine/it/config/AiChatConfig.java(3 hunks)base/src/main/java/com/tinyengine/it/model/dto/AiMessages.java(1 hunks)base/src/main/java/com/tinyengine/it/service/app/adapter/GeminiApiAdapter.java(1 hunks)base/src/main/java/com/tinyengine/it/service/app/impl/AiChatServiceImpl.java(1 hunks)base/src/main/java/com/tinyengine/it/service/app/impl/v1/AiChatV1ServiceImpl.java(7 hunks)base/src/test/java/com/tinyengine/it/service/app/adapter/GeminiApiAdapterTest.java(1 hunks)documents/gemini-examples.http(1 hunks)documents/gemini-integration.md(1 hunks)documents/gemini-test-examples.http(1 hunks)pom.xml(1 hunks)修复总结_JSON反序列化错误.md(1 hunks)修复总结_Model_Not_Exist错误.md(1 hunks)问题分析_JSON反序列化错误.md(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
base/src/main/java/com/tinyengine/it/config/AiChatConfig.java (1)
base/src/main/java/com/tinyengine/it/common/enums/Enums.java (1)
Enums(20-1183)
base/src/test/java/com/tinyengine/it/service/app/adapter/GeminiApiAdapterTest.java (1)
base/src/main/java/com/tinyengine/it/service/app/adapter/GeminiApiAdapter.java (1)
GeminiApiAdapter(28-245)
base/src/main/java/com/tinyengine/it/service/app/impl/v1/AiChatV1ServiceImpl.java (2)
base/src/main/java/com/tinyengine/it/service/app/adapter/GeminiApiAdapter.java (1)
GeminiApiAdapter(28-245)base/src/main/java/com/tinyengine/it/common/utils/JsonUtils.java (1)
JsonUtils(45-349)
🪛 markdownlint-cli2 (0.18.1)
问题分析_JSON反序列化错误.md
4-4: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
README_GEMINI.md
61-61: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
106-106: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
修复总结_Model_Not_Exist错误.md
29-29: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
documents/gemini-integration.md
159-159: Bare URL used
(MD034, no-bare-urls)
Gemini集成完成总结.md
70-70: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🔇 Additional comments (10)
pom.xml (2)
121-130: Align this change with database configuration and verify codebase consistency.The switch from MariaDB to MySQL Connector/J is a significant dependency migration. Ensure that:
- Application configuration files (e.g.,
application.yml,application-dev.yml) have updated the JDBC driver class and connection URL to MySQL format (e.g.,com.mysql.cj.jdbc.Driverinstead of MariaDB driver)- All environment configurations across dev, test, and production are updated consistently
- Any MariaDB-specific JDBC URL parameters are replaced with MySQL equivalents
The AI summary mentions changes in
application-dev.ymlswitching to MySQL, but verify all environments are aligned.
126-130: MySQL Connector/J uses inherited versioning from Spring Boot parent POM.The
mysql-connector-jdependency (line 128) does not specify an explicit version and inherits it from the Spring Boot parent POM. This is standard practice with Spring Boot and ensures dependency consistency. Verify the inherited version meets your MySQL database requirements; Spring Boot's dependency management is designed to provide stable, tested versions.Additionally, remove the unused
mariadb-java-client.versionproperty from the pom.xml properties section.app/src/main/resources/application-dev.yml (1)
1-92: Mismatch between PR objectives and actual file changes.The PR summary states that this change adds "Google Gemini API integration via an adapter-based pattern" with a new
GeminiApiAdapterclass and Gemini model registration. However, the only file provided for review is a database configuration file (application-dev.yml) with no Gemini-related configuration or code changes visible.Please verify:
- Is this the complete set of changed files, or were Gemini API adapter classes omitted from this review batch?
- Should the PR include additional Java source files, configuration for API keys, or other Gemini integration code?
base/src/main/java/com/tinyengine/it/common/enums/Enums.java (1)
780-815: Gemini model enum additions look consistentThe new
GEMINI_PRO,GEMINI_1_5_PRO, andGEMINI_1_5_FLASHconstants and their values match the documented model strings used in the examples and adapter logic. No issues here.documents/gemini-examples.http (1)
1-344: Comprehensive Gemini examples align well with the adapter contractThe HTTP requests and multi-language client snippets all match the unified
model/apiKey/baseUrl/messages/temperature/stream/maxTokensschema and cover key scenarios (basic, streaming, multi-turn, codegen, system prompts, image input). This file should be very helpful for consumers; no changes needed from my side.documents/gemini-integration.md (1)
1-252: LGTM - Well-structured integration documentation.The documentation provides comprehensive coverage of Gemini API integration including configuration, usage examples, error handling, and extension guidance.
base/src/test/java/com/tinyengine/it/service/app/adapter/GeminiApiAdapterTest.java (1)
32-42: LGTM - Good model detection test coverage.The test properly covers positive cases (gemini-pro, gemini-1.5-pro, gemini-1.5-flash, models/gemini-pro) and negative cases (gpt-3.5-turbo, deepseek-chat, null).
base/src/main/java/com/tinyengine/it/service/app/impl/v1/AiChatV1ServiceImpl.java (2)
146-158: LGTM - Model name normalization.The
normalizeGeminiModelNamemethod correctly handles themodels/prefix removal and provides a sensible default.
77-81: LGTM - Correct authentication handling for Gemini.Properly skips Bearer token header for Gemini (which uses API key in URL query parameter) while maintaining it for other providers.
base/src/main/java/com/tinyengine/it/config/AiChatConfig.java (1)
80-94: LGTM - Gemini model configurations.The three Gemini model configurations follow the established pattern and correctly use the Gemini API URL structure with the
x-goog-api-keyheader approach.Note: These configs use
:generateContentendpoint (non-streaming). If streaming requests use this config, they would need the:streamGenerateContentendpoint instead. Verify that the application either uses separate streaming configuration or handles endpoint switching at the call site.
| password: Nn2005,050618 | ||
| url: jdbc:mysql://localhost:3306/tiny_engine_db?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
find . -name ".gitignore" -type f -exec grep -l "application\|yml" {} \;Repository: opentiny/tiny-engine-backend-java
Length of output: 210
🏁 Script executed:
# Check repository structure for config files
find . -path "*resources*" -name "application*.yml" -o -name "application*.yaml" | head -20Repository: opentiny/tiny-engine-backend-java
Length of output: 210
🏁 Script executed:
# Check if there's a git history showing if this file is tracked
git ls-files app/src/main/resources/ | grep applicationRepository: opentiny/tiny-engine-backend-java
Length of output: 210
Database credentials hardcoded in development configuration file.
Lines 17-18 contain a database password and JDBC URL. Even for development configurations tracked in version control, hardcoding credentials is not recommended. If this is a shared repository, these details become accessible to all developers.
Recommended approach:
- Use environment variables for sensitive values:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
- username: root
- password: Nn2005,050618
- url: jdbc:mysql://localhost:3306/tiny_engine_db?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
+ username: ${DB_USERNAME:root}
+ password: ${DB_PASSWORD}
+ url: ${DB_URL:jdbc:mysql://localhost:3306/tiny_engine_db?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true}-
Add
application-dev.ymlto.gitignoreif it's environment-specific, or exclude sensitive properties and use a committedapplication-dev.yml.exampletemplate instead. -
Set environment variables locally or through a
.envfile (excluded from git).
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In app/src/main/resources/application-dev.yml around lines 17-18 the database
password and JDBC URL are hardcoded; replace these sensitive values with
environment variable references (e.g., read password and URL from env vars or
Spring placeholders like ${DB_PASSWORD} and ${JDBC_URL}) and update the file to
omit secrets, then add application-dev.yml to .gitignore if it contains
environment-specific secrets or alternatively commit an
application-dev.yml.example template without credentials and document using a
.env or environment variables for local setup.
| Map<?, ?> imageUrl = (Map<?, ?>) contentItem.get("image_url"); | ||
| if (imageUrl != null) { | ||
| Map<String, Object> part = new HashMap<>(); | ||
| Map<String, Object> inlineData = new HashMap<>(); | ||
| String url = (String) imageUrl.get("url"); | ||
|
|
||
| // Handle base64 images | ||
| if (url.startsWith("data:")) { | ||
| String[] parts_url = url.split(","); | ||
| if (parts_url.length == 2) { | ||
| String mimeType = parts_url[0].split(";")[0].substring(5); | ||
| inlineData.put("mimeType", mimeType); | ||
| inlineData.put("data", parts_url[1]); | ||
| } | ||
| } | ||
|
|
||
| part.put("inlineData", inlineData); | ||
| parts.add(part); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Guard against null/invalid image URLs and avoid emitting empty inlineData
In the image_url branch:
urlcan benull, sourl.startsWith("data:")can throw an NPE at runtime.- For non‑
data:URLs, the current code still adds a part with an emptyinlineDatamap, which is almost certainly not what you want and can produce invalid requests downstream.
You can both prevent the NPE and avoid emitting empty inlineData by guarding the cast and only creating the part when a valid data URI was parsed, e.g.:
- if (imageUrl != null) {
- Map<String, Object> part = new HashMap<>();
- Map<String, Object> inlineData = new HashMap<>();
- String url = (String) imageUrl.get("url");
-
- // Handle base64 images
- if (url.startsWith("data:")) {
- String[] parts_url = url.split(",");
- if (parts_url.length == 2) {
- String mimeType = parts_url[0].split(";")[0].substring(5);
- inlineData.put("mimeType", mimeType);
- inlineData.put("data", parts_url[1]);
- }
- }
-
- part.put("inlineData", inlineData);
- parts.add(part);
- }
+ if (imageUrl != null) {
+ Object urlObj = imageUrl.get("url");
+ if (urlObj instanceof String) {
+ String url = (String) urlObj;
+ // Handle base64 data: URLs only for now
+ if (url.startsWith("data:")) {
+ Map<String, Object> part = new HashMap<>();
+ Map<String, Object> inlineData = new HashMap<>();
+ String[] parts_url = url.split(",");
+ if (parts_url.length == 2) {
+ String mimeType = parts_url[0].split(";")[0].substring(5);
+ inlineData.put("mimeType", mimeType);
+ inlineData.put("data", parts_url[1]);
+ }
+ if (!inlineData.isEmpty()) {
+ part.put("inlineData", inlineData);
+ parts.add(part);
+ }
+ }
+ }
+ }This keeps current base64 support, avoids NPEs, and doesn’t send structurally empty image parts. You can extend the branch later if you decide to support non‑data: URLs.
🤖 Prompt for AI Agents
In
base/src/main/java/com/tinyengine/it/service/app/adapter/GeminiApiAdapter.java
around lines 123 to 141, the code assumes image_url->url is a non-null String
and always emits a part with an (possibly empty) inlineData map; to fix, first
verify imageUrl.get("url") is a non-null instance of String before calling
startsWith, then only parse and create the part when the URL is a valid data:
URI that yields both mimeType and data (e.g., check parts_url.length == 2 and
that mimeType/data are non-empty); do not add a part or inlineData when the
checks fail (thus avoiding NPEs and empty inlineData entries), and optionally
log or ignore non-data URLs for future handling.
| for (Object msg : messageList) { | ||
| if (!(msg instanceof Map)) { | ||
| normalizedMessages.add((Map<String, Object>) msg); | ||
| continue; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unsafe cast when message is not a Map.
If msg is not a Map, the code attempts to cast it to Map<String, Object> anyway, which will throw a ClassCastException.
for (Object msg : messageList) {
if (!(msg instanceof Map)) {
- normalizedMessages.add((Map<String, Object>) msg);
continue;
}Alternatively, if non-Map messages should be preserved, handle them appropriately without an unsafe cast.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| for (Object msg : messageList) { | |
| if (!(msg instanceof Map)) { | |
| normalizedMessages.add((Map<String, Object>) msg); | |
| continue; | |
| } | |
| for (Object msg : messageList) { | |
| if (!(msg instanceof Map)) { | |
| continue; | |
| } |
🤖 Prompt for AI Agents
In
base/src/main/java/com/tinyengine/it/service/app/impl/v1/AiChatV1ServiceImpl.java
around lines 243-247, the loop currently casts every msg to Map<String,Object>
even when it's not a Map which causes ClassCastException; change the logic so
that if msg is an instance of Map you cast and add it, otherwise create a safe
Map<String,Object> wrapper (e.g. put the original msg under a "content" key and
any default metadata you need) and add that wrapper to normalizedMessages so no
unsafe casts occur and non-Map messages are preserved.
| 修改 `OpenAIConfig.java` 的默认配置: | ||
|
|
||
| ```java | ||
| @Data | ||
| @Configuration | ||
| public class OpenAIConfig { | ||
| private String apiKey = "YOUR_GEMINI_API_KEY"; | ||
| private String baseUrl = "https://generativelanguage.googleapis.com"; | ||
| private String defaultModel = "gemini-1.5-pro"; | ||
| private int timeoutSeconds = 300; | ||
| } | ||
| ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent configuration class reference.
The documentation references OpenAIConfig.java but based on the PR changes, Gemini configuration is managed in AiChatConfig.java. This could confuse developers.
#### 方式二:修改配置文件
-修改 `OpenAIConfig.java` 的默认配置:
+修改 `AiChatConfig.java` 或 `OpenAIConfig.java` 的默认配置:📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| 修改 `OpenAIConfig.java` 的默认配置: | |
| ```java | |
| @Data | |
| @Configuration | |
| public class OpenAIConfig { | |
| private String apiKey = "YOUR_GEMINI_API_KEY"; | |
| private String baseUrl = "https://generativelanguage.googleapis.com"; | |
| private String defaultModel = "gemini-1.5-pro"; | |
| private int timeoutSeconds = 300; | |
| } | |
| ``` | |
| 修改 `AiChatConfig.java` 或 `OpenAIConfig.java` 的默认配置: | |
🤖 Prompt for AI Agents
In documents/gemini-integration.md around lines 52 to 63, the doc wrongly
references OpenAIConfig.java; update the documentation to reference
AiChatConfig.java (the actual config class used by the PR), and adjust any
example property names or descriptions to match AiChatConfig.java's fields and
defaults so the sample configuration and filenames are consistent with the
codebase.
English | 简体中文
PR
PR Checklist
Please check if your PR fulfills the following requirements:
PR Type
What kind of change does this PR introduce?
Background and solution
What is the current behavior?
Issue Number: N/A
What is the new behavior?
Does this PR introduce a breaking change?
Other information
Summary by CodeRabbit
Release Notes
New Features
Bug Fixes
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.