-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
feat(translator): grounding metadata + Claude web_search citation passthrough #1532
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: main
Are you sure you want to change the base?
feat(translator): grounding metadata + Claude web_search citation passthrough #1532
Conversation
Add google_search, code_execution, and url_context tool passthrough in request translators and grounding_metadata response passthrough across all provider/format combinations. Streaming paths compact raw JSON to prevent SSE newline framing issues. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary of ChangesHello @cjangrist, 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 significantly enhances the proxy's capability to integrate with Gemini's advanced features by enabling the seamless use of Google Search grounding, code execution, and URL context tools. It ensures that requests containing these tools are correctly translated and forwarded to Gemini, and critically, that the rich grounding metadata returned by Gemini is accurately propagated back to the client across various API formats and streaming/non-streaming scenarios. This broadens the functionality available to users and improves the fidelity of responses by providing crucial context from the model's grounding process. 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
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request introduces support for Google Search grounding by passing through google_search, code_execution, and url_context tools in request translators, and groundingMetadata in response translators. The changes are applied consistently across numerous translators for various API formats (OpenAI, Claude) and providers (Antigravity, Gemini CLI, Gemini API). While the implementation appears correct, there is significant code duplication in handling the passthrough tools and in compacting JSON for streaming responses. I've added a few comments with suggestions to refactor this duplicated logic into shared utility functions to enhance maintainability.
| // Handle google_search passthrough | ||
| if gs := toolResult.Get("google_search"); gs.Exists() { | ||
| node, _ := sjson.SetRaw(`{}`, "googleSearch", gs.Raw) | ||
| extraToolNodes = append(extraToolNodes, node) | ||
| continue | ||
| } | ||
| // Handle code_execution passthrough | ||
| if ce := toolResult.Get("code_execution"); ce.Exists() { | ||
| node, _ := sjson.SetRaw(`{}`, "codeExecution", ce.Raw) | ||
| extraToolNodes = append(extraToolNodes, node) | ||
| continue | ||
| } | ||
| // Handle url_context passthrough | ||
| if uc := toolResult.Get("url_context"); uc.Exists() { | ||
| node, _ := sjson.SetRaw(`{}`, "urlContext", uc.Raw) | ||
| extraToolNodes = append(extraToolNodes, node) | ||
| 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.
The logic for handling passthrough tools (google_search, code_execution, url_context) consists of repeated blocks of if/continue. This pattern is also duplicated across other request translator files (e.g., gemini-cli_claude_request.go, gemini_claude_request.go). This can be refactored to be more concise and data-driven, improving readability and maintainability.
Consider using a map to define the passthrough tools and iterating over it. This would also make it easier to add more passthrough tools in the future.
| // Handle google_search passthrough | |
| if gs := toolResult.Get("google_search"); gs.Exists() { | |
| node, _ := sjson.SetRaw(`{}`, "googleSearch", gs.Raw) | |
| extraToolNodes = append(extraToolNodes, node) | |
| continue | |
| } | |
| // Handle code_execution passthrough | |
| if ce := toolResult.Get("code_execution"); ce.Exists() { | |
| node, _ := sjson.SetRaw(`{}`, "codeExecution", ce.Raw) | |
| extraToolNodes = append(extraToolNodes, node) | |
| continue | |
| } | |
| // Handle url_context passthrough | |
| if uc := toolResult.Get("url_context"); uc.Exists() { | |
| node, _ := sjson.SetRaw(`{}`, "urlContext", uc.Raw) | |
| extraToolNodes = append(extraToolNodes, node) | |
| continue | |
| } | |
| passthroughTools := map[string]string{ | |
| "google_search": "googleSearch", | |
| "code_execution": "codeExecution", | |
| "url_context": "urlContext", | |
| } | |
| isPassthrough := false | |
| for claudeName, geminiName := range passthroughTools { | |
| if tool := toolResult.Get(claudeName); tool.Exists() { | |
| node, _ := sjson.SetRaw(`{}`, geminiName, tool.Raw) | |
| extraToolNodes = append(extraToolNodes, node) | |
| isPassthrough = true | |
| break | |
| } | |
| } | |
| if isPassthrough { | |
| continue | |
| } |
| if toolDeclCount > 0 || len(extraToolNodes) > 0 { | ||
| toolsNode := `[]` | ||
| if toolDeclCount > 0 { | ||
| toolsNode, _ = sjson.SetRaw(toolsNode, "-1", functionToolNode) | ||
| } | ||
| for _, node := range extraToolNodes { | ||
| toolsNode, _ = sjson.SetRaw(toolsNode, "-1", node) | ||
| } | ||
| toolsJSON = toolsNode | ||
| } |
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.
This logic for combining standard function tools with passthrough 'extra' tools is duplicated across multiple request translators (e.g., gemini-cli_claude_request.go, gemini_claude_request.go). To improve maintainability and reduce code duplication, this could be extracted into a shared helper function. Such a function could take the functionToolNode and extraToolNodes and return the final toolsJSON string, which can then be set on the output request.
|
|
||
| // Cache groundingMetadata for inclusion in the message_delta event. | ||
| if gm := gjson.GetBytes(rawJSON, "response.candidates.0.groundingMetadata"); gm.Exists() { | ||
| params.GroundingMetadataRaw = strings.ReplaceAll(strings.ReplaceAll(gm.Raw, "\n", ""), "\r", "") |
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.
This JSON compacting logic using chained strings.ReplaceAll is repeated in at least 7 different files for streaming responses. To improve maintainability and reduce code duplication, consider extracting this into a shared utility function, for example in the internal/util package.
A new function like util.CompactJSON(string) string could encapsulate this logic, making the translators cleaner and ensuring consistency across the codebase.
…formats Preserve web_search_20250305 built-in tools in request translation and surface web_search_tool_result/citations_delta in Chat Completions and Responses API streaming + non-streaming output. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…format Preserve built-in tools in Gemini request translation and convert web_search_tool_result/citations_delta to groundingMetadata in streaming and non-streaming Gemini output. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
11 tests covering request tool preservation, streaming/non-streaming response translation for OpenAI, Responses, and Gemini formats, plus accumulator reset verification. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
Test plan
Generated with Claude Code