fix(gemini-cli): sanitize tool schemas and filter empty parts#2108
fix(gemini-cli): sanitize tool schemas and filter empty parts#2108sususu98 wants to merge 1 commit intorouter-for-me:mainfrom
Conversation
1. Claude translator: add CleanJSONSchemaForGemini() to sanitize tool input schemas (removes $schema, anyOf, const, format, etc.) and delete eager_input_streaming from tool declarations. Remove fragile bytes.Replace for format:"uri" now covered by schema cleaner. 2. Gemini native translator: filter out content entries with empty or missing parts arrays to prevent Gemini API 400 error "required oneof field 'data' must have one initialized field". Both fixes align gemini-cli with protections already present in the antigravity translator.
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request enhances the Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces two important fixes to the gemini-cli translator to prevent INVALID_ARGUMENT errors from the Gemini API. It sanitizes tool schemas by removing unsupported fields and filters out content with empty parts arrays. The changes are logical and address the described issues. I've provided a couple of suggestions to improve maintainability and performance in the implementation.
| tool, _ = sjson.Delete(tool, "strict") | ||
| tool, _ = sjson.Delete(tool, "input_examples") | ||
| tool, _ = sjson.Delete(tool, "type") | ||
| tool, _ = sjson.Delete(tool, "cache_control") | ||
| tool, _ = sjson.Delete(tool, "defer_loading") | ||
| tool, _ = sjson.Delete(tool, "eager_input_streaming") |
There was a problem hiding this comment.
For better maintainability, you can refactor these repeated sjson.Delete calls into a loop over a slice of fields to be deleted. This makes it easier to add or remove fields from the blacklist in the future.
fieldsToDelete := []string{
"strict",
"input_examples",
"type",
"cache_control",
"defer_loading",
"eager_input_streaming",
}
for _, field := range fieldsToDelete {
tool, _ = sjson.Delete(tool, field)
}| filteredContents := "[]" | ||
| hasFiltered := false | ||
| gjson.GetBytes(rawJSON, "request.contents").ForEach(func(_, content gjson.Result) bool { | ||
| parts := content.Get("parts") | ||
| if !parts.IsArray() || len(parts.Array()) == 0 { | ||
| hasFiltered = true | ||
| return true | ||
| } | ||
| filteredContents, _ = sjson.SetRaw(filteredContents, "-1", content.Raw) | ||
| return true | ||
| }) | ||
| if hasFiltered { | ||
| rawJSON, _ = sjson.SetRawBytes(rawJSON, "request.contents", []byte(filteredContents)) | ||
| } |
There was a problem hiding this comment.
The current implementation of filtering contents with empty parts can be inefficient. Calling sjson.SetRaw inside the ForEach loop causes the filteredContents JSON string to be parsed on every iteration. A more performant approach would be to collect the raw content strings into a slice and then build the final JSON array string once at the end using strings.Join.
| filteredContents := "[]" | |
| hasFiltered := false | |
| gjson.GetBytes(rawJSON, "request.contents").ForEach(func(_, content gjson.Result) bool { | |
| parts := content.Get("parts") | |
| if !parts.IsArray() || len(parts.Array()) == 0 { | |
| hasFiltered = true | |
| return true | |
| } | |
| filteredContents, _ = sjson.SetRaw(filteredContents, "-1", content.Raw) | |
| return true | |
| }) | |
| if hasFiltered { | |
| rawJSON, _ = sjson.SetRawBytes(rawJSON, "request.contents", []byte(filteredContents)) | |
| } | |
| var filteredContentsRaws []string | |
| hasFiltered := false | |
| gjson.GetBytes(rawJSON, "request.contents").ForEach(func(_, content gjson.Result) bool { | |
| parts := content.Get("parts") | |
| if !parts.IsArray() || len(parts.Array()) == 0 { | |
| hasFiltered = true | |
| return true | |
| } | |
| filteredContentsRaws = append(filteredContentsRaws, content.Raw) | |
| return true | |
| }) | |
| if hasFiltered { | |
| filteredContentsJSON := "[" + strings.Join(filteredContentsRaws, ",") + "]" | |
| rawJSON, _ = sjson.SetRawBytes(rawJSON, "request.contents", []byte(filteredContentsJSON)) | |
| } |
Summary
The gemini-cli translator is missing several protections that the antigravity translator already has, causing 400 INVALID_ARGUMENT errors from the Gemini API in two scenarios:
Claude Code tool declarations contain unsupported fields — Fields like
eager_input_streamingand JSON Schema keywords ($schema,anyOf,const,format,additionalProperties, etc.) are passed through to the Gemini API, which rejects them.Gemini-native clients send empty parts arrays — Clients like CherryStudio include
{"role":"model","parts":[]}in conversation history, which Gemini API rejects with"required oneof field 'data' must have one initialized field".Changes
Claude translator (
gemini-cli_claude_request.go)util.CleanJSONSchemaForGemini()call to sanitize tool input schemas before forwarding (removes$schema,anyOf,const,format,additionalProperties,propertyNames,x-*, etc.)eager_input_streamingto the blacklist of deleted tool-level fieldsbytes.Replacefor"format":"uri"— now fully covered byCleanJSONSchemaForGemini()Gemini native translator (
gemini-cli_gemini_request.go)partsarrays before sending upstreamBoth fixes align the gemini-cli translator with protections already present in the antigravity translator (see PR #1294 for the antigravity empty-parts fix).
Actual error logs
Error 1 —
eager_input_streaming:(repeated for all 13 tool declarations)
Error 2 — empty parts:
Test plan
go build ./...— compiles cleanlygo test ./...— all existing tests pass