-
Notifications
You must be signed in to change notification settings - Fork 186
fix: claude code fixes #1319
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
fix: claude code fixes #1319
Conversation
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (16)
📝 WalkthroughWalkthroughAdds thought/signature extraction/propagation for Gemini and Anthropic, WebSearch argument sanitization and synthetic delta handling, changes JSON schema AdditionalProperties to a union struct (bool or ordered map), and extends Anthropic citation types and content fields with custom JSON marshal/unmarshal. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Provider
participant ResponsesConverter
participant FunctionExecutor
participant StreamEmitter
Client->>Provider: send content / tool call (CallID may include "_ts_"+base64)
Provider->>ResponsesConverter: deliver content, CallID, tool args (may include WebSearch args)
ResponsesConverter->>ResponsesConverter: parse CallID, extract/decode thoughtSignature -> Part.ThoughtSignature
ResponsesConverter->>FunctionExecutor: emit function call (base CallID, signature in metadata / embedded)
ResponsesConverter->>ResponsesConverter: sanitize WebSearch args, record webSearchItemIDs, synthesize input_json_delta if applicable
FunctionExecutor-->>ResponsesConverter: return result (tool output, StopReason/indices)
ResponsesConverter->>StreamEmitter: emit deltas/stops with Signature and indices (OutputIndex preferred, ContentIndex fallback)
StreamEmitter->>Client: deliver enriched stream events
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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 |
This stack of pull requests is managed by Graphite. Learn more about stacking. |
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: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
core/providers/gemini/responses.go (1)
1048-1094: State tracking uses different ID than emitted events.The state maps (
ItemIDs,ToolCallIDs) are populated at lines 1056-1057 with the originaltoolUseIDbefore the thought signature is appended (lines 1069-1073). However, the emitted events use the modifiedtoolUseIDwith the signature appended.This inconsistency means
state.ItemIDs[outputIndex]won't match theItemIDin the emitted events, which could cause issues incloseGeminiOpenItems(line 1393) when it looks upitemID := state.ItemIDs[outputIndex].🔧 Proposed fix - update state after signature attachment
state.ItemIDs[outputIndex] = toolUseID state.ToolCallIDs[outputIndex] = toolUseID state.ToolCallNames[outputIndex] = part.FunctionCall.Name // Convert args to JSON string argsJSON := "" if part.FunctionCall.Args != nil { if argsBytes, err := sonic.Marshal(part.FunctionCall.Args); err == nil { argsJSON = string(argsBytes) } } state.ToolArgumentBuffers[outputIndex] = argsJSON // Attach thought signature to ID if present if len(part.ThoughtSignature) > 0 { encoded := base64.StdEncoding.EncodeToString(part.ThoughtSignature) toolUseID = fmt.Sprintf("%s%s%s", toolUseID, thoughtSignatureSeparator, encoded) + // Update state maps with the signature-appended ID + state.ItemIDs[outputIndex] = toolUseID + state.ToolCallIDs[outputIndex] = toolUseID }core/providers/anthropic/responses.go (2)
731-734: Fix potential nil deref onchunk.Deltainmessage_deltahandling.
if chunk.Delta.StopReason != nil { ... }will panic ifchunk.Delta == nil(you already treat it as nullable later in the same block).Proposed diff
case AnthropicStreamEventTypeMessageDelta: - if chunk.Delta.StopReason != nil { + if chunk.Delta != nil && chunk.Delta.StopReason != nil { state.StopReason = schemas.Ptr(ConvertAnthropicFinishReasonToBifrost(*chunk.Delta.StopReason)) }
898-924: OutputIndex-first mapping is consistent; ensure synthetic deltas use the same chosen index.The OutputIndex fallback logic is good, but
generateSyntheticInputJSONDeltas(..., bifrostResp.ContentIndex)will reintroduce ContentIndex-based indexing for the synthetic deltas, desyncing them from thecontent_block_startindex when OutputIndex is present.Proposed diff
- if shouldGenerateDeltas && argumentsJSON != "" { + if shouldGenerateDeltas && argumentsJSON != "" { // Generate synthetic input_json_delta events by chunking the JSON - deltaEvents := generateSyntheticInputJSONDeltas(argumentsJSON, bifrostResp.ContentIndex) + index := bifrostResp.OutputIndex + if index == nil { + index = bifrostResp.ContentIndex + } + deltaEvents := generateSyntheticInputJSONDeltas(argumentsJSON, index) events = append(events, deltaEvents...) }Also applies to: 1057-1063, 1073-1078, 1094-1099, 1128-1133, 1156-1161, 1198-1203, 1220-1225
🤖 Fix all issues with AI agents
In @core/providers/anthropic/responses.go:
- Around line 988-989: The code is incorrectly populating the tool-result
correlation into the tool_use-specific field ToolUseID; remove the assignment to
contentBlock.ToolUseID and instead set the block's standard id field used for
tool_use blocks (e.g., contentBlock.ID =
bifrostResp.Item.ResponsesToolMessage.CallID) while keeping contentBlock.Name
as-is, so you only emit the canonical id for tool_use blocks and avoid emitting
a separate tool_use_id.
In @core/providers/gemini/responses.go:
- Around line 469-489: The streaming branch currently assigns the full CallID
(including the thought signature) to functionCall.ID; change it to mirror the
non-streaming path by splitting bifrostResp.Item.ResponsesToolMessage.CallID on
thoughtSignatureSeparator, set functionCall.ID to the base ID (parts[0]) and
store the signature portion (parts[1]) only in functionCallPart.ThoughtSignature
(after base64 decoding) so functionCall.ID matches the non-streaming path; keep
the existing behavior of creating functionCallPart := &Part{ FunctionCall:
functionCall } and appending to candidate.Content.Parts.
In @core/schemas/chatcompletions.go:
- Around line 340-359: The UnmarshalJSON for AdditionalPropertiesStruct must
explicitly handle JSON null to avoid creating a non-nil pointer to a nil map; in
AdditionalPropertiesStruct.UnmarshalJSON check for the literal JSON null (e.g.,
bytes.Equal(data, []byte("null")) or equivalent) and if so set
AdditionalPropertiesBool = nil and AddtionalPropertiesMap = nil and return nil;
otherwise proceed with the existing boolean then map unmarshal attempts. Ensure
you clear both fields on null so MarshalJSON remains symmetric.
- Around line 312-315: The struct AdditionalPropertiesStruct has a typo in the
exported field AddtionalPropertiesMap — rename it to AdditionalPropertiesMap
(preserving pointer type *OrderedMap) and update all internal references to the
corrected name, including the methods MarshalJSON (where the field is
read/assigned) and UnmarshalJSON (where it’s populated), and any external usages
such as core/providers/cohere/utils.go; ensure you keep JSON
marshaling/unmarshaling behavior identical after the rename and run tests to
catch any missed references.
In @transports/bifrost-http/integrations/router.go:
- Around line 386-387: Remove the stray debug print in router.go that prints
"New request started" with fmt.Printf; replace it with a structured debug log
using the router's logger or delete it entirely. Specifically, remove the
fmt.Printf call near the request handling code and, if request-level logging is
desired, call g.logger.Debug with context (e.g., "New request started", "path",
string(ctx.Path()), "method", method, and any request ID) so logging uses
g.logger rather than stdout and includes useful context.
🧹 Nitpick comments (3)
core/providers/gemini/responses.go (1)
2388-2414: Minor: Dead code assignment.Line 2412 (
thoughtSig = "") has no effect sincethoughtSigis never used after this point. This can be safely removed.🧹 Suggested cleanup
if thoughtSig != "" { var err error part.ThoughtSignature, err = base64.StdEncoding.DecodeString(thoughtSig) if err != nil { // Silently ignore decode errors - ID will be used without signature - thoughtSig = "" } }core/providers/cohere/utils.go (1)
76-80: Consider handling the object case foradditionalProperties.The new
AdditionalPropertiesStructsupports both boolean and object types, but only the boolean case is handled here. If Cohere can returnadditionalPropertiesas an object (schema definition), it won't be captured.♻️ Suggested enhancement to handle both cases
// Extract additionalProperties if addPropsVal, ok := paramsMap["additionalProperties"].(bool); ok { result.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ AdditionalPropertiesBool: &addPropsVal, } + } else if orderedAddProps, ok := schemas.SafeExtractOrderedMap(paramsMap["additionalProperties"]); ok { + result.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ + AddtionalPropertiesMap: &orderedAddProps, + } }core/providers/anthropic/types.go (1)
248-334: HardenAnthropicCitations(un)marshal against reuse + make marshal receiver value-based.Two small robustness tweaks:
- In
UnmarshalJSON, clearac.Configwhen settingTextCitationsand clearac.TextCitationswhen settingConfig.- Consider making
MarshalJSONa value receiver (func (ac AnthropicCitations) MarshalJSON) so it’s honored even if a non-pointer instance is marshaled later.Proposed diff
-// Custom marshal/unmarshal methods -func (ac *AnthropicCitations) MarshalJSON() ([]byte, error) { +// Custom marshal/unmarshal methods +func (ac AnthropicCitations) MarshalJSON() ([]byte, error) { if ac.Config != nil && ac.TextCitations != nil { return nil, fmt.Errorf("AnthropicCitations: both Config and TextCitations are set; only one should be non-nil") } @@ return sonic.Marshal(nil) } func (ac *AnthropicCitations) UnmarshalJSON(data []byte) error { + // Defensive reset (helps if the same instance is reused) + ac.Config = nil + ac.TextCitations = nil + // Try to unmarshal as array of citations var textCitations []AnthropicTextCitation if err := sonic.Unmarshal(data, &textCitations); err == nil { ac.TextCitations = textCitations return nil } @@ var config schemas.Citations if err := sonic.Unmarshal(data, &config); err == nil { ac.Config = &config return nil }
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
anthropic-event-order.txtcore/providers/anthropic/responses.gocore/providers/anthropic/types.gocore/providers/cohere/utils.gocore/providers/gemini/responses.gocore/schemas/chatcompletions.gocore/schemas/responses.gotransports/bifrost-http/integrations/router.go
🧰 Additional context used
📓 Path-based instructions (1)
**
⚙️ CodeRabbit configuration file
always check the stack if there is one for the current PR. do not give localized reviews for the PR, always see all changes in the light of the whole stack of PRs (if there is a stack, if there is no stack you can continue to make localized suggestions/reviews)
Files:
core/providers/cohere/utils.gocore/providers/gemini/responses.gocore/schemas/chatcompletions.gotransports/bifrost-http/integrations/router.gocore/schemas/responses.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.go
🧠 Learnings (8)
📚 Learning: 2025-12-09T17:07:42.007Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/schemas/account.go:9-18
Timestamp: 2025-12-09T17:07:42.007Z
Learning: In core/schemas/account.go, the HuggingFaceKeyConfig field within the Key struct is currently unused and reserved for future Hugging Face inference endpoint deployments. Do not flag this field as missing from OpenAPI documentation or require its presence in the API spec until the feature is actively implemented and used. When the feature is added, update the OpenAPI docs accordingly; otherwise, treat this field as non-breaking and not part of the current API surface.
Applied to files:
core/providers/cohere/utils.gocore/providers/gemini/responses.gocore/schemas/chatcompletions.gotransports/bifrost-http/integrations/router.gocore/schemas/responses.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.go
📚 Learning: 2025-12-29T11:54:55.836Z
Learnt from: akshaydeo
Repo: maximhq/bifrost PR: 1153
File: framework/configstore/rdb.go:2221-2246
Timestamp: 2025-12-29T11:54:55.836Z
Learning: In Go reviews, do not flag range-over-int patterns like for i := range n as compile-time errors, assuming Go 1.22+ semantics. Only flag actual range-capable values (slices, arrays, maps, channels, strings) and other compile-time issues. This applies to all Go files across the repository.
Applied to files:
core/providers/cohere/utils.gocore/providers/gemini/responses.gocore/schemas/chatcompletions.gotransports/bifrost-http/integrations/router.gocore/schemas/responses.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.go
📚 Learning: 2025-12-19T09:26:54.961Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/providers/utils/utils.go:1050-1051
Timestamp: 2025-12-19T09:26:54.961Z
Learning: Update streaming end-marker handling so HuggingFace is treated as a non-[DONE] provider for backends that do not emit a DONE marker (e.g., meta llama on novita). In core/providers/utils/utils.go, adjust ProviderSendsDoneMarker() (or related logic) to detect providers that may not emit DONE and avoid relying on DONE as the sole end signal. Add tests to cover both DONE-emitting and non-DONE backends, with clear documentation in code comments explaining the rationale and any fallback behavior.
Applied to files:
core/providers/cohere/utils.gocore/providers/gemini/responses.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.go
📚 Learning: 2026-01-10T11:27:47.535Z
Learnt from: Radheshg04
Repo: maximhq/bifrost PR: 1256
File: core/providers/openai/openai.go:2276-2385
Timestamp: 2026-01-10T11:27:47.535Z
Learning: Validate image generation requests for nil and missing prompts before dispatch. Follow the same pattern used here: core/bifrost.go validates nil/empty prompts, providerUtils.CheckContextAndGetRequestBody returns a structured error when the request converter yields nil, and apply this across all providers (including OpenAI) to avoid sending null bodies.
Applied to files:
core/providers/cohere/utils.gocore/providers/gemini/responses.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.go
📚 Learning: 2025-12-17T08:44:08.788Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1114
File: core/schemas/chatcompletions.go:224-228
Timestamp: 2025-12-17T08:44:08.788Z
Learning: In core/schemas/chatcompletions.go, ensure the schema structures mirror OpenAI's API specifications exactly. Use the valid values for fields like ChatAudioParameters.Format and ChatAudioParameters.Voice as defined by OpenAI's documentation, and avoid adding additional inline documentation or constants to maintain direct compatibility with OpenAI's API.
Applied to files:
core/schemas/chatcompletions.go
📚 Learning: 2025-12-12T08:25:02.629Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1000
File: transports/bifrost-http/integrations/router.go:709-712
Timestamp: 2025-12-12T08:25:02.629Z
Learning: In transports/bifrost-http/**/*.go, update streaming response handling to align with OpenAI Responses API: use typed SSE events such as response.created, response.output_text.delta, response.done, etc., and do not rely on the legacy data: [DONE] termination marker. Note that data: [DONE] is only used by the older Chat Completions and Text Completions streaming APIs. Ensure parsers, writers, and tests distinguish SSE events from the [DONE] sentinel and handle each event type accordingly for correct stream termination and progress updates.
Applied to files:
transports/bifrost-http/integrations/router.go
📚 Learning: 2025-12-29T09:14:16.633Z
Learnt from: akshaydeo
Repo: maximhq/bifrost PR: 888
File: transports/bifrost-http/handlers/middlewares.go:246-256
Timestamp: 2025-12-29T09:14:16.633Z
Learning: In the bifrost HTTP transport, fasthttp.RequestCtx is the primary context carrier and should be passed directly to functions that expect a context.Context. Do not convert to context.Context unless explicitly required. Ensure tracer implementations and related components are designed to accept fasthttp.RequestCtx directly, and document this architectural decision for maintainers.
Applied to files:
transports/bifrost-http/integrations/router.go
📚 Learning: 2026-01-11T14:08:10.341Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1298
File: core/providers/anthropic/anthropic.go:682-699
Timestamp: 2026-01-11T14:08:10.341Z
Learning: In Anthroplic streaming implementations (and analogous providers), ensure that the final 'summary' chunk, which carries usage information and metadata, is emitted after all delta chunks and uses a chunk index of last_delta_index + 1. This differentiates the summary chunk from content delta chunks. Apply this convention consistently in the anthropic provider code and in similar streaming providers, and consider adding a targeted test to assert the ordering and chunk index logic.
Applied to files:
core/providers/anthropic/responses.gocore/providers/anthropic/types.go
🧬 Code graph analysis (4)
core/providers/cohere/utils.go (1)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(312-315)
core/providers/gemini/responses.go (3)
core/schemas/responses.go (1)
ResponsesToolMessage(475-495)ui/lib/types/logs.ts (2)
ResponsesToolMessage(419-425)FunctionCall(165-168)core/providers/gemini/types.go (3)
Part(1027-1051)FunctionCall(1233-1243)FunctionResponse(1246-1268)
core/providers/anthropic/responses.go (2)
core/schemas/utils.go (1)
Ptr(14-16)core/providers/anthropic/types.go (3)
AnthropicContentBlockTypeToolUse(207-207)AnthropicStreamDelta(517-525)AnthropicStreamEvent(496-505)
core/providers/anthropic/types.go (2)
core/schemas/chatcompletions.go (1)
CacheControl(699-702)core/schemas/responses.go (1)
Citations(421-423)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
🔇 Additional comments (9)
core/providers/gemini/responses.go (4)
15-16: LGTM - Clear constant definition for thought signature separator.The separator constant is well-documented. The implementation gracefully handles decode failures by falling back to the full ID, which mitigates the edge case where a user-defined tool ID might contain
_ts_.
249-298: LGTM - Robust thought signature extraction with fallback logic.The extraction logic correctly handles:
- Signatures embedded in CallID with graceful decode failure handling
- Fallback to look-ahead for reasoning messages
- Tracking consumed indices to avoid double-processing
The error handling on decode failure (lines 263-265) appropriately falls back to the full ID.
1878-1882: LGTM - Consistent thought signature encoding for non-streaming responses.The encoding format matches the streaming path, ensuring consistent ID format across both response paths.
2452-2463: LGTM - Correct base ID extraction for function responses.The function response correctly strips the thought signature from the CallID, ensuring proper matching with the original function call's base ID.
core/schemas/chatcompletions.go (2)
276-282: LGTM!The
ToolFunctionParametersstruct update correctly integrates the newAdditionalPropertiesStructtype, enabling support for both boolean and object representations ofadditionalPropertiesin JSON schemas.
317-338: LGTM!The
MarshalJSONimplementation correctly handles the mutually exclusive nature of the bool/map representation with proper validation and fallback tonullwhen both are nil.core/providers/anthropic/types.go (1)
219-237: Newweb_search_resultfields look consistent; verify they’re only emitted for that block type.
URL,EncryptedContent, andPageAgeare correctlyomitempty, but ensure your converters don’t accidentally populate them for non-web_search_resultblocks (some clients validate bytype).core/providers/anthropic/responses.go (1)
937-945: Signature oncontent_block_start: verify Anthropic clients expectsignature:""(vs omitted).Setting
Signature = Ptr("")forces"signature":""into the event (since it’somitemptybut pointer is non-nil). If the goal is “field present but empty until signature_delta,” confirm downstream parsers don’t interpret empty signature as “final signature is empty” / invalid.Also applies to: 953-958, 981-985
core/schemas/responses.go (1)
421-423: Thetype Citationsdefinition is unique in the package and has no compilation issues.The original concern about a "compile-blocker" is incorrect. Go allows a type named
Citationsto coexist with struct fields namedCitations []stringwithout conflict, as they exist in different namespaces. The newCitationsstruct is already in use incore/providers/anthropic/types.go(e.g.,Config *schemas.Citations), confirming intentional design.A doc comment clarifying this struct's purpose (e.g., "// Citations configuration for citation handling") would be helpful for maintainability, but this is a minor suggestion, not a compilation concern.
1ce1ee8 to
91e06c6
Compare
28f9996 to
1cb5e03
Compare
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: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
core/providers/gemini/responses.go (1)
1040-1064: Fix inconsistent tool call IDs: state maps store original ID while emitted events use augmented ID.The
state.ItemIDs[outputIndex]is set at line 1047 with the originaltoolUseID, but at line 1063toolUseIDis augmented with the base64-encodedThoughtSignature. The emitted events at lines 1069-1102 (bothoutput_item.addedandoutput_item.function_call_arguments_done) use the augmented ID. However, whencloseGeminiOpenItemsretrieves the ID fromstate.ItemIDs[outputIndex]at line 1384, it gets the original unaugmented ID and emits the finaloutput_item.donewith this original ID. This causes the same tool call to have different IDs across its lifecycle—augmented in initial events, original in the final done event—breaking ID correlation on the client side.
🤖 Fix all issues with AI agents
In @core/providers/anthropic/responses.go:
- Line 937: Remove the empty signature initialization on content_block_start
events: do not set contentBlock.Signature = schemas.Ptr("") in the
content_block_start handling (so the Signature field is nil/omitted), since
signature metadata is delivered via signature_delta events; ensure only
signature_delta handling (e.g., the existing signature_delta logic around
signature_delta event handling) sets contentBlock.Signature, and remove or guard
any assignment that forces an empty string pointer for ContentBlock.Signature to
prevent `"signature": ""` from being serialized.
🧹 Nitpick comments (4)
core/providers/cohere/utils.go (1)
75-80: Consider handling object-type additionalProperties for completeness.The boolean case is correctly wrapped in the new
AdditionalPropertiesStruct. However, the schema now also supports object-typeadditionalPropertiesviaAdditionalPropertiesMap. If Cohere's API ever sendsadditionalPropertiesas an object (e.g.,{"type": "string"}), it would be silently ignored.♻️ Suggested enhancement
// Extract additionalProperties if addPropsVal, ok := paramsMap["additionalProperties"].(bool); ok { result.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ AdditionalPropertiesBool: &addPropsVal, } + } else if addPropsMap, ok := paramsMap["additionalProperties"].(map[string]interface{}); ok { + orderedMap := schemas.OrderedMap(addPropsMap) + result.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ + AdditionalPropertiesMap: &orderedMap, + } }core/providers/gemini/responses.go (2)
15-17: Consider documenting the separator collision risk.The separator
_ts_could theoretically collide with tool IDs that legitimately contain this substring. WhileSplitN(..., 2)ensures only the first occurrence is used for splitting, a tool namedmy_ts_toolwould be incorrectly parsed if a thought signature is appended.Consider documenting this constraint or using a more unique separator (e.g.,
::ts::or a control character if the ID format permits).
2397-2404: Consider debug logging for base64 decode failures.When
base64.StdEncoding.DecodeStringfails, the error is silently swallowed. While graceful degradation is appropriate, a debug-level log would help troubleshoot cases where thought signatures are unexpectedly lost.♻️ Optional: Add debug logging
if thoughtSig != "" { var err error part.ThoughtSignature, err = base64.StdEncoding.DecodeString(thoughtSig) if err != nil { - // Silently ignore decode errors - ID will be used without signature - thoughtSig = "" + // Log decode error for debugging, ID will be used without signature + // log.Debug().Err(err).Str("thoughtSig", thoughtSig).Msg("failed to decode thought signature") + part.ThoughtSignature = nil } }core/providers/anthropic/types.go (1)
304-334: UnmarshalJSON order may cause incorrect parsing for empty config objects.The current
UnmarshalJSONimplementation tries to unmarshal as[]AnthropicTextCitationfirst (line 321), then falls back toschemas.Citationsconfig (line 328). However, an empty JSON object{}would successfully unmarshal as an emptyschemas.Citationsstruct, but it would also fail to unmarshal as an array, so the current order works.The potential issue is with the config object:
schemas.Citationshas only an optionalEnabled *boolfield. If Anthropic sends{"enabled": true}, this correctly becomes a config. But if the response contains an empty array[], this would unmarshal as an emptyTextCitationsslice rather than treating it as "no citations" - which may be the intended behavior but is worth verifying.Consider adding a nil/empty check or documentation clarifying the expected behavior for edge cases.
📝 Optional: Add clarifying comment for unmarshal order rationale
func (ac *AnthropicCitations) UnmarshalJSON(data []byte) error { - // Try to unmarshal as array of citations + // Try to unmarshal as array of citations first (response format) + // Note: Empty array [] will result in empty TextCitations slice var textCitations []AnthropicTextCitation if err := sonic.Unmarshal(data, &textCitations); err == nil { ac.TextCitations = textCitations return nil } - // Try to unmarshal as config object first + // Fall back to config object (request format: {"enabled": true}) var config schemas.Citations
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
core/providers/anthropic/responses.gocore/providers/anthropic/types.gocore/providers/cohere/utils.gocore/providers/gemini/responses.gocore/schemas/chatcompletions.gocore/schemas/responses.gotransports/bifrost-http/integrations/router.go
💤 Files with no reviewable changes (1)
- transports/bifrost-http/integrations/router.go
🚧 Files skipped from review as they are similar to previous changes (1)
- core/schemas/responses.go
🧰 Additional context used
📓 Path-based instructions (1)
**
⚙️ CodeRabbit configuration file
always check the stack if there is one for the current PR. do not give localized reviews for the PR, always see all changes in the light of the whole stack of PRs (if there is a stack, if there is no stack you can continue to make localized suggestions/reviews)
Files:
core/schemas/chatcompletions.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.gocore/providers/cohere/utils.gocore/providers/gemini/responses.go
🧠 Learnings (6)
📚 Learning: 2025-12-09T17:07:42.007Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/schemas/account.go:9-18
Timestamp: 2025-12-09T17:07:42.007Z
Learning: In core/schemas/account.go, the HuggingFaceKeyConfig field within the Key struct is currently unused and reserved for future Hugging Face inference endpoint deployments. Do not flag this field as missing from OpenAPI documentation or require its presence in the API spec until the feature is actively implemented and used. When the feature is added, update the OpenAPI docs accordingly; otherwise, treat this field as non-breaking and not part of the current API surface.
Applied to files:
core/schemas/chatcompletions.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.gocore/providers/cohere/utils.gocore/providers/gemini/responses.go
📚 Learning: 2025-12-29T11:54:55.836Z
Learnt from: akshaydeo
Repo: maximhq/bifrost PR: 1153
File: framework/configstore/rdb.go:2221-2246
Timestamp: 2025-12-29T11:54:55.836Z
Learning: In Go reviews, do not flag range-over-int patterns like for i := range n as compile-time errors, assuming Go 1.22+ semantics. Only flag actual range-capable values (slices, arrays, maps, channels, strings) and other compile-time issues. This applies to all Go files across the repository.
Applied to files:
core/schemas/chatcompletions.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.gocore/providers/cohere/utils.gocore/providers/gemini/responses.go
📚 Learning: 2025-12-17T08:44:08.788Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1114
File: core/schemas/chatcompletions.go:224-228
Timestamp: 2025-12-17T08:44:08.788Z
Learning: In core/schemas/chatcompletions.go, ensure the schema structures mirror OpenAI's API specifications exactly. Use the valid values for fields like ChatAudioParameters.Format and ChatAudioParameters.Voice as defined by OpenAI's documentation, and avoid adding additional inline documentation or constants to maintain direct compatibility with OpenAI's API.
Applied to files:
core/schemas/chatcompletions.go
📚 Learning: 2025-12-19T09:26:54.961Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/providers/utils/utils.go:1050-1051
Timestamp: 2025-12-19T09:26:54.961Z
Learning: Update streaming end-marker handling so HuggingFace is treated as a non-[DONE] provider for backends that do not emit a DONE marker (e.g., meta llama on novita). In core/providers/utils/utils.go, adjust ProviderSendsDoneMarker() (or related logic) to detect providers that may not emit DONE and avoid relying on DONE as the sole end signal. Add tests to cover both DONE-emitting and non-DONE backends, with clear documentation in code comments explaining the rationale and any fallback behavior.
Applied to files:
core/providers/anthropic/responses.gocore/providers/anthropic/types.gocore/providers/cohere/utils.gocore/providers/gemini/responses.go
📚 Learning: 2026-01-10T11:27:47.535Z
Learnt from: Radheshg04
Repo: maximhq/bifrost PR: 1256
File: core/providers/openai/openai.go:2276-2385
Timestamp: 2026-01-10T11:27:47.535Z
Learning: Validate image generation requests for nil and missing prompts before dispatch. Follow the same pattern used here: core/bifrost.go validates nil/empty prompts, providerUtils.CheckContextAndGetRequestBody returns a structured error when the request converter yields nil, and apply this across all providers (including OpenAI) to avoid sending null bodies.
Applied to files:
core/providers/anthropic/responses.gocore/providers/anthropic/types.gocore/providers/cohere/utils.gocore/providers/gemini/responses.go
📚 Learning: 2026-01-11T14:08:10.341Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1298
File: core/providers/anthropic/anthropic.go:682-699
Timestamp: 2026-01-11T14:08:10.341Z
Learning: In Anthroplic streaming implementations (and analogous providers), ensure that the final 'summary' chunk, which carries usage information and metadata, is emitted after all delta chunks and uses a chunk index of last_delta_index + 1. This differentiates the summary chunk from content delta chunks. Apply this convention consistently in the anthropic provider code and in similar streaming providers, and consider adding a targeted test to assert the ordering and chunk index logic.
Applied to files:
core/providers/anthropic/responses.gocore/providers/anthropic/types.go
🧬 Code graph analysis (5)
core/schemas/chatcompletions.go (2)
core/schemas/json_native.go (2)
Marshal(8-10)Unmarshal(18-20)core/schemas/json_wasm.go (2)
Marshal(8-10)Unmarshal(22-24)
core/providers/anthropic/responses.go (3)
core/schemas/utils.go (1)
Ptr(14-16)core/providers/anthropic/types.go (1)
AnthropicStreamDelta(517-525)core/providers/anthropic/utils.go (1)
ConvertBifrostFinishReasonToAnthropic(88-93)
core/providers/anthropic/types.go (2)
core/schemas/chatcompletions.go (1)
CacheControl(705-708)core/schemas/responses.go (1)
Citations(421-423)
core/providers/cohere/utils.go (1)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(312-315)
core/providers/gemini/responses.go (2)
core/schemas/responses.go (2)
ResponsesToolMessage(475-495)ResponsesReasoning(745-748)core/providers/gemini/types.go (3)
Part(1027-1051)FunctionCall(1233-1243)Content(1013-1021)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
🔇 Additional comments (14)
core/schemas/chatcompletions.go (2)
276-282: LGTM!The field type change correctly supports both boolean and object representations for
additionalProperties, aligning with JSON Schema specification. The custom JSON marshalling ensures backward compatibility with existing JSON payloads.
312-365: Well-structured union type with proper JSON handling.The implementation correctly handles:
- Mutual exclusivity validation in
MarshalJSON- Direct marshaling of bool/object without wrapper (maintains JSON compatibility)
- Graceful null handling in
UnmarshalJSON- Clear error messages when input is neither boolean nor object
core/providers/gemini/responses.go (5)
249-289: LGTM!The thought signature extraction logic correctly prioritizes embedded signatures in CallID over lookahead reasoning messages. The
consumedIndicesmap properly prevents double-processing, and error handling gracefully degrades when decoding fails.
460-480: LGTM!The streaming path correctly mirrors the non-streaming extraction logic, maintaining consistency in how thought signatures are parsed from CallIDs.
1869-1886: LGTM!The non-streaming Gemini-to-Bifrost conversion correctly encodes and appends the thought signature to the CallID, maintaining consistency with the streaming path in
processGeminiFunctionCallPart.
2442-2449: LGTM!The FunctionResponse ID assignment correctly uses the CallID for proper matching between function calls and their responses.
2388-2396: Gemini API accepts arbitrary ID strings—no format validation concern.The Gemini API FunctionCall.ID field accepts arbitrary strings with no documented format requirements. The only constraint is that if an ID is provided, the client must echo the same ID in the response for matching. Keeping the full CallID with the embedded
_ts_base64signaturesuffix will not cause validation failures or issues with the Gemini API.Likely an incorrect or invalid review comment.
core/providers/anthropic/responses.go (4)
898-924: LGTM - Consistent index handling in computer tool and text content blocks.The pattern of prioritizing
OutputIndexwithContentIndexas fallback is correctly applied for both computer tool calls (lines 898-902) and other content blocks (lines 920-924). This aligns with the PR objective of consistent global indexing for Anthropic.
1056-1062: LGTM - OutputTextDelta index handling updated correctly.The fallback pattern and inline comment clearly document the rationale for preferring
OutputIndexoverContentIndex.
1197-1224: LGTM - MCP delta and completion handlers follow consistent index pattern.Both
MCPCallArgumentsDelta(lines 1197-1202) andMCPCallCompleted(lines 1219-1224) correctly apply the OutputIndex-first, ContentIndex-fallback pattern.
1169-1192: Confirm the JSON serialization behavior ofStopSequenceinAnthropicStreamDeltaresponses.The asymmetry is real: when
bifrostResp.Response.StopReasonis nil,StopSequenceis set to an empty string pointer (""), but when a stop reason is present, it's set tonil. SinceAnthropicStreamDelta.StopSequenceis defined as*stringwithjson:"stop_sequence"(noomitempty), these serialize differently—empty string becomes"stop_sequence": ""whilenilbecomes"stop_sequence": null. Verify this mixed behavior matches Anthropic's expected response format.core/providers/anthropic/types.go (3)
218-238: LGTM - AnthropicContentBlock expanded with web search and metadata fields.The new fields (
URL,EncryptedContent,PageAge) properly support web search result content blocks. The existingSignaturefield placement is appropriate for thinking content. Field documentation via inline comments is clear.
248-291: LGTM - Comprehensive citation type definitions.The
AnthropicTextCitationstruct properly supports all five citation location types with their respective fields:
char_location: StartCharIndex, EndCharIndexpage_location: StartPageNumber, EndPageNumbercontent_block_location: StartBlockIndex, EndBlockIndexweb_search_result_location: EncryptedIndex, Title, URLsearch_result_location: SearchResultIndex, SourceThe use of pointer types for optional fields and clear inline documentation makes the intent clear.
305-307: The field design is sound as-is.The
Citationsfield is correctly tagged withjson:"citations,omitempty", so when theAnthropicCitationsvalue itself is nil, the field is omitted entirely from the JSON output. WhenAnthropicCitationsis non-nil but bothConfigandTextCitationsare nil,MarshalJSONreturnsnull, which is acceptable behavior. The validation check preventing both fields from being set simultaneously is appropriate.
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 (3)
core/providers/gemini/responses.go (2)
496-501: Bug:OutputItemAddedthought signature uses raw base64 string bytes instead of decoding.
This will produce a differentThoughtSignaturethan the non-stream path (and than other stream paths that decode).Proposed fix
case schemas.ResponsesStreamResponseTypeOutputItemAdded: - if bifrostResp.Item != nil && bifrostResp.Item.ResponsesReasoning != nil && bifrostResp.Item.EncryptedContent != nil { - candidate.Content.Parts = append(candidate.Content.Parts, &Part{ - ThoughtSignature: []byte(*bifrostResp.Item.ResponsesReasoning.EncryptedContent), - }) - } + if bifrostResp.Item != nil && bifrostResp.Item.ResponsesReasoning != nil && bifrostResp.Item.ResponsesReasoning.EncryptedContent != nil { + if decoded, err := base64.StdEncoding.DecodeString(*bifrostResp.Item.ResponsesReasoning.EncryptedContent); err == nil { + candidate.Content.Parts = append(candidate.Content.Parts, &Part{ + ThoughtSignature: decoded, + }) + } + }
1060-1103: Bug: stream state stores the pre-signature tool ID, but emitted events use the post-signature ID.
closeGeminiOpenItems()later emitsoutput_item.doneusingstate.ItemIDs[outputIndex], which currently won’t match the earlieroutput_item.added/function_call_arguments.doneIDs when a thought signature exists.Proposed fix
toolUseID := part.FunctionCall.ID if toolUseID == "" { toolUseID = part.FunctionCall.Name // Fallback to name as ID } - state.ItemIDs[outputIndex] = toolUseID - state.ToolCallIDs[outputIndex] = toolUseID - state.ToolCallNames[outputIndex] = part.FunctionCall.Name + state.ToolCallNames[outputIndex] = part.FunctionCall.Name // Convert args to JSON string argsJSON := "" if part.FunctionCall.Args != nil { if argsBytes, err := sonic.Marshal(part.FunctionCall.Args); err == nil { argsJSON = string(argsBytes) } } state.ToolArgumentBuffers[outputIndex] = argsJSON // Attach thought signature to ID if present if len(part.ThoughtSignature) > 0 { encoded := base64.StdEncoding.EncodeToString(part.ThoughtSignature) toolUseID = fmt.Sprintf("%s%s%s", toolUseID, thoughtSignatureSeparator, encoded) } + + // Store the final ID (must match emitted events) + state.ItemIDs[outputIndex] = toolUseID + state.ToolCallIDs[outputIndex] = toolUseIDcore/schemas/chatcompletions.go (1)
276-308: Add unit tests forToolFunctionParameters.UnmarshalJSONwith stringified JSON parameters.The implementation now supports xAI-style stringified JSON parameters (
"parameters": "{...json...}") in addition to standard JSON objects. This is a behavior change that should be covered by tests including:
- Valid stringified JSON input → parsed correctly
- Invalid/malformed stringified JSON → error handling
Note:
additionalPropertiescorrectly supports both boolean and object (schema) per JSON Schema spec, which theAdditionalPropertiesStructimplementation handles. OpenAI requiresadditionalProperties: false(boolean) for Structured Outputs; xAI accepts stringified parameters.
🤖 Fix all issues with AI agents
In @core/providers/anthropic/types.go:
- Around line 248-334: When unmarshalling in AnthropicCitations.UnmarshalJSON,
clear the mutually exclusive field before setting the other to avoid stale state
(i.e., set ac.Config = nil before assigning TextCitations and set
ac.TextCitations = nil before assigning Config); apply the same fix to
AdditionalPropertiesStruct.UnmarshalJSON for its mutually exclusive fields so
MarshalJSON's exclusivity contract can't be violated by reused struct instances.
In @core/providers/cohere/utils.go:
- Around line 75-80: The current block only handles boolean additionalProperties
and drops object-valued cases; extend it to also call SafeExtractOrderedMap on
paramsMap["additionalProperties"] when it isn't a bool, convert the returned
ordered map into the same schema map type you use for `properties` (reuse the
conversion logic used around the `properties` extraction), and set
result.AdditionalProperties = &schemas.AdditionalPropertiesStruct{
AdditionalPropertiesMap: <convertedMap> } so both boolean and object forms are
supported alongside the existing AdditionalPropertiesBool handling.
In @core/providers/gemini/responses.go:
- Around line 15-17: The code uses base64.StdEncoding to encode thought
signatures for Gemini tool IDs which yields illegal characters (+, /, =) and
will break IDs; update the encoding to a URL-safe variant such as
base64.RawURLEncoding (or base64.URLEncoding) wherever the thought signature is
encoded (look for calls that produce the signature string paired with
thoughtSignatureSeparator) so the resulting tool ID matches
/^[a-zA-Z0-9_-]{1,64}$/; ensure padding is removed (use RawURLEncoding) or
otherwise sanitize/replace forbidden characters consistently so all generated
tool IDs conform to Gemini constraints.
In @core/schemas/chatcompletions.go:
- Around line 312-365: UnmarshalJSON on AdditionalPropertiesStruct must clear
the opposite field when one variant is decoded to avoid stale state; update
AdditionalPropertiesStruct.UnmarshalJSON so that when the input is "null" both
AdditionalPropertiesBool and AdditionalPropertiesMap are set to nil, when a
boolean is successfully decoded set AdditionalPropertiesBool to the value and
set AdditionalPropertiesMap = nil, and when a map (OrderedMap) is successfully
decoded set AdditionalPropertiesMap to the value and set
AdditionalPropertiesBool = nil; ensure the function still returns an error only
if neither decode succeeds.
🧹 Nitpick comments (4)
core/schemas/responses.go (1)
421-423: Add a doc comment for exportedCitationsand clarify intent (request-config vs response citations).
This is an exported schema type and is easy to confuse with existingCitations []stringfields and annotation citations.Proposed tweak
+// Citations represents provider-specific citations configuration (e.g., enabling citations in downstream requests). type Citations struct { Enabled *bool `json:"enabled,omitempty"` }core/providers/gemini/responses.go (3)
249-289: Non-stream thought-signature extraction + “consume next reasoning message” looks coherent; add a regression test for ordering.
Specifically: function_call followed by reasoning(encrypted_content), and the variant where signature is embedded in CallID.
460-480: Streaming path: signature parsing is OK, but keep the encoding consistent with the producer.
Right now this assumes the CallID always embeds a base64.StdEncoding payload; if you later switch to URL-safe encoding, this must be updated everywhere.
1869-1873: CallID augmentation in non-stream output path matches streaming intent; watch for ID bloat.
IfThoughtSignatureis large, embedding it into CallID can produce very large IDs; consider a size cap / fallback strategy.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
core/providers/anthropic/responses.gocore/providers/anthropic/types.gocore/providers/cohere/utils.gocore/providers/gemini/responses.gocore/schemas/chatcompletions.gocore/schemas/responses.gotransports/bifrost-http/integrations/router.go
💤 Files with no reviewable changes (1)
- transports/bifrost-http/integrations/router.go
🧰 Additional context used
📓 Path-based instructions (1)
**
⚙️ CodeRabbit configuration file
always check the stack if there is one for the current PR. do not give localized reviews for the PR, always see all changes in the light of the whole stack of PRs (if there is a stack, if there is no stack you can continue to make localized suggestions/reviews)
Files:
core/schemas/chatcompletions.gocore/schemas/responses.gocore/providers/gemini/responses.gocore/providers/cohere/utils.gocore/providers/anthropic/types.gocore/providers/anthropic/responses.go
🧠 Learnings (6)
📚 Learning: 2025-12-09T17:07:42.007Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/schemas/account.go:9-18
Timestamp: 2025-12-09T17:07:42.007Z
Learning: In core/schemas/account.go, the HuggingFaceKeyConfig field within the Key struct is currently unused and reserved for future Hugging Face inference endpoint deployments. Do not flag this field as missing from OpenAPI documentation or require its presence in the API spec until the feature is actively implemented and used. When the feature is added, update the OpenAPI docs accordingly; otherwise, treat this field as non-breaking and not part of the current API surface.
Applied to files:
core/schemas/chatcompletions.gocore/schemas/responses.gocore/providers/gemini/responses.gocore/providers/cohere/utils.gocore/providers/anthropic/types.gocore/providers/anthropic/responses.go
📚 Learning: 2025-12-29T11:54:55.836Z
Learnt from: akshaydeo
Repo: maximhq/bifrost PR: 1153
File: framework/configstore/rdb.go:2221-2246
Timestamp: 2025-12-29T11:54:55.836Z
Learning: In Go reviews, do not flag range-over-int patterns like for i := range n as compile-time errors, assuming Go 1.22+ semantics. Only flag actual range-capable values (slices, arrays, maps, channels, strings) and other compile-time issues. This applies to all Go files across the repository.
Applied to files:
core/schemas/chatcompletions.gocore/schemas/responses.gocore/providers/gemini/responses.gocore/providers/cohere/utils.gocore/providers/anthropic/types.gocore/providers/anthropic/responses.go
📚 Learning: 2025-12-17T08:44:08.788Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1114
File: core/schemas/chatcompletions.go:224-228
Timestamp: 2025-12-17T08:44:08.788Z
Learning: In core/schemas/chatcompletions.go, ensure the schema structures mirror OpenAI's API specifications exactly. Use the valid values for fields like ChatAudioParameters.Format and ChatAudioParameters.Voice as defined by OpenAI's documentation, and avoid adding additional inline documentation or constants to maintain direct compatibility with OpenAI's API.
Applied to files:
core/schemas/chatcompletions.go
📚 Learning: 2025-12-19T09:26:54.961Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/providers/utils/utils.go:1050-1051
Timestamp: 2025-12-19T09:26:54.961Z
Learning: Update streaming end-marker handling so HuggingFace is treated as a non-[DONE] provider for backends that do not emit a DONE marker (e.g., meta llama on novita). In core/providers/utils/utils.go, adjust ProviderSendsDoneMarker() (or related logic) to detect providers that may not emit DONE and avoid relying on DONE as the sole end signal. Add tests to cover both DONE-emitting and non-DONE backends, with clear documentation in code comments explaining the rationale and any fallback behavior.
Applied to files:
core/providers/gemini/responses.gocore/providers/cohere/utils.gocore/providers/anthropic/types.gocore/providers/anthropic/responses.go
📚 Learning: 2026-01-10T11:27:47.535Z
Learnt from: Radheshg04
Repo: maximhq/bifrost PR: 1256
File: core/providers/openai/openai.go:2276-2385
Timestamp: 2026-01-10T11:27:47.535Z
Learning: Validate image generation requests for nil and missing prompts before dispatch. Follow the same pattern used here: core/bifrost.go validates nil/empty prompts, providerUtils.CheckContextAndGetRequestBody returns a structured error when the request converter yields nil, and apply this across all providers (including OpenAI) to avoid sending null bodies.
Applied to files:
core/providers/gemini/responses.gocore/providers/cohere/utils.gocore/providers/anthropic/types.gocore/providers/anthropic/responses.go
📚 Learning: 2026-01-11T14:08:10.341Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1298
File: core/providers/anthropic/anthropic.go:682-699
Timestamp: 2026-01-11T14:08:10.341Z
Learning: In Anthroplic streaming implementations (and analogous providers), ensure that the final 'summary' chunk, which carries usage information and metadata, is emitted after all delta chunks and uses a chunk index of last_delta_index + 1. This differentiates the summary chunk from content delta chunks. Apply this convention consistently in the anthropic provider code and in similar streaming providers, and consider adding a targeted test to assert the ordering and chunk index logic.
Applied to files:
core/providers/anthropic/types.gocore/providers/anthropic/responses.go
🧬 Code graph analysis (2)
core/providers/cohere/utils.go (1)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(312-315)
core/providers/anthropic/responses.go (4)
core/schemas/utils.go (1)
Ptr(14-16)core/providers/anthropic/types.go (4)
AnthropicStreamDelta(517-525)AnthropicStopReasonEndTurn(433-433)AnthropicStreamEvent(496-505)AnthropicStreamEventTypeContentBlockDelta(488-488)core/providers/anthropic/utils.go (1)
ConvertBifrostFinishReasonToAnthropic(88-93)core/schemas/responses.go (1)
ResponsesStreamResponseTypeMCPCallArgumentsDelta(1420-1420)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Graphite / mergeability_check
🔇 Additional comments (9)
core/providers/gemini/responses.go (1)
2380-2404: Good: Responses→Gemini parses signature from CallID but keeps the full ID for round-tripping.
One tweak: if decode succeeds, consider also validating you got non-empty bytes (to avoid treating""as a real signature).Also applies to: 2446-2447
core/providers/anthropic/types.go (1)
219-237: Fields are valid and correctly implemented per Anthropic API.The
url,encrypted_content, andpage_agefields are confirmed as valid Anthropic API fields forweb_search_resultcontent blocks. All are correctly declared as optional (*stringwithomitemptytags) and the JSON tag names match the specification exactly.core/providers/anthropic/responses.go (7)
898-902: LGTM! OutputIndex prioritization is correctly implemented.The change properly prefers
OutputIndexfor global Anthropic indexing withContentIndexas a fallback, ensuring consistent index handling for computer tool content blocks.
919-937: LGTM! Consistent index handling and signature initialization.The OutputIndex-first pattern is correctly applied, and initializing
Signatureto an empty string for thinking blocks ensures proper field presence in the Anthropic streaming response.
946-993: LGTM! Signature field propagated through all thinking block paths.The signature initialization is consistently applied across:
- Line 953: Misclassified FunctionCall that contains reasoning content
- Line 983: First block when thinking is enabled
This ensures metadata integrity for encrypted thinking content across all edge cases.
1054-1098: LGTM! Consistent OutputIndex-first pattern across all delta handlers.The index handling is uniformly applied to:
OutputTextDelta(lines 1056-1061)FunctionCallArgumentsDelta(lines 1072-1076)ReasoningSummaryTextDelta(lines 1093-1097)All include proper fallback to
ContentIndexwhenOutputIndexis not available.
1127-1161: LGTM! OutputItemDone handlers consistently updated.Both the computer tool path (lines 1127-1131) and the general content block path (lines 1155-1159) now use the same OutputIndex-first pattern with ContentIndex fallback.
1165-1192: LGTM! Proper stop reason handling in message_stop events.The implementation correctly:
- Sets default
StopReasontoend_turnwith an emptyStopSequencepointer (lines 1169-1172)- Overrides with the converted Bifrost stop reason when available, with
StopSequence: nil(lines 1187-1189)This aligns with Anthropic's API specification where
stop_sequenceis present but empty forend_turnand may be omitted for other stop reasons.
1194-1224: LGTM! MCP handlers consistently updated with OutputIndex-first pattern.Both
MCPCallArgumentsDelta(lines 1197-1201) andMCPCallCompleted(lines 1219-1223) follow the same index selection logic as other handlers, ensuring consistent global indexing for MCP tool calls.
91e06c6 to
3dfb95c
Compare
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: 1
🤖 Fix all issues with AI agents
In @core/schemas/chatcompletions.go:
- Around line 276-282: The type of AdditionalProperties changed from *bool to
*AdditionalPropertiesStruct; update all places that pass a *bool to instead
allocate an AdditionalPropertiesStruct with its AdditionalPropertiesBool field
set to the existing *bool (e.g. replace schemas.Ptr(additionalProps) or
&additionalProps with &AdditionalPropertiesStruct{AdditionalPropertiesBool:
schemas.Ptr(additionalProps)}), specifically in the helper constructing
ToolFunctionParameters.AdditionalProperties in core/providers/gemini/utils.go
(current call creating schemas.Ptr(additionalProps)), in the test at
core/providers/gemini/gemini_test.go (replace schemas.Ptr(false) with
&AdditionalPropertiesStruct{AdditionalPropertiesBool: schemas.Ptr(false)}), in
core/providers/anthropic/utils.go (wrap &additionalProps into
&AdditionalPropertiesStruct{AdditionalPropertiesBool: &additionalProps} or
schemas.Ptr), and in core/internal/testutil/structured_outputs.go at the two
locations mentioned; keep the new struct wrapping consistent with
core/providers/cohere/utils.go.
🧹 Nitpick comments (3)
core/providers/gemini/responses.go (2)
15-16: Consider documenting the separator choice and potential collision risks.The separator
_ts_is embedded in tool IDs to carry thought signatures. If a legitimate tool ID contains_ts_, it could be incorrectly parsed as having a signature.Consider either:
- Using a more unique separator (e.g.,
__bifrost_ts__)- Adding validation that the signature part is valid base64 before treating it as a signature
2380-2404: Silent error handling for signature decode is appropriate here.The code silently ignores base64 decode errors (lines 2399-2402), which is reasonable since an invalid signature shouldn't break the entire conversion. The comment at line 2401 documents this behavior.
However, consider logging at debug level for troubleshooting purposes.
core/schemas/chatcompletions.go (1)
336-338: Consider returning[]byte("null")directly instead ofMarshal(nil).For consistency with other marshal methods in this file (e.g., line 401) and to avoid an unnecessary function call:
♻️ Suggested minor optimization
// If both are nil, return null - return Marshal(nil) + return []byte("null"), nil
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
core/providers/anthropic/responses.gocore/providers/anthropic/types.gocore/providers/cohere/utils.gocore/providers/gemini/responses.gocore/schemas/chatcompletions.gocore/schemas/responses.gotransports/bifrost-http/integrations/router.go
💤 Files with no reviewable changes (1)
- transports/bifrost-http/integrations/router.go
🚧 Files skipped from review as they are similar to previous changes (1)
- core/providers/cohere/utils.go
🧰 Additional context used
📓 Path-based instructions (1)
**
⚙️ CodeRabbit configuration file
always check the stack if there is one for the current PR. do not give localized reviews for the PR, always see all changes in the light of the whole stack of PRs (if there is a stack, if there is no stack you can continue to make localized suggestions/reviews)
Files:
core/providers/anthropic/responses.gocore/providers/anthropic/types.gocore/providers/gemini/responses.gocore/schemas/chatcompletions.gocore/schemas/responses.go
🧠 Learnings (7)
📚 Learning: 2025-12-09T17:07:42.007Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/schemas/account.go:9-18
Timestamp: 2025-12-09T17:07:42.007Z
Learning: In core/schemas/account.go, the HuggingFaceKeyConfig field within the Key struct is currently unused and reserved for future Hugging Face inference endpoint deployments. Do not flag this field as missing from OpenAPI documentation or require its presence in the API spec until the feature is actively implemented and used. When the feature is added, update the OpenAPI docs accordingly; otherwise, treat this field as non-breaking and not part of the current API surface.
Applied to files:
core/providers/anthropic/responses.gocore/providers/anthropic/types.gocore/providers/gemini/responses.gocore/schemas/chatcompletions.gocore/schemas/responses.go
📚 Learning: 2025-12-29T11:54:55.836Z
Learnt from: akshaydeo
Repo: maximhq/bifrost PR: 1153
File: framework/configstore/rdb.go:2221-2246
Timestamp: 2025-12-29T11:54:55.836Z
Learning: In Go reviews, do not flag range-over-int patterns like for i := range n as compile-time errors, assuming Go 1.22+ semantics. Only flag actual range-capable values (slices, arrays, maps, channels, strings) and other compile-time issues. This applies to all Go files across the repository.
Applied to files:
core/providers/anthropic/responses.gocore/providers/anthropic/types.gocore/providers/gemini/responses.gocore/schemas/chatcompletions.gocore/schemas/responses.go
📚 Learning: 2025-12-19T09:26:54.961Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/providers/utils/utils.go:1050-1051
Timestamp: 2025-12-19T09:26:54.961Z
Learning: Update streaming end-marker handling so HuggingFace is treated as a non-[DONE] provider for backends that do not emit a DONE marker (e.g., meta llama on novita). In core/providers/utils/utils.go, adjust ProviderSendsDoneMarker() (or related logic) to detect providers that may not emit DONE and avoid relying on DONE as the sole end signal. Add tests to cover both DONE-emitting and non-DONE backends, with clear documentation in code comments explaining the rationale and any fallback behavior.
Applied to files:
core/providers/anthropic/responses.gocore/providers/anthropic/types.gocore/providers/gemini/responses.go
📚 Learning: 2026-01-10T11:27:47.535Z
Learnt from: Radheshg04
Repo: maximhq/bifrost PR: 1256
File: core/providers/openai/openai.go:2276-2385
Timestamp: 2026-01-10T11:27:47.535Z
Learning: Validate image generation requests for nil and missing prompts before dispatch. Follow the same pattern used here: core/bifrost.go validates nil/empty prompts, providerUtils.CheckContextAndGetRequestBody returns a structured error when the request converter yields nil, and apply this across all providers (including OpenAI) to avoid sending null bodies.
Applied to files:
core/providers/anthropic/responses.gocore/providers/anthropic/types.gocore/providers/gemini/responses.go
📚 Learning: 2026-01-11T14:08:10.341Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1298
File: core/providers/anthropic/anthropic.go:682-699
Timestamp: 2026-01-11T14:08:10.341Z
Learning: In Anthroplic streaming implementations (and analogous providers), ensure that the final 'summary' chunk, which carries usage information and metadata, is emitted after all delta chunks and uses a chunk index of last_delta_index + 1. This differentiates the summary chunk from content delta chunks. Apply this convention consistently in the anthropic provider code and in similar streaming providers, and consider adding a targeted test to assert the ordering and chunk index logic.
Applied to files:
core/providers/anthropic/responses.gocore/providers/anthropic/types.go
📚 Learning: 2026-01-13T13:36:35.221Z
Learnt from: TejasGhatte
Repo: maximhq/bifrost PR: 1319
File: core/providers/anthropic/responses.go:937-937
Timestamp: 2026-01-13T13:36:35.221Z
Learning: In core/providers/anthropic/responses.go, when handling Anthropic API streaming responses, ensure that content_block_start events include a signature field set to an empty string (e.g., contentBlock.Signature = ""). The actual signature is delivered later via signature_delta events. This behavior is per Anthropic's specification and should not be treated as an error. This guideline should apply to all Anthropic response handling files under core/providers/anthropic/ and similar go files that process streaming blocks.
Applied to files:
core/providers/anthropic/responses.gocore/providers/anthropic/types.go
📚 Learning: 2025-12-17T08:44:08.788Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1114
File: core/schemas/chatcompletions.go:224-228
Timestamp: 2025-12-17T08:44:08.788Z
Learning: In core/schemas/chatcompletions.go, ensure the schema structures mirror OpenAI's API specifications exactly. Use the valid values for fields like ChatAudioParameters.Format and ChatAudioParameters.Voice as defined by OpenAI's documentation, and avoid adding additional inline documentation or constants to maintain direct compatibility with OpenAI's API.
Applied to files:
core/schemas/chatcompletions.go
🧬 Code graph analysis (2)
core/providers/anthropic/types.go (2)
core/schemas/chatcompletions.go (1)
CacheControl(705-708)core/schemas/responses.go (1)
Citations(421-423)
core/providers/gemini/responses.go (2)
core/schemas/responses.go (3)
ResponsesToolMessage(475-495)ResponsesMessageTypeReasoning(312-312)ResponsesReasoning(745-748)core/providers/gemini/types.go (4)
Part(1027-1051)FunctionCall(1233-1243)Type(822-822)Content(1013-1021)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Graphite / mergeability_check
🔇 Additional comments (25)
core/providers/gemini/responses.go (6)
249-289: Thought signature extraction and look-ahead logic is well-structured.The implementation correctly:
- Extracts signatures from CallID using the separator
- Falls back to look-ahead for reasoning messages with encrypted content
- Tracks consumed indices to avoid duplicate processing
One consideration: the look-ahead at line 277 only checks
i+1, but if there are multiple function calls in sequence, each would only look at its immediate next message. This appears intentional for matching function calls with their associated reasoning.
460-480: Streaming path correctly mirrors the batch path for thought signature extraction.The signature extraction logic is consistent with the non-streaming path, using the same separator and base64 decoding approach.
1060-1085: Thought signature attachment to function call IDs is correctly implemented.The logic properly encodes the signature and appends it to the tool use ID using the defined separator. The augmented ID is then propagated to both the
addedEventanddoneEvent.
1086-1102: LGTM for done event emission with augmented tool ID.The stream response correctly includes the augmented
toolUseIDin both theItemIDand nestedCallIDfields, ensuring consistency for downstream consumers.
1869-1873: Batch response path correctly attaches thought signature to callID.This mirrors the streaming path logic, ensuring consistency between batch and streaming responses when converting Gemini candidates to Responses output.
2446-2446: Full CallID with thought signature is intentionally preserved for FunctionResponse.ID.At line 2446, the assignment of the full
CallID(including the_ts_separator and base64 signature) toFunctionResponse.IDis deliberate, as documented by the explicit comment at line 2391:// Keep the full CallID as-is (don't strip thought signature). The thought signature is extracted separately (lines 2386-2388) for downstream uses, while the complete ID is propagated throughFunctionResponse.IDconsistently. The Gemini API should handle this augmented ID format as designed.core/schemas/chatcompletions.go (1)
312-365: Well-implemented union type forAdditionalPropertiesStruct.The implementation correctly:
- Validates mutual exclusivity in MarshalJSON (line 322-324)
- Handles null input in UnmarshalJSON (lines 343-347)
- Tries boolean first, then map, which matches JSON schema semantics
- Returns a clear error message when neither type matches (line 364)
One minor note: the empty line at 320 before the validation is unnecessary but doesn't affect functionality.
core/schemas/responses.go (1)
421-423: NewCitationstype added for citation control.This is a clean, minimal struct following the existing patterns in this file. The
omitemptytag ensures the field is excluded from JSON when nil.Based on the PR summary, this type supports the enhanced citations structure for Anthropic provider compatibility.
core/providers/anthropic/responses.go (13)
898-902: LGTM!The OutputIndex-first indexing with ContentIndex fallback is correctly implemented for computer tool handling, consistent with the PR objective to prioritize OutputIndex for global Anthropic indexing.
919-924: LGTM!Consistent OutputIndex-first indexing pattern for text and other content blocks.
937-937: LGTM!Correctly initializes the signature field to an empty string for thinking blocks in content_block_start events, per Anthropic's specification. The actual signature will be delivered via signature_delta events. Based on learnings, this is the expected behavior.
953-953: LGTM!Consistent empty signature initialization for thinking blocks that were misclassified as function calls.
983-983: LGTM!Consistent empty signature initialization for the first-block reasoning case.
1056-1062: LGTM!OutputIndex-first indexing correctly applied to OutputTextDelta with helpful inline comment.
1072-1077: LGTM!Consistent OutputIndex-first indexing for FunctionCallArgumentsDelta.
1093-1098: LGTM!Consistent OutputIndex-first indexing for ReasoningSummaryTextDelta.
1127-1132: LGTM!Consistent OutputIndex-first indexing for computer tool OutputItemDone events.
1155-1160: LGTM!Consistent OutputIndex-first indexing for content_block_stop events.
1197-1202: LGTM!Consistent OutputIndex-first indexing for MCPCallArgumentsDelta.
1219-1224: LGTM!Consistent OutputIndex-first indexing for MCPCallCompleted events.
1169-1192: VerifyStopSequencefield handling consistency.The default case initializes
StopSequenceto an empty string (Line 1171), but whenStopReasonis explicitly provided from the response (Line 1189),StopSequenceis set tonil. Since the type definition forAnthropicStreamDelta.StopSequencehas noomitemptytag, these will serialize differently in JSON (empty string vs. null).Without access to Anthropic API specifications or test cases defining the expected behavior, it's unclear if this inconsistency is intentional. If both cases should produce the same output format, consider standardizing to use
schemas.Ptr("")consistently.♻️ Suggested fix for consistency
if bifrostResp.Response.StopReason != nil { anthropicContentDeltaEvent.Delta = &AnthropicStreamDelta{ StopReason: schemas.Ptr(ConvertBifrostFinishReasonToAnthropic(*bifrostResp.Response.StopReason)), - StopSequence: nil, + StopSequence: schemas.Ptr(""), } }core/providers/anthropic/types.go (4)
219-238: LGTM!The new fields (
Signature,URL,EncryptedContent,PageAge) are correctly added toAnthropicContentBlockwith appropriate json tags and omitempty. These align with the PR objectives for thought signature support and enhanced citation handling.
248-256: LGTM!The
AnthropicCitationTypeenum covers all citation location types supported by the Anthropic API.
258-291: LGTM!Well-structured union type for
AnthropicTextCitationthat cleanly handles multiple citation formats with helpful comments indicating field applicability.
293-334: LGTM!The
AnthropicCitationsunion type with custom marshal/unmarshal logic correctly handles both request format (config object) and response format (citation array). The mutual exclusivity validation inMarshalJSONand the order of attempts inUnmarshalJSON(array first, then object) are appropriate for the expected usage patterns.
903a34e to
a994828
Compare
1cb5e03 to
5888ee9
Compare
a994828 to
8a99f70
Compare
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: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (7)
core/providers/gemini/chat.go (1)
360-368: Streaming path doesn't associate ThoughtSignature with tool call ID.In the non-streaming path (lines 186-198), when handling
ThoughtSignaturefor aFunctionCall, the code setsreasoningDetail.ID = schemas.Ptr(fmt.Sprintf("tool_call_%s", callID)).In the streaming path here,
ThoughtSignaturehandling doesn't check if the part has aFunctionCalland doesn't set the corresponding ID. This inconsistency could cause issues downstream when correlating signatures with tool calls.🐛 Suggested fix to match non-streaming behavior
// Handle thought signature separately (not part of the switch since it can co-exist with other types) if part.ThoughtSignature != nil { thoughtSig := base64.StdEncoding.EncodeToString(part.ThoughtSignature) - reasoningDetails = append(reasoningDetails, schemas.ChatReasoningDetails{ + reasoningDetail := schemas.ChatReasoningDetails{ Index: len(reasoningDetails), Type: schemas.BifrostReasoningDetailsTypeEncrypted, Signature: &thoughtSig, - }) + } + + // Associate with tool call if present + if part.FunctionCall != nil { + callID := part.FunctionCall.Name + if part.FunctionCall.ID != "" { + callID = part.FunctionCall.ID + } + if strings.Contains(callID, thoughtSignatureSeparator) { + parts := strings.SplitN(callID, thoughtSignatureSeparator, 2) + if len(parts) == 2 { + callID = parts[0] + } + } + reasoningDetail.ID = schemas.Ptr(fmt.Sprintf("tool_call_%s", callID)) + } + + reasoningDetails = append(reasoningDetails, reasoningDetail) }core/providers/gemini/responses.go (4)
246-287: Avoid double-encoding and unintended overwrites when derivingThoughtSignaturefrom CallID.
Right now you (a) keepfunctionCall.ID = callID(possibly already containing the signature) and (b) may later append the signature again in other conversion paths. Also, the lookahead reasoning message overwritesThoughtSignatureeven if you successfully extracted one from the CallID.Proposed fix (guard overwrites + avoid double-append patterns)
- // Use thought signature from CallID if we extracted one - if len(thoughtSignature) > 0 { + // Use thought signature from CallID if we extracted one + if len(thoughtSignature) > 0 { part.ThoughtSignature = thoughtSignature } else { // Otherwise, look ahead to see if the next message is a reasoning message with encrypted content // (thought signature for this function call) if i+1 < len(bifrostResp.Output) { nextMsg := bifrostResp.Output[i+1] if nextMsg.Type != nil && *nextMsg.Type == schemas.ResponsesMessageTypeReasoning && nextMsg.ResponsesReasoning != nil && nextMsg.ResponsesReasoning.EncryptedContent != nil { decodedSig, err := base64.StdEncoding.DecodeString(*nextMsg.ResponsesReasoning.EncryptedContent) if err == nil { part.ThoughtSignature = decodedSig // Mark this reasoning message as consumed consumedIndices[i+1] = true } } } }- // Preserve thought signature from ResponsesReasoning message (required for Gemini 3 Pro) - // Look ahead to see if the next message is a reasoning message with encrypted content - if i+1 < len(messages) { + // Preserve thought signature from ResponsesReasoning message (required for Gemini 3 Pro) + // Only if we don't already have one (e.g., extracted from CallID). + if len(part.ThoughtSignature) == 0 && i+1 < len(messages) { nextMsg := messages[i+1] if nextMsg.Type != nil && *nextMsg.Type == schemas.ResponsesMessageTypeReasoning && nextMsg.ResponsesReasoning != nil && nextMsg.ResponsesReasoning.EncryptedContent != nil { decodedSig, err := base64.StdEncoding.DecodeString(*nextMsg.ResponsesReasoning.EncryptedContent) if err == nil { part.ThoughtSignature = decodedSig } } }-->
493-499: BUG:OutputItemAddedreasoning signature path likely doesn’t compile and mishandles base64.
bifrostResp.Item.EncryptedContentdoesn’t match the schema shape shown elsewhere (encrypted content lives underbifrostResp.Item.ResponsesReasoning.EncryptedContent).- Even if it compiled,
[]byte(base64String)is not decoded bytes.Proposed fix
case schemas.ResponsesStreamResponseTypeOutputItemAdded: - if bifrostResp.Item != nil && bifrostResp.Item.ResponsesReasoning != nil && bifrostResp.Item.EncryptedContent != nil { - candidate.Content.Parts = append(candidate.Content.Parts, &Part{ - ThoughtSignature: []byte(*bifrostResp.Item.ResponsesReasoning.EncryptedContent), - }) - } + if bifrostResp.Item != nil && bifrostResp.Item.ResponsesReasoning != nil && bifrostResp.Item.ResponsesReasoning.EncryptedContent != nil { + if decodedSig, err := base64.StdEncoding.DecodeString(*bifrostResp.Item.ResponsesReasoning.EncryptedContent); err == nil { + candidate.Content.Parts = append(candidate.Content.Parts, &Part{ + ThoughtSignature: decodedSig, + }) + } + }-->
1057-1100: Streaming state inconsistency:state.ItemIDs/ToolCallIDsstore pre-signature ID, but events use post-signature ID.
This can cause later close/finalization (output_item.done) to reference a different ID thanoutput_item.added/arguments.done.Proposed fix (compute final ID first, then store in state)
- toolUseID := part.FunctionCall.ID + toolUseID := part.FunctionCall.ID if toolUseID == "" { toolUseID = part.FunctionCall.Name // Fallback to name as ID } - state.ItemIDs[outputIndex] = toolUseID - state.ToolCallIDs[outputIndex] = toolUseID - state.ToolCallNames[outputIndex] = part.FunctionCall.Name - // Convert args to JSON string argsJSON := "" if part.FunctionCall.Args != nil { if argsBytes, err := sonic.Marshal(part.FunctionCall.Args); err == nil { argsJSON = string(argsBytes) } } state.ToolArgumentBuffers[outputIndex] = argsJSON // Attach thought signature to ID if present if len(part.ThoughtSignature) > 0 { encoded := base64.StdEncoding.EncodeToString(part.ThoughtSignature) - toolUseID = fmt.Sprintf("%s%s%s", toolUseID, thoughtSignatureSeparator, encoded) + // Avoid double-append if caller already embedded it. + if !strings.Contains(toolUseID, thoughtSignatureSeparator) { + toolUseID = fmt.Sprintf("%s%s%s", toolUseID, thoughtSignatureSeparator, encoded) + } } + + // Now that toolUseID is finalized, store it in state + state.ItemIDs[outputIndex] = toolUseID + state.ToolCallIDs[outputIndex] = toolUseID + state.ToolCallNames[outputIndex] = part.FunctionCall.Name-->
2377-2414: Don’t overwrite decoded signature from CallID with lookahead unless required.
The lookahead currently clobberspart.ThoughtSignatureeven whenthoughtSigdecoded successfully, which can make the ID/signature pair inconsistent.(See earlier diff suggestion to gate the lookahead with
len(part.ThoughtSignature) == 0.)-->
core/providers/anthropic/responses.go (1)
1043-1047: Synthetic input_json deltas likely use the wrongindexafter OutputIndex-first migration.
You now setstreamResp.IndexfromOutputIndexfirst, but synthetic deltas still usebifrostResp.ContentIndex, which can desync delta events from the block they belong to.Proposed fix (pass the same index you used for content_block_start)
- if shouldGenerateDeltas && argumentsJSON != "" { + if shouldGenerateDeltas && argumentsJSON != "" { // Generate synthetic input_json_delta events by chunking the JSON - deltaEvents := generateSyntheticInputJSONDeltas(argumentsJSON, bifrostResp.ContentIndex) + deltaEvents := generateSyntheticInputJSONDeltas(argumentsJSON, streamResp.Index) events = append(events, deltaEvents...) }-->
Also applies to: 3844-3877
tests/integrations/python/tests/test_google.py (1)
38-49: Header docstring test case numbering is inconsistent with actual test methods.The header lists test cases 28-40, but after renaming
test_33_file_deletetotest_34_file_delete, the numbering no longer aligns. Additionally, there's now a duplicate test_34 (both file_delete and batch_create_with_file). Consider updating the header to reflect the actual test case structure.
🤖 Fix all issues with AI agents
In @core/providers/gemini/responses.go:
- Around line 1866-1871: The code appends a base64 thought signature to callID
unconditionally, which can produce duplicate signatures like "..._ts_sig_ts_sig"
if callID already contains thoughtSignatureSeparator; change the logic in the
block that references callID, part.ThoughtSignature and
thoughtSignatureSeparator to only append the signature when
part.ThoughtSignature is non-empty AND callID does not already contain
thoughtSignatureSeparator (e.g., use strings.Contains(callID,
thoughtSignatureSeparator) to guard the append), and ensure strings is imported
if needed.
- Around line 457-477: The code decodes thoughtSig with base64.StdEncoding which
fails for URL-safe variants; update the logic around thoughtSig (extracted from
bifrostResp.Item.ResponsesToolMessage.CallID using thoughtSignatureSeparator) to
normalize URL-safe base64 before decoding: replace '-' with '+' and '_' with '/'
(or alternatively use base64.URLEncoding), add '=' padding so len%4==0, then
call DecodeString and set functionCallPart.ThoughtSignature on success (same
target: the block that currently calls base64.StdEncoding.DecodeString and
assigns functionCallPart.ThoughtSignature).
In @tests/integrations/python/tests/test_google.py:
- Around line 2118-2131: The test assigns response_3 from
client.models.generate_content (using conversation_without_sig and
calculator_tool) but never uses it, and the two print statements use f-strings
without placeholders; either use response_3 in an assertion/log or remove the
variable assignment to avoid unused variable warnings, and convert the print
calls to regular strings (e.g., remove the leading f) or include actual
placeholders like {response_3} if you intend to log the response; update the
code around the response_3 assignment and the two print lines accordingly.
- Around line 2012-2013: The test method name
test_30e_gemini_3_signature_validation_strict and its docstring disagree
(docstring says "Test Case 30f"); fix by making them consistent: either rename
the method to test_30f_gemini_3_signature_validation_strict if the test is
actually case 30f, or update the docstring to "Test Case 30e: Gemini 3 - Strict
validation of thought signatures in function calling" to match the existing
method name; update any references in test suite metadata if you choose to
rename the method.
- Around line 2291-2293: The test function name test_34_file_delete collides
with test_34_batch_create_with_file; revert the rename by renaming
test_34_file_delete back to test_33_file_delete (or alternatively renumber the
subsequent batch tests test_34_batch_create_with_file...test_39_* to start at
35) so each test function has a unique identifier; locate the function
definition for test_34_file_delete and update its name (and any references) to
test_33_file_delete, or update the names of test_34_batch_create_with_file and
following batch test functions to increment by one.
🧹 Nitpick comments (10)
core/changelog.md (1)
6-6: Improve entry clarity.The phrase "through integration" is vague. Based on the PR summary, the Anthropic changes include:
- Prioritizing OutputIndex over ContentIndex for consistent global indexing
- Adding missing signature fields to thinking content blocks
- Adding proper stop reason handling in message_stop events
Consider making this entry more specific about which streaming aspect was fixed, for example:
fix: anthropic streaming events index handling and signature fieldsfix: anthropic streaming events metadata and stop reason handlingcore/providers/anthropic/types.go (1)
318-336: Consider reversing unmarshal order for clarity.The
UnmarshalJSONtries to unmarshal as an array of citations first (line 321), then as a config object (line 328). While this works because arrays and objects are syntactically distinct in JSON, the order is counterintuitive given the comment on lines 294-295 that describes request format (config) first, then response format (array).This is a minor readability concern and not a functional issue since JSON parsing distinguishes between arrays and objects unambiguously.
core/providers/gemini/utils.go (1)
1145-1155: Consider usingelse iffor mutually exclusive branches.The two conditions are mutually exclusive (bool vs map extraction), but using separate
ifstatements obscures this intent. Anelse ifwould make the mutual exclusivity explicit.♻️ Suggested refactor
// Extract additionalProperties if additionalProps, ok := normalizedSchemaMap["additionalProperties"].(bool); ok { jsonSchema.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ AdditionalPropertiesBool: &additionalProps, } - } - - if additionalProps, ok := schemas.SafeExtractOrderedMap(normalizedSchemaMap["additionalProperties"]); ok { + } else if additionalProps, ok := schemas.SafeExtractOrderedMap(normalizedSchemaMap["additionalProperties"]); ok { jsonSchema.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ AdditionalPropertiesMap: &additionalProps, } }core/providers/cohere/utils.go (1)
76-86: Consider usingelse iffor mutually exclusive branches.Same pattern as in gemini/utils.go - the bool and map extraction are mutually exclusive, so
else ifwould make the intent clearer.♻️ Suggested refactor
// Extract additionalProperties if addPropsVal, ok := paramsMap["additionalProperties"].(bool); ok { result.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ AdditionalPropertiesBool: &addPropsVal, } - } - - if addPropsVal, ok := schemas.SafeExtractOrderedMap(paramsMap["additionalProperties"]); ok { + } else if addPropsVal, ok := schemas.SafeExtractOrderedMap(paramsMap["additionalProperties"]); ok { result.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ AdditionalPropertiesMap: &addPropsVal, } }core/providers/anthropic/utils.go (1)
768-778: Consider usingelse iffor mutually exclusive branches.Consistent with the pattern in gemini/utils.go and cohere/utils.go - the bool and map extraction are mutually exclusive.
♻️ Suggested refactor
if additionalProps, ok := schemaMap["additionalProperties"].(bool); ok { jsonSchema.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ AdditionalPropertiesBool: &additionalProps, } - } - - if additionalProps, ok := schemas.SafeExtractOrderedMap(schemaMap["additionalProperties"]); ok { + } else if additionalProps, ok := schemas.SafeExtractOrderedMap(schemaMap["additionalProperties"]); ok { jsonSchema.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ AdditionalPropertiesMap: &additionalProps, } }tests/integrations/python/tests/test_google.py (5)
1749-1750: Remove extraneous f-prefix from string literal.Line 1750 has an f-string without any placeholders.
Suggested fix
if hasattr(part, 'thought_signature') and part.thought_signature: signatures.append((idx, len(part.thought_signature))) print(f" → Has signature ({len(part.thought_signature)} bytes)") else: - print(f" → No signature") + print(" → No signature")
1894-1899: Remove extraneous f-prefix from string literals.Lines 1895 and 1898 have f-strings without any placeholders.
Suggested fix
# Should have either a second function call with signature or text response if second_fc: - print(f"\n✓ Sequential function calling: Step 2 has function call with signature") + print("\n✓ Sequential function calling: Step 2 has function call with signature") assert second_signature is not None, "Second function call should also have thought signature" else: - print(f"\n✓ Model provided text response after first function call") + print("\n✓ Model provided text response after first function call") assert has_text, "Should have text response"
1941-1955: Remove extraneous f-prefix from string literals.Lines 1942 and 1954 have f-strings without any placeholders.
Suggested fix
- print(f"\n Summary:") + print("\n Summary:") print(f" Thought parts: {thought_parts}") print(f" Text parts: {text_parts}") print(f" Signatures on parts: {signatures_found}") # According to docs: signature should be in the last part (when thinking is enabled) if len(signatures_found) > 0: last_part_idx = len(parts) - 1 # Signature might be on the last part or on a thought part print(f" Last part index: {last_part_idx}") print(f"✓ Found {len(signatures_found)} thought signature(s) in text response") else: - print(f" Note: No explicit thought signatures found (may be internal)") + print(" Note: No explicit thought signatures found (may be internal)")
1999-2008: Remove extraneous f-prefix from string literals.Lines 2001 and 2007 have f-strings without any placeholders.
Suggested fix
# Minimal should have very few or no thinking tokens if level == "minimal": - print(f" ✓ Minimal thinking level (expected: minimal/no thinking)") + print(" ✓ Minimal thinking level (expected: minimal/no thinking)") elif level == "high": # High should have substantial thinking if thought_tokens > 0: print(f" ✓ High thinking level with {thought_tokens} thought tokens") else: - print(f" ! High thinking but no thought tokens reported") + print(" ! High thinking but no thought tokens reported")
2092-2093: Remove extraneous f-prefix from string literal.Suggested fix
assert response_2.candidates, "Request with signature should succeed" - print(f" ✓ Request WITH signature succeeded") + print(" ✓ Request WITH signature succeeded")
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (16)
core/changelog.mdcore/internal/testutil/structured_outputs.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.gocore/providers/anthropic/utils.gocore/providers/cohere/utils.gocore/providers/gemini/chat.gocore/providers/gemini/gemini_test.gocore/providers/gemini/responses.gocore/providers/gemini/types.gocore/providers/gemini/utils.gocore/schemas/chatcompletions.gocore/schemas/responses.gotests/integrations/python/tests/test_google.pytransports/bifrost-http/integrations/router.gotransports/changelog.md
💤 Files with no reviewable changes (1)
- transports/bifrost-http/integrations/router.go
🧰 Additional context used
📓 Path-based instructions (1)
**
⚙️ CodeRabbit configuration file
always check the stack if there is one for the current PR. do not give localized reviews for the PR, always see all changes in the light of the whole stack of PRs (if there is a stack, if there is no stack you can continue to make localized suggestions/reviews)
Files:
core/providers/gemini/types.gocore/providers/gemini/utils.gocore/providers/gemini/chat.gocore/providers/gemini/responses.gocore/providers/anthropic/utils.gocore/providers/gemini/gemini_test.gotransports/changelog.mdcore/providers/anthropic/types.gocore/schemas/responses.gocore/providers/cohere/utils.gocore/providers/anthropic/responses.gocore/internal/testutil/structured_outputs.gocore/changelog.mdcore/schemas/chatcompletions.gotests/integrations/python/tests/test_google.py
🧠 Learnings (9)
📚 Learning: 2025-12-09T17:07:42.007Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/schemas/account.go:9-18
Timestamp: 2025-12-09T17:07:42.007Z
Learning: In core/schemas/account.go, the HuggingFaceKeyConfig field within the Key struct is currently unused and reserved for future Hugging Face inference endpoint deployments. Do not flag this field as missing from OpenAPI documentation or require its presence in the API spec until the feature is actively implemented and used. When the feature is added, update the OpenAPI docs accordingly; otherwise, treat this field as non-breaking and not part of the current API surface.
Applied to files:
core/providers/gemini/types.gocore/providers/gemini/utils.gocore/providers/gemini/chat.gocore/providers/gemini/responses.gocore/providers/anthropic/utils.gocore/providers/gemini/gemini_test.gocore/providers/anthropic/types.gocore/schemas/responses.gocore/providers/cohere/utils.gocore/providers/anthropic/responses.gocore/internal/testutil/structured_outputs.gocore/schemas/chatcompletions.go
📚 Learning: 2025-12-29T11:54:55.836Z
Learnt from: akshaydeo
Repo: maximhq/bifrost PR: 1153
File: framework/configstore/rdb.go:2221-2246
Timestamp: 2025-12-29T11:54:55.836Z
Learning: In Go reviews, do not flag range-over-int patterns like for i := range n as compile-time errors, assuming Go 1.22+ semantics. Only flag actual range-capable values (slices, arrays, maps, channels, strings) and other compile-time issues. This applies to all Go files across the repository.
Applied to files:
core/providers/gemini/types.gocore/providers/gemini/utils.gocore/providers/gemini/chat.gocore/providers/gemini/responses.gocore/providers/anthropic/utils.gocore/providers/gemini/gemini_test.gocore/providers/anthropic/types.gocore/schemas/responses.gocore/providers/cohere/utils.gocore/providers/anthropic/responses.gocore/internal/testutil/structured_outputs.gocore/schemas/chatcompletions.go
📚 Learning: 2025-12-19T09:26:54.961Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/providers/utils/utils.go:1050-1051
Timestamp: 2025-12-19T09:26:54.961Z
Learning: Update streaming end-marker handling so HuggingFace is treated as a non-[DONE] provider for backends that do not emit a DONE marker (e.g., meta llama on novita). In core/providers/utils/utils.go, adjust ProviderSendsDoneMarker() (or related logic) to detect providers that may not emit DONE and avoid relying on DONE as the sole end signal. Add tests to cover both DONE-emitting and non-DONE backends, with clear documentation in code comments explaining the rationale and any fallback behavior.
Applied to files:
core/providers/gemini/types.gocore/providers/gemini/utils.gocore/providers/gemini/chat.gocore/providers/gemini/responses.gocore/providers/anthropic/utils.gocore/providers/gemini/gemini_test.gocore/providers/anthropic/types.gocore/providers/cohere/utils.gocore/providers/anthropic/responses.go
📚 Learning: 2026-01-10T11:27:47.535Z
Learnt from: Radheshg04
Repo: maximhq/bifrost PR: 1256
File: core/providers/openai/openai.go:2276-2385
Timestamp: 2026-01-10T11:27:47.535Z
Learning: Validate image generation requests for nil and missing prompts before dispatch. Follow the same pattern used here: core/bifrost.go validates nil/empty prompts, providerUtils.CheckContextAndGetRequestBody returns a structured error when the request converter yields nil, and apply this across all providers (including OpenAI) to avoid sending null bodies.
Applied to files:
core/providers/gemini/types.gocore/providers/gemini/utils.gocore/providers/gemini/chat.gocore/providers/gemini/responses.gocore/providers/anthropic/utils.gocore/providers/gemini/gemini_test.gocore/providers/anthropic/types.gocore/providers/cohere/utils.gocore/providers/anthropic/responses.go
📚 Learning: 2026-01-11T14:08:10.341Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1298
File: core/providers/anthropic/anthropic.go:682-699
Timestamp: 2026-01-11T14:08:10.341Z
Learning: In Anthroplic streaming implementations (and analogous providers), ensure that the final 'summary' chunk, which carries usage information and metadata, is emitted after all delta chunks and uses a chunk index of last_delta_index + 1. This differentiates the summary chunk from content delta chunks. Apply this convention consistently in the anthropic provider code and in similar streaming providers, and consider adding a targeted test to assert the ordering and chunk index logic.
Applied to files:
core/providers/anthropic/utils.gocore/providers/anthropic/types.gocore/providers/anthropic/responses.go
📚 Learning: 2026-01-13T13:36:35.221Z
Learnt from: TejasGhatte
Repo: maximhq/bifrost PR: 1319
File: core/providers/anthropic/responses.go:937-937
Timestamp: 2026-01-13T13:36:35.221Z
Learning: In core/providers/anthropic/responses.go, when handling Anthropic API streaming responses, ensure that content_block_start events include a signature field set to an empty string (e.g., contentBlock.Signature = ""). The actual signature is delivered later via signature_delta events. This behavior is per Anthropic's specification and should not be treated as an error. This guideline should apply to all Anthropic response handling files under core/providers/anthropic/ and similar go files that process streaming blocks.
Applied to files:
core/providers/anthropic/utils.gocore/providers/anthropic/types.gocore/providers/anthropic/responses.go
📚 Learning: 2025-12-15T10:16:21.909Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/providers/huggingface/huggingface_test.go:12-63
Timestamp: 2025-12-15T10:16:21.909Z
Learning: In provider tests under core/providers/<provider>/*_test.go, do not require or flag the use of defer for Shutdown(); instead call client.Shutdown() at the end of each test function. This pattern appears consistent across all provider tests. Apply this rule only within this path; for other tests or resources, defer may still be appropriate.
Applied to files:
core/providers/gemini/gemini_test.go
📚 Learning: 2025-12-19T08:29:20.286Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1095
File: core/internal/testutil/count_tokens.go:30-67
Timestamp: 2025-12-19T08:29:20.286Z
Learning: In core/internal/testutil test files, enforce using GetTestRetryConfigForScenario() to obtain a generic retry config, then construct a typed retry config (e.g., CountTokensRetryConfig, EmbeddingRetryConfig, TranscriptionRetryConfig) with an empty Conditions slice. Copy only MaxAttempts, BaseDelay, MaxDelay, OnRetry, and OnFinalFail from the generic config. This convention should be consistently applied across all test files in this directory.
Applied to files:
core/internal/testutil/structured_outputs.go
📚 Learning: 2025-12-17T08:44:08.788Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1114
File: core/schemas/chatcompletions.go:224-228
Timestamp: 2025-12-17T08:44:08.788Z
Learning: In core/schemas/chatcompletions.go, ensure the schema structures mirror OpenAI's API specifications exactly. Use the valid values for fields like ChatAudioParameters.Format and ChatAudioParameters.Voice as defined by OpenAI's documentation, and avoid adding additional inline documentation or constants to maintain direct compatibility with OpenAI's API.
Applied to files:
core/schemas/chatcompletions.go
🧬 Code graph analysis (10)
core/providers/gemini/chat.go (3)
core/schemas/chatcompletions.go (3)
ChatReasoningDetails(857-865)BifrostReasoningDetailsTypeText(853-853)BifrostReasoningDetailsTypeEncrypted(852-852)core/schemas/utils.go (1)
Ptr(14-16)ui/lib/types/logs.ts (1)
ReasoningDetails(127-134)
core/providers/gemini/responses.go (2)
core/schemas/responses.go (3)
ResponsesToolMessage(475-495)ResponsesMessageTypeReasoning(312-312)ResponsesReasoning(745-748)core/providers/gemini/types.go (4)
Part(1030-1054)FunctionCall(1236-1246)Type(825-825)Content(1016-1024)
core/providers/anthropic/utils.go (2)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(312-315)core/schemas/utils.go (1)
SafeExtractOrderedMap(549-572)
core/providers/gemini/gemini_test.go (2)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(312-315)core/schemas/utils.go (1)
Ptr(14-16)
core/providers/anthropic/types.go (2)
core/schemas/chatcompletions.go (1)
CacheControl(707-710)core/schemas/responses.go (1)
Citations(421-423)
core/schemas/responses.go (1)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(312-315)
core/providers/cohere/utils.go (2)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(312-315)core/schemas/utils.go (1)
SafeExtractOrderedMap(549-572)
core/internal/testutil/structured_outputs.go (2)
core/providers/gemini/types.go (1)
Type(825-825)core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(312-315)
core/schemas/chatcompletions.go (2)
core/schemas/json_native.go (2)
Marshal(8-10)Unmarshal(18-20)core/schemas/json_wasm.go (2)
Marshal(8-10)Unmarshal(22-24)
tests/integrations/python/tests/test_google.py (2)
tests/integrations/python/tests/utils/common.py (1)
skip_if_no_api_key(1830-1841)core/providers/gemini/types.go (4)
Tool(647-674)Content(1016-1024)Part(1030-1054)FunctionResponse(1249-1271)
🪛 Ruff (0.14.10)
tests/integrations/python/tests/test_google.py
1682-1682: Unused method argument: test_config
(ARG002)
1750-1750: f-string without any placeholders
Remove extraneous f prefix
(F541)
1768-1768: Unused method argument: test_config
(ARG002)
1895-1895: f-string without any placeholders
Remove extraneous f prefix
(F541)
1898-1898: f-string without any placeholders
Remove extraneous f prefix
(F541)
1902-1902: Unused method argument: test_config
(ARG002)
1942-1942: f-string without any placeholders
Remove extraneous f prefix
(F541)
1954-1954: f-string without any placeholders
Remove extraneous f prefix
(F541)
1957-1957: Unused method argument: test_config
(ARG002)
2001-2001: f-string without any placeholders
Remove extraneous f prefix
(F541)
2007-2007: f-string without any placeholders
Remove extraneous f prefix
(F541)
2012-2012: Unused method argument: test_config
(ARG002)
2093-2093: f-string without any placeholders
Remove extraneous f prefix
(F541)
2095-2095: Do not catch blind exception: Exception
(BLE001)
2120-2120: Local variable response_3 is assigned to but never used
Remove assignment to unused variable response_3
(F841)
2129-2129: f-string without any placeholders
Remove extraneous f prefix
(F541)
2130-2130: f-string without any placeholders
Remove extraneous f prefix
(F541)
2132-2132: Do not catch blind exception: Exception
(BLE001)
2292-2292: Unused method argument: test_config
(ARG002)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
🔇 Additional comments (22)
core/changelog.md (2)
1-2: Verify changelog scope: entries may be from other PRs in the stack.Lines 1-2 don't appear to match the scope of PR #1319 as described in the PR summary (which focuses on Anthropic/Gemini/Cohere provider fixes). These entries may belong to other PRs in the Graphite stack (e.g., the target branch name
01-12-feat_send_back_raw_request_with_bifrost_errorssuggests line 2 could be from PR #1313 or the base branch).Please verify:
- Are these entries intentionally included to represent cumulative stack changes?
- Or should each PR maintain separate changelog entries?
3-5: LGTM!These changelog entries accurately reflect the core fixes in this PR for schema AdditionalProperties handling, Gemini thought signatures, and Anthropic citations structure.
core/providers/gemini/types.go (1)
22-23: LGTM!The new
thoughtSignatureSeparatorconstant is clearly documented and provides a consistent separator for embedding thought signatures in tool IDs. The choice of"_ts_"as the separator is reasonable and unlikely to conflict with typical ID formats.transports/changelog.md (1)
1-6: LGTM!The changelog entries accurately summarize the key changes in this PR stack, covering the AdditionalProperties structure, thought signature handling, citations enhancements, and streaming fixes.
core/schemas/chatcompletions.go (2)
312-367: Well-structured dual-mode type for JSON schema compatibility.The
AdditionalPropertiesStructcorrectly models JSON schema'sadditionalPropertieswhich can be either a boolean or an object schema. The marshal/unmarshal implementations properly handle:
- Mutual exclusivity validation in
MarshalJSON- Null handling in
UnmarshalJSON- Type detection by attempting boolean first, then object
276-282: LGTM - Field type enhancement for richer JSON schema support.The change from
*boolto*AdditionalPropertiesStructenables proper modeling of JSON schema'sadditionalProperties, which can be either a boolean or an object schema. This is correctly aligned with OpenAPI/JSON Schema specifications.core/providers/anthropic/types.go (2)
258-291: LGTM - Comprehensive citation model.The
AnthropicTextCitationstruct elegantly handles multiple citation types (char_location, page_location, content_block_location, web_search_result_location, search_result_location) with well-documented optional fields for each type. The design uses optional pointers appropriately for type-specific fields.
219-238: LGTM - Content block expansion for web search support.The
AnthropicContentBlocknow properly supportsweb_search_resultcontent type with URL, EncryptedContent, and PageAge fields, along with the enhanced citations structure. This aligns with Anthropic's API capabilities for web search and document citations.core/internal/testutil/structured_outputs.go (2)
421-426: LGTM - Tests correctly updated for new AdditionalPropertiesStruct.The test properly constructs
AdditionalPropertiesStructwith the boolean value wrapper, aligning with the schema changes incore/schemas/chatcompletions.goandcore/schemas/responses.go.
537-542: LGTM - Consistent update for streaming test.The streaming test follows the same pattern for constructing
AdditionalPropertiesStruct, maintaining consistency with the non-streaming test.core/schemas/responses.go (2)
132-140: LGTM!The
AdditionalPropertiesfield correctly uses the new*AdditionalPropertiesStructtype, aligning with the struct-based representation introduced incore/schemas/chatcompletions.go. The JSON tag is preserved, maintaining API compatibility.
421-424: LGTM!The new
Citationsstruct is straightforward and provides a clean representation for citations configuration. This aligns with the broader citations enhancements mentioned in the PR objectives.core/providers/gemini/gemini_test.go (1)
558-561: LGTM!Test correctly updated to use the new
AdditionalPropertiesStructwrapper, aligning with the schema changes across the codebase.core/providers/gemini/chat.go (3)
94-102: LGTM!Correctly handles thought/reasoning text by adding to
reasoningDetailswithBifrostReasoningDetailsTypeTexttype and skipping the regular content path.
130-160: Potential duplicate reasoning details when both embedded and standalone signatures are present.If a
FunctionCallpart has both an embedded signature inCallIDAND a separateThoughtSignaturefield set, this code will add two reasoning detail entries for the same tool call:
- Lines 152-160: From extracted embedded signature
- Lines 177-201: From standalone
ThoughtSignatureThe comment on line 176 says "standalone thought signature (not embedded in CallID)" but there's no guard to skip if a signature was already extracted from the CallID.
Please verify:
- Can Gemini API send both an embedded signature in CallID AND a separate ThoughtSignature field for the same part?
- If not, consider adding an early continue or flag to prevent duplicate entries:
♻️ Suggested guard
+ // Track if we already processed a signature from CallID + var signatureHandled bool + // Extract thought signature from CallID if embedded (Gemini 3 behavior) var extractedSig []byte if strings.Contains(callID, thoughtSignatureSeparator) { parts := strings.SplitN(callID, thoughtSignatureSeparator, 2) if len(parts) == 2 { if decoded, err := base64.StdEncoding.DecodeString(parts[1]); err == nil { extractedSig = decoded callID = parts[0] // Use base ID without signature for the tool call + signatureHandled = true } } } ... // Handle standalone thought signature (not embedded in CallID) - if part.ThoughtSignature != nil { + if part.ThoughtSignature != nil && !signatureHandled {Also applies to: 176-202
388-388: LGTM!Correctly updated the delta content check to include
ReasoningDetails, ensuring chunks with only reasoning content are properly emitted.core/providers/anthropic/responses.go (3)
898-904: Indexing change looks consistent: OutputIndex-first with ContentIndex fallback.
This aligns with the PR objective (“prioritize OutputIndex over ContentIndex”) and should stabilize global indexing across mixed event types. Based on learnings, the empty signature atcontent_block_startis correct (actual signature arrives viasignature_delta).-->
Also applies to: 919-924
935-984: Good: initializeSignatureto""on thinking/redacted_thinkingcontent_block_start.
This matches the expected Anthropic streaming shape where the signature arrives later viasignature_delta. Based on learnings, this is the right behavior.-->
1166-1192: Remove this suggestion: the code is correct as-is.The code correctly sets
stop_sequencetonil(null in JSON) per Anthropic's streaming API specification. According to Anthropic's SSE contract,stop_sequenceshould benullwhen no custom stop sequence triggered the end of generation—it's only non-null when a specific custom stop string was matched. The proposed fix to keep""(empty string) would violate the spec.The lack of
omitemptyon theStopSequencefield is intentional: the field must be present in the JSON output (either with a string value ornull). The current implementation at line 1189 settingStopSequence: nilis correct.Note: The initial assignment of
schemas.Ptr("")at line 1171 is suboptimal and could be initialized tonildirectly for clarity, but the final value after the conditional check is spec-compliant.Likely an incorrect or invalid review comment.
tests/integrations/python/tests/test_google.py (3)
1681-1766: New Gemini 3 parallel function calls test looks good.The test properly validates thought signature handling for parallel function calls, correctly checking that signatures are attached to function call parts. The assertion that the first function call should have the signature aligns with Gemini documentation.
1767-1900: Sequential function calls test is well-structured.The test demonstrates proper multi-turn conversation handling with thought signature preservation, which is critical for Gemini 3's function calling workflow.
1956-2010: Thinking levels test provides good coverage.Testing all four thinking levels (minimal, low, medium, high) ensures the Bifrost implementation properly handles the
thinking_configparameter variations.
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: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
core/providers/anthropic/responses.go (1)
898-924: Fix syntheticinput_json_deltaindexing to follow OutputIndex-first. Right nowgenerateSyntheticInputJSONDeltas(..., bifrostResp.ContentIndex)can emit deltas under a different index than the associatedcontent_block_start/output_item.addedafter the OutputIndex-first change.Proposed fix (prefer OutputIndex for synthetic deltas)
- deltaEvents := generateSyntheticInputJSONDeltas(argumentsJSON, bifrostResp.ContentIndex) + idx := bifrostResp.OutputIndex + if idx == nil { + idx = bifrostResp.ContentIndex + } + deltaEvents := generateSyntheticInputJSONDeltas(argumentsJSON, idx) events = append(events, deltaEvents...)Also applies to: 1056-1062, 1072-1077, 1093-1114, 1127-1161, 1197-1202, 1013-1047
core/providers/gemini/chat.go (1)
130-160: Stream path should link thought signatures to tool calls (missingReasoningDetails.ID). Non-stream usesID: tool_call_<callID>; streaming currently omits it for both embedded signatures and standaloneThoughtSignature.Proposed fix (match non-stream linking behavior)
toolCalls = append(toolCalls, toolCall) // If we extracted a signature from CallID, add it to reasoning details if len(extractedSig) > 0 { thoughtSig := base64.StdEncoding.EncodeToString(extractedSig) reasoningDetails = append(reasoningDetails, schemas.ChatReasoningDetails{ Index: len(reasoningDetails), Type: schemas.BifrostReasoningDetailsTypeEncrypted, Signature: &thoughtSig, + ID: schemas.Ptr(fmt.Sprintf("tool_call_%s", callID)), }) } ... if part.ThoughtSignature != nil { thoughtSig := base64.StdEncoding.EncodeToString(part.ThoughtSignature) reasoningDetails = append(reasoningDetails, schemas.ChatReasoningDetails{ Index: len(reasoningDetails), Type: schemas.BifrostReasoningDetailsTypeEncrypted, Signature: &thoughtSig, + // If this Part is also a tool call, associate the signature with the tool call ID + // (strip embedded signature suffix if present, same as non-stream path). + // ID: schemas.Ptr(fmt.Sprintf("tool_call_%s", callID)), }) }(Second hunk needs the same “derive callID / strip suffix” logic you already have in non-stream.)
Also applies to: 176-202, 318-351, 360-368
core/providers/anthropic/utils.go (1)
687-692: FixAdditionalPropertieshandling (now a struct, old code dereferences it like*bool).schema["additionalProperties"] = *format.JSONSchema.AdditionalPropertiesis no longer valid and should be split into bool vs map branches.Proposed fix
- if format.JSONSchema.Type != nil && *format.JSONSchema.Type == "object" { - schema["additionalProperties"] = false - } else if format.JSONSchema.AdditionalProperties != nil { - schema["additionalProperties"] = *format.JSONSchema.AdditionalProperties - } + if format.JSONSchema.AdditionalProperties != nil { + ap := format.JSONSchema.AdditionalProperties + if ap.AdditionalPropertiesBool != nil { + schema["additionalProperties"] = *ap.AdditionalPropertiesBool + } else if ap.AdditionalPropertiesMap != nil { + schema["additionalProperties"] = *ap.AdditionalPropertiesMap + } + } else if format.JSONSchema.Type != nil && *format.JSONSchema.Type == "object" { + // Maintain prior default for objects when not specified. + schema["additionalProperties"] = false + }Also applies to: 768-778
🤖 Fix all issues with AI agents
In @tests/integrations/python/tests/test_google.py:
- Around line 2291-2293: Rename the duplicate/misnumbered test definitions so
the sequence is contiguous: change the test function named test_34_file_delete
to test_33_file_delete (so test_33 is present) and ensure the later test remains
test_34_batch_create_with_file; update any references or parametrize decorators
that reference test_34_file_delete to the new test_33_file_delete name (look for
symbols test_34_file_delete and test_34_batch_create_with_file in the file and
rename the first to test_33_file_delete).
🧹 Nitpick comments (9)
core/changelog.md (1)
3-6: Improve changelog specificity and completeness.While these entries align with the PR objectives, consider enhancing clarity:
Line 6 lacks specificity: "anthropic streaming events through integration" is vague. The PR summary mentions multiple specific Anthropic fixes:
- Prioritizing OutputIndex over ContentIndex for consistent global indexing
- Adding missing signature fields to thinking content blocks
- Adding proper stop reason handling in message_stop events
Consider separate entries: Breaking line 6 into specific entries would better document what was fixed and aid future debugging.
Example improvement:
-fix: anthropic streaming events through integration +fix: prioritize OutputIndex over ContentIndex in Anthropic for consistent global indexing +fix: add signature fields to Anthropic thinking content blocks +fix: add stop reason handling in Anthropic message_stop eventscore/providers/cohere/utils.go (1)
76-86: Consider usingelse iffor cleaner control flow.The current implementation works correctly because
SafeExtractOrderedMapreturnsfalsefor boolean values. However, usingelse ifwould make the mutual exclusivity explicit and avoid an unnecessary function call when the boolean branch succeeds.♻️ Suggested improvement
// Extract additionalProperties if addPropsVal, ok := paramsMap["additionalProperties"].(bool); ok { result.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ AdditionalPropertiesBool: &addPropsVal, } - } - - if addPropsVal, ok := schemas.SafeExtractOrderedMap(paramsMap["additionalProperties"]); ok { + } else if addPropsVal, ok := schemas.SafeExtractOrderedMap(paramsMap["additionalProperties"]); ok { result.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ AdditionalPropertiesMap: &addPropsVal, } }core/internal/testutil/structured_outputs.go (1)
406-434: Avoid sharing mutable schema maps across parallel tests (potential data race/flakes). Consider deep-copyingstructuredOutputSchema(esp."properties","required") per test run before passing to request builders.Proposed tweak (defensive copies + Ptr helper)
- props := structuredOutputSchema["properties"].(map[string]interface{}) - additionalProps := structuredOutputSchema["additionalProperties"].(bool) + props := maps.Clone(structuredOutputSchema["properties"].(map[string]interface{})) + additionalProps := structuredOutputSchema["additionalProperties"].(bool) responsesReq := &schemas.BifrostResponsesRequest{ ... Params: &schemas.ResponsesParameters{ Text: &schemas.ResponsesTextConfig{ Format: &schemas.ResponsesTextConfigFormat{ JSONSchema: &schemas.ResponsesTextConfigFormatJSONSchema{ ... - AdditionalProperties: &schemas.AdditionalPropertiesStruct{ - AdditionalPropertiesBool: &additionalProps, - }, + AdditionalProperties: &schemas.AdditionalPropertiesStruct{ + AdditionalPropertiesBool: schemas.Ptr(additionalProps), + }, }, }, }, }, }(You’ll need
import "maps"; if you want to avoid that, we can inline a small helper clone.)Also applies to: 523-549
core/providers/gemini/utils.go (1)
1145-1155: AdditionalProperties bool/map handling looks good; preferschemas.Ptr(...)for the bool.Small clarity tweak
if additionalProps, ok := normalizedSchemaMap["additionalProperties"].(bool); ok { jsonSchema.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ - AdditionalPropertiesBool: &additionalProps, + AdditionalPropertiesBool: schemas.Ptr(additionalProps), } }core/providers/gemini/responses.go (1)
2377-2401: Consider silently ignoring decode errors instead of resettingthoughtSig.The error handling sets
thoughtSig = ""after a decode failure, butpart.ThoughtSignaturewill already benil(empty slice from failed decode), so this assignment is redundant. The logic works correctly, but for clarity, you could simplify by just logging or ignoring the error.Minor simplification
if thoughtSig != "" { - var err error - part.ThoughtSignature, err = base64.StdEncoding.DecodeString(thoughtSig) - if err != nil { - // Silently ignore decode errors - ID will be used without signature - thoughtSig = "" + if decoded, err := base64.StdEncoding.DecodeString(thoughtSig); err == nil { + part.ThoughtSignature = decoded } + // Silently ignore decode errors - ID will be used without signature }tests/integrations/python/tests/test_google.py (4)
1681-1766: Test for parallel function calls with thought signatures is well-structured.The test properly validates that:
- Parallel function calls are made when requested
- At least the first function call has a thought signature
- According to Gemini docs, only the first function call should have the signature
Minor static analysis issues:
- Line 1750: f-string without placeholders - replace
f" → No signature"with" → No signature"test_configparameter is unused (common in pytest fixtures, acceptable)Remove extraneous f-prefix on Line 1750
- print(f" → No signature") + print(" → No signature")
1767-1899: Sequential function calls test validates multi-turn signature preservation.The test properly builds a conversation with the signature preserved on the function call part, which is required for Gemini 3 thinking mode to work correctly across turns.
Minor static analysis issues on lines 1895 and 1898 - f-strings without placeholders:
Remove extraneous f-prefixes
- print(f"\n✓ Sequential function calling: Step 2 has function call with signature") + print("\n✓ Sequential function calling: Step 2 has function call with signature") else: - print(f"\n✓ Model provided text response after first function call") + print("\n✓ Model provided text response after first function call")
2093-2139: Broad exception catching may mask real errors.Lines 2095 and 2132 catch
Exceptionbroadly. While this is intentional for the "informational" step 3 test (testing without signature), the step 2 exception handler at line 2095 should either catch a more specific exception type or log the exception for debugging.Additionally,
response_3on line 2120 is assigned but never used per static analysis.Address unused variable and consider narrowing exception scope
try: conversation_without_sig = [ types.Content(role="user", parts=[types.Part(text="Calculate 45 * 23")]), types.Content( role="model", parts=[types.Part(function_call=fc)] # WITHOUT signature ), types.Content( role="user", parts=[types.Part( function_response=types.FunctionResponse( name=fc.name, response={"result": "1035"} ) )] ) ] - response_3 = client.models.generate_content( + _response_3 = client.models.generate_content( # Prefix with _ to indicate intentionally unused model=model, contents=conversation_without_sig, config=types.GenerateContentConfig( tools=[calculator_tool], thinking_config=types.ThinkingConfig(thinking_level="medium") ) ) - print(f" ! Request WITHOUT signature succeeded (SDK may auto-handle)") - print(f" Note: Gemini 3 docs state this should fail, but SDK might preserve signatures") + print(" ! Request WITHOUT signature succeeded (SDK may auto-handle)") + print(" Note: Gemini 3 docs state this should fail, but SDK might preserve signatures") except Exception as e:
1957-2009: Thinking levels test provides good coverage for Gemini 3 modes.The test iterates through all thinking levels (minimal, low, medium, high) and validates token usage and thought parts. Minor f-string issues exist on lines 2001 and 2007.
Remove extraneous f-prefixes
if level == "minimal": - print(f" ✓ Minimal thinking level (expected: minimal/no thinking)") + print(" ✓ Minimal thinking level (expected: minimal/no thinking)") elif level == "high": # High should have substantial thinking if thought_tokens > 0: print(f" ✓ High thinking level with {thought_tokens} thought tokens") else: - print(f" ! High thinking but no thought tokens reported") + print(" ! High thinking but no thought tokens reported")
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (16)
core/changelog.mdcore/internal/testutil/structured_outputs.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.gocore/providers/anthropic/utils.gocore/providers/cohere/utils.gocore/providers/gemini/chat.gocore/providers/gemini/gemini_test.gocore/providers/gemini/responses.gocore/providers/gemini/types.gocore/providers/gemini/utils.gocore/schemas/chatcompletions.gocore/schemas/responses.gotests/integrations/python/tests/test_google.pytransports/bifrost-http/integrations/router.gotransports/changelog.md
💤 Files with no reviewable changes (1)
- transports/bifrost-http/integrations/router.go
🚧 Files skipped from review as they are similar to previous changes (1)
- core/schemas/responses.go
🧰 Additional context used
📓 Path-based instructions (1)
**
⚙️ CodeRabbit configuration file
always check the stack if there is one for the current PR. do not give localized reviews for the PR, always see all changes in the light of the whole stack of PRs (if there is a stack, if there is no stack you can continue to make localized suggestions/reviews)
Files:
core/providers/gemini/types.gocore/changelog.mdcore/providers/gemini/chat.gocore/internal/testutil/structured_outputs.gocore/providers/anthropic/utils.gocore/providers/anthropic/responses.gocore/providers/gemini/responses.gotests/integrations/python/tests/test_google.pytransports/changelog.mdcore/schemas/chatcompletions.gocore/providers/gemini/utils.gocore/providers/gemini/gemini_test.gocore/providers/cohere/utils.gocore/providers/anthropic/types.go
🧠 Learnings (10)
📚 Learning: 2025-12-09T17:07:42.007Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/schemas/account.go:9-18
Timestamp: 2025-12-09T17:07:42.007Z
Learning: In core/schemas/account.go, the HuggingFaceKeyConfig field within the Key struct is currently unused and reserved for future Hugging Face inference endpoint deployments. Do not flag this field as missing from OpenAPI documentation or require its presence in the API spec until the feature is actively implemented and used. When the feature is added, update the OpenAPI docs accordingly; otherwise, treat this field as non-breaking and not part of the current API surface.
Applied to files:
core/providers/gemini/types.gocore/providers/gemini/chat.gocore/internal/testutil/structured_outputs.gocore/providers/anthropic/utils.gocore/providers/anthropic/responses.gocore/providers/gemini/responses.gocore/schemas/chatcompletions.gocore/providers/gemini/utils.gocore/providers/gemini/gemini_test.gocore/providers/cohere/utils.gocore/providers/anthropic/types.go
📚 Learning: 2025-12-29T11:54:55.836Z
Learnt from: akshaydeo
Repo: maximhq/bifrost PR: 1153
File: framework/configstore/rdb.go:2221-2246
Timestamp: 2025-12-29T11:54:55.836Z
Learning: In Go reviews, do not flag range-over-int patterns like for i := range n as compile-time errors, assuming Go 1.22+ semantics. Only flag actual range-capable values (slices, arrays, maps, channels, strings) and other compile-time issues. This applies to all Go files across the repository.
Applied to files:
core/providers/gemini/types.gocore/providers/gemini/chat.gocore/internal/testutil/structured_outputs.gocore/providers/anthropic/utils.gocore/providers/anthropic/responses.gocore/providers/gemini/responses.gocore/schemas/chatcompletions.gocore/providers/gemini/utils.gocore/providers/gemini/gemini_test.gocore/providers/cohere/utils.gocore/providers/anthropic/types.go
📚 Learning: 2025-12-19T09:26:54.961Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/providers/utils/utils.go:1050-1051
Timestamp: 2025-12-19T09:26:54.961Z
Learning: Update streaming end-marker handling so HuggingFace is treated as a non-[DONE] provider for backends that do not emit a DONE marker (e.g., meta llama on novita). In core/providers/utils/utils.go, adjust ProviderSendsDoneMarker() (or related logic) to detect providers that may not emit DONE and avoid relying on DONE as the sole end signal. Add tests to cover both DONE-emitting and non-DONE backends, with clear documentation in code comments explaining the rationale and any fallback behavior.
Applied to files:
core/providers/gemini/types.gocore/providers/gemini/chat.gocore/providers/anthropic/utils.gocore/providers/anthropic/responses.gocore/providers/gemini/responses.gocore/providers/gemini/utils.gocore/providers/gemini/gemini_test.gocore/providers/cohere/utils.gocore/providers/anthropic/types.go
📚 Learning: 2026-01-10T11:27:47.535Z
Learnt from: Radheshg04
Repo: maximhq/bifrost PR: 1256
File: core/providers/openai/openai.go:2276-2385
Timestamp: 2026-01-10T11:27:47.535Z
Learning: Validate image generation requests for nil and missing prompts before dispatch. Follow the same pattern used here: core/bifrost.go validates nil/empty prompts, providerUtils.CheckContextAndGetRequestBody returns a structured error when the request converter yields nil, and apply this across all providers (including OpenAI) to avoid sending null bodies.
Applied to files:
core/providers/gemini/types.gocore/providers/gemini/chat.gocore/providers/anthropic/utils.gocore/providers/anthropic/responses.gocore/providers/gemini/responses.gocore/providers/gemini/utils.gocore/providers/gemini/gemini_test.gocore/providers/cohere/utils.gocore/providers/anthropic/types.go
📚 Learning: 2025-12-19T08:29:20.286Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1095
File: core/internal/testutil/count_tokens.go:30-67
Timestamp: 2025-12-19T08:29:20.286Z
Learning: In core/internal/testutil test files, enforce using GetTestRetryConfigForScenario() to obtain a generic retry config, then construct a typed retry config (e.g., CountTokensRetryConfig, EmbeddingRetryConfig, TranscriptionRetryConfig) with an empty Conditions slice. Copy only MaxAttempts, BaseDelay, MaxDelay, OnRetry, and OnFinalFail from the generic config. This convention should be consistently applied across all test files in this directory.
Applied to files:
core/internal/testutil/structured_outputs.go
📚 Learning: 2026-01-11T14:08:10.341Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1298
File: core/providers/anthropic/anthropic.go:682-699
Timestamp: 2026-01-11T14:08:10.341Z
Learning: In Anthroplic streaming implementations (and analogous providers), ensure that the final 'summary' chunk, which carries usage information and metadata, is emitted after all delta chunks and uses a chunk index of last_delta_index + 1. This differentiates the summary chunk from content delta chunks. Apply this convention consistently in the anthropic provider code and in similar streaming providers, and consider adding a targeted test to assert the ordering and chunk index logic.
Applied to files:
core/providers/anthropic/utils.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.go
📚 Learning: 2026-01-13T13:36:35.221Z
Learnt from: TejasGhatte
Repo: maximhq/bifrost PR: 1319
File: core/providers/anthropic/responses.go:937-937
Timestamp: 2026-01-13T13:36:35.221Z
Learning: In core/providers/anthropic/responses.go, when handling Anthropic API streaming responses, ensure that content_block_start events include a signature field set to an empty string (e.g., contentBlock.Signature = ""). The actual signature is delivered later via signature_delta events. This behavior is per Anthropic's specification and should not be treated as an error. This guideline should apply to all Anthropic response handling files under core/providers/anthropic/ and similar go files that process streaming blocks.
Applied to files:
core/providers/anthropic/utils.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.go
📚 Learning: 2026-01-13T17:10:07.064Z
Learnt from: Radheshg04
Repo: maximhq/bifrost PR: 1312
File: tests/integrations/python/tests/test_openai.py:1166-1258
Timestamp: 2026-01-13T17:10:07.064Z
Learning: In tests under tests/integrations/python, prefer using the OpenAI image generation model 'gpt-image-1' via the config key providers.openai.image_generation for image-generation scenarios. This avoids DALLE-3 parameter limitations (e.g., n>1, quality/size combos). Ensure tests reference this provider in mocks/fixtures and document why this choice is used for test determinism.
Applied to files:
tests/integrations/python/tests/test_google.py
📚 Learning: 2025-12-17T08:44:08.788Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1114
File: core/schemas/chatcompletions.go:224-228
Timestamp: 2025-12-17T08:44:08.788Z
Learning: In core/schemas/chatcompletions.go, ensure the schema structures mirror OpenAI's API specifications exactly. Use the valid values for fields like ChatAudioParameters.Format and ChatAudioParameters.Voice as defined by OpenAI's documentation, and avoid adding additional inline documentation or constants to maintain direct compatibility with OpenAI's API.
Applied to files:
core/schemas/chatcompletions.go
📚 Learning: 2025-12-15T10:16:21.909Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/providers/huggingface/huggingface_test.go:12-63
Timestamp: 2025-12-15T10:16:21.909Z
Learning: In provider tests under core/providers/<provider>/*_test.go, do not require or flag the use of defer for Shutdown(); instead call client.Shutdown() at the end of each test function. This pattern appears consistent across all provider tests. Apply this rule only within this path; for other tests or resources, defer may still be appropriate.
Applied to files:
core/providers/gemini/gemini_test.go
🧬 Code graph analysis (7)
core/internal/testutil/structured_outputs.go (2)
core/providers/gemini/types.go (1)
Type(825-825)core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(312-315)
core/providers/anthropic/utils.go (2)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(312-315)core/schemas/utils.go (1)
SafeExtractOrderedMap(549-572)
core/providers/gemini/responses.go (3)
core/schemas/responses.go (4)
ResponsesToolMessage(475-495)ResponsesMessageTypeReasoning(312-312)ResponsesReasoning(745-748)BifrostResponsesStreamResponse(1446-1485)ui/lib/types/logs.ts (3)
ResponsesToolMessage(419-425)FunctionCall(165-168)ResponsesReasoning(433-436)core/providers/gemini/types.go (4)
Part(1030-1054)FunctionCall(1236-1246)Type(825-825)Content(1016-1024)
core/providers/gemini/utils.go (2)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(312-315)core/schemas/utils.go (1)
SafeExtractOrderedMap(549-572)
core/providers/gemini/gemini_test.go (2)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(312-315)core/schemas/utils.go (1)
Ptr(14-16)
core/providers/cohere/utils.go (2)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(312-315)core/schemas/utils.go (1)
SafeExtractOrderedMap(549-572)
core/providers/anthropic/types.go (2)
core/schemas/chatcompletions.go (1)
CacheControl(707-710)core/schemas/responses.go (1)
Citations(421-423)
🪛 Ruff (0.14.10)
tests/integrations/python/tests/test_google.py
1682-1682: Unused method argument: test_config
(ARG002)
1750-1750: f-string without any placeholders
Remove extraneous f prefix
(F541)
1768-1768: Unused method argument: test_config
(ARG002)
1895-1895: f-string without any placeholders
Remove extraneous f prefix
(F541)
1898-1898: f-string without any placeholders
Remove extraneous f prefix
(F541)
1902-1902: Unused method argument: test_config
(ARG002)
1942-1942: f-string without any placeholders
Remove extraneous f prefix
(F541)
1954-1954: f-string without any placeholders
Remove extraneous f prefix
(F541)
1957-1957: Unused method argument: test_config
(ARG002)
2001-2001: f-string without any placeholders
Remove extraneous f prefix
(F541)
2007-2007: f-string without any placeholders
Remove extraneous f prefix
(F541)
2012-2012: Unused method argument: test_config
(ARG002)
2093-2093: f-string without any placeholders
Remove extraneous f prefix
(F541)
2095-2095: Do not catch blind exception: Exception
(BLE001)
2120-2120: Local variable response_3 is assigned to but never used
Remove assignment to unused variable response_3
(F841)
2129-2129: f-string without any placeholders
Remove extraneous f prefix
(F541)
2130-2130: f-string without any placeholders
Remove extraneous f prefix
(F541)
2132-2132: Do not catch blind exception: Exception
(BLE001)
2292-2292: Unused method argument: test_config
(ARG002)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (15)
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
🔇 Additional comments (17)
core/changelog.md (1)
1-2: Verify changelog scope for stacked PR.Lines 1-2 don't appear in this PR's summary and seem to document changes from downstack PR #1313 (based on the target branch name "01-12-feat_send_back_raw_request_with_bifrost_errors").
Typically, each PR's changelog documents only its own changes. Please confirm whether these entries should be included here or if they belong to the downstack PR's changelog.
transports/changelog.md (1)
1-6: LGTM!The changelog entries accurately document the changes in this PR stack, covering AdditionalProperties support, thought signature handling, citations enhancement, and streaming event fixes.
core/providers/anthropic/types.go (3)
218-238: LGTM!The
AnthropicContentBlockstruct is well-extended with the new fields for signatures and web search results. The field organization and JSON tags are consistent.
248-291: LGTM!The new
AnthropicCitationTypeconstants andAnthropicTextCitationstruct comprehensively cover all Anthropic citation location types (char_location, page_location, content_block_location, web_search_result_location, search_result_location) with appropriate optional fields for each type.
293-336: LGTM!The
AnthropicCitationsunion type correctly handles the dual representation (request config vs response citations) with proper mutual exclusivity validation inMarshalJSONand type detection inUnmarshalJSON. Based on the learning that Anthropic streaming content_block_start events include signature fields set to empty strings, this implementation aligns well with the expected API behavior.core/schemas/chatcompletions.go (2)
276-282: LGTM!The field type change from
*boolto*AdditionalPropertiesStructenables support for both boolean and object forms ofadditionalProperties, which is required for proper OpenAPI schema compatibility.
312-367: LGTM!The
AdditionalPropertiesStructimplementation correctly handles:
- Mutual exclusivity validation in
MarshalJSON- Explicit null handling in
UnmarshalJSON- Proper type detection for boolean vs object forms
- Error reporting for invalid input types
The union type pattern is well-suited for this use case where JSON Schema's
additionalPropertiescan be either a boolean or a schema object.core/providers/gemini/types.go (1)
22-23: LGTM!The
thoughtSignatureSeparatorconstant is well-documented and provides a clean mechanism for embedding thought signatures in tool IDs for Gemini's function calling flow. The constant is used consistently across all three Gemini provider files (responses.go, chat.go) with no hard-coded literals, following proper encapsulation practices.core/providers/gemini/gemini_test.go (1)
558-561: Test expectation correctly updated forAdditionalPropertiesStruct. Looks aligned with the new bool-wrapper representation.core/providers/anthropic/responses.go (1)
937-954: Signature lifecycle for thinking blocks looks correct (empty on start, real value via signature_delta). This matches Anthropic streaming behavior (signature delivered later) and preserves Signature into the emitted reasoning content parts.Also applies to: 428-439, 533-551
core/providers/anthropic/utils.go (2)
774-778: Map-formadditionalPropertiessupport is a solid addition. This matches the schema union representation and keeps parity with Gemini’s schema handling.Also applies to: 1151-1155
31-94: Stack-level AdditionalProperties sweep needed. Search the stack for*AdditionalPropertiesdereferencing patterns and verify safe usage across providers. Found instances in:
core/providers/anthropic/utils.go:690core/providers/cohere/responses.go:903core/providers/gemini/responses.go:2092core/schemas/utils.go:801All current instances have proper nil checks, but verify consistency with the recent
AdditionalPropertiesStructrefactoring (commit "additional properties struct fixes") to ensure all providers correctly handle theAdditionalPropertiesBoolvsAdditionalPropertiesMapunion type.core/providers/gemini/responses.go (5)
246-286: Thought signature extraction and look-ahead logic is well implemented.The implementation correctly:
- Extracts thought signature from CallID if it contains the separator
- Falls back to look-ahead for a reasoning message with EncryptedContent
- Uses
consumedIndicesto track consumed reasoning messagesThe code handles edge cases properly by checking for decode errors before using the signature.
457-477: Streaming path correctly mirrors non-streaming signature extraction.The logic extracts the base64-encoded thought signature from the CallID (after the separator) and decodes it to attach to the function call part, maintaining consistency with the non-streaming path.
1057-1099: Function call streaming: signature embedded in CallID for downstream propagation.The implementation correctly appends the base64-encoded thought signature to the tool ID using the separator, ensuring the signature travels with the function call through the streaming pipeline.
1866-1870: Non-streaming output conversion matches streaming approach.The implementation attaches thought signature to callID in the same format (
{callID}{separator}{base64sig}) as the streaming path, ensuring consistent handling across both paths.
2440-2444:FunctionResponse.IDassignment looks correct.The ID is correctly assigned from
msg.ResponsesToolMessage.CallIDwithout stripping the thought signature separator, preserving the full ID for response matching.
8a99f70 to
cd8b2ea
Compare
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: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
core/providers/anthropic/responses.go (1)
898-924: Keep synthetic toolcontent_block_deltaindices consistent with the OutputIndex-firstcontent_block_start.You’re now emitting
content_block_startwithIndex = OutputIndex(fallbackContentIndex), but syntheticinput_json_deltageneration still keys offbifrostResp.ContentIndex. If these diverge (orContentIndexis nil), the reconstructed Anthropic stream can become internally inconsistent.Proposed fix
- deltaEvents := generateSyntheticInputJSONDeltas(argumentsJSON, bifrostResp.ContentIndex) + idx := bifrostResp.OutputIndex + if idx == nil { + idx = bifrostResp.ContentIndex + } + deltaEvents := generateSyntheticInputJSONDeltas(argumentsJSON, idx) events = append(events, deltaEvents...)Also applies to: 1013-1047, 1056-1062, 1072-1077, 1093-1098, 1127-1132, 1155-1160, 1198-1202, 1220-1224
core/providers/gemini/responses.go (1)
1057-1100: Bug: update stream state maps after you append thoughtSignature intotoolUseID.
state.ItemIDs[outputIndex]/state.ToolCallIDs[outputIndex]are set beforetoolUseIDis modified. If a thought signature is appended, subsequent logic (likecloseGeminiOpenItems) will emit done events using the old ID, not the one actually sent to clients.Proposed fix
// Attach thought signature to ID if present if len(part.ThoughtSignature) > 0 && !strings.Contains(toolUseID, thoughtSignatureSeparator) { encoded := base64.StdEncoding.EncodeToString(part.ThoughtSignature) toolUseID = fmt.Sprintf("%s%s%s", toolUseID, thoughtSignatureSeparator, encoded) + // Keep state consistent with emitted IDs + state.ItemIDs[outputIndex] = toolUseID + state.ToolCallIDs[outputIndex] = toolUseID }core/providers/gemini/chat.go (1)
318-350: Add ID field to streaming ReasoningDetails for extracted signatures.In the streaming path (lines 318-350), when extracting a thought signature from CallID, append the ReasoningDetails with
ID: schemas.Ptr(fmt.Sprintf("tool_call_%s", callID)). Currently, the ID field is omitted, which prevents the signature from being matched back to the tool call during round-tripping, as expected by the matching logic in utils.go (lines 951-966).The non-streaming path in the same file correctly sets this ID field; the streaming path should do the same for consistency.
For lines 360-368 (standalone part.ThoughtSignature): This appears to be a separate case without an associated callID, so the applicability of the "tool_call_" ID pattern is unclear and may require separate consideration.
🤖 Fix all issues with AI agents
In @core/providers/gemini/chat.go:
- Around line 94-103: The non-streaming handler currently appends thought text
and then uses `continue`, which prevents capturing `part.ThoughtSignature` when
a part has both text and a signature; remove the `continue` in the non-stream
path (the block that checks `if part.Text != "" && part.Thought`) and
restructure so that the `ThoughtSignature` processing (the same logic used later
in the non-stream path and mirrored by the streaming path handling) runs
unconditionally after the type-specific handling; ensure `reasoningDetails` is
still appended for text and that `part.ThoughtSignature` is processed afterwards
(matching the streaming path behavior that allows signatures to co-exist with
other types).
In @core/schemas/chatcompletions.go:
- Around line 312-368: AdditionalPropertiesStruct.MarshalJSON currently returns
JSON null when both fields are nil, which can produce `"additionalProperties":
null` and violate JSON Schema expectations; change MarshalJSON so that when both
AdditionalPropertiesBool and AdditionalPropertiesMap are nil it emits an empty
object (e.g., marshal an empty OrderedMap/object) instead of null, and make
UnmarshalJSON defensively normalize an empty object back to both fields nil
(i.e., if unmarshalling into OrderedMap yields an empty map, set
AdditionalPropertiesMap = nil and AdditionalPropertiesBool = nil) so empty
structs don’t round-trip into `"additionalProperties": null`.
🧹 Nitpick comments (4)
core/providers/gemini/responses.go (1)
247-287: ThoughtSignature-in-CallID extraction looks consistent, but guard delimiter collisions.If
thoughtSignatureSeparatorcan appear in “base” IDs (from clients),SplitN(..., 2)will parse the first occurrence. Consider parsing by the last occurrence (or enforcing/validating the format before split) so tool IDs remain unambiguous.Also applies to: 458-477, 2377-2401
core/providers/anthropic/responses.go (1)
1165-1193: Fix struct tag or standardize null handling forstop_sequence.
AnthropicStreamDelta.StopSequenceis taggedjson:"stop_sequence"withoutomitempty, so setting it tonilserializes as"stop_sequence": null. This is inconsistent withAnthropicMessageResponse.StopSequence, which hasomitempty. While Anthropic's API permitsnullforstop_sequence, the codebase is inconsistent: line 1172 sets it toschemas.Ptr("")(empty string), while line 1189 sets it tonil. Either addomitemptyto the struct tag to omit the field entirely, or consistently useschemas.Ptr("")across both code paths.core/providers/anthropic/types.go (1)
248-336: Citations union type is OK structurally; consider tightening validation + clarifying{}behavior.
Today,citations: {}will successfully unmarshal asschemas.CitationswithEnabled=nil(since it’s tried after slice). If{}is invalid per Anthropic, consider returning an error when config is “empty”. Also consider validating required fields perAnthropicCitationType(e.g.,cited_textpresent is good, but location-specific required fields aren’t enforced).core/providers/gemini/utils.go (1)
1145-1155: AdditionalPropertiesStruct handling is good; consider usingschemas.Ptrfor consistency and confirm marshaling matches expected JSON shape.
Right now bool uses&additionalPropswhile tests tend to useschemas.Ptr(false). Not a functional issue, but consistency helps. More importantly: please confirmAdditionalPropertiesStructJSON marshaling produces eitherfalseor{...}(not an object wrapper) as expected by downstream.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (16)
core/changelog.mdcore/internal/testutil/structured_outputs.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.gocore/providers/anthropic/utils.gocore/providers/cohere/utils.gocore/providers/gemini/chat.gocore/providers/gemini/gemini_test.gocore/providers/gemini/responses.gocore/providers/gemini/types.gocore/providers/gemini/utils.gocore/schemas/chatcompletions.gocore/schemas/responses.gotests/integrations/python/tests/test_google.pytransports/bifrost-http/integrations/router.gotransports/changelog.md
💤 Files with no reviewable changes (1)
- transports/bifrost-http/integrations/router.go
🚧 Files skipped from review as they are similar to previous changes (7)
- core/providers/cohere/utils.go
- core/internal/testutil/structured_outputs.go
- core/schemas/responses.go
- transports/changelog.md
- core/providers/anthropic/utils.go
- core/providers/gemini/types.go
- core/changelog.md
🧰 Additional context used
📓 Path-based instructions (1)
**
⚙️ CodeRabbit configuration file
always check the stack if there is one for the current PR. do not give localized reviews for the PR, always see all changes in the light of the whole stack of PRs (if there is a stack, if there is no stack you can continue to make localized suggestions/reviews)
Files:
core/providers/anthropic/responses.gocore/providers/gemini/gemini_test.gocore/providers/gemini/responses.gocore/providers/gemini/chat.gocore/providers/gemini/utils.gocore/providers/anthropic/types.gocore/schemas/chatcompletions.gotests/integrations/python/tests/test_google.py
🧠 Learnings (9)
📚 Learning: 2026-01-13T13:36:35.221Z
Learnt from: TejasGhatte
Repo: maximhq/bifrost PR: 1319
File: core/providers/anthropic/responses.go:937-937
Timestamp: 2026-01-13T13:36:35.221Z
Learning: In core/providers/anthropic/responses.go, when handling Anthropic API streaming responses, ensure that content_block_start events include a signature field set to an empty string (e.g., contentBlock.Signature = ""). The actual signature is delivered later via signature_delta events. This behavior is per Anthropic's specification and should not be treated as an error. This guideline should apply to all Anthropic response handling files under core/providers/anthropic/ and similar go files that process streaming blocks.
Applied to files:
core/providers/anthropic/responses.gocore/providers/anthropic/types.go
📚 Learning: 2025-12-09T17:07:42.007Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/schemas/account.go:9-18
Timestamp: 2025-12-09T17:07:42.007Z
Learning: In core/schemas/account.go, the HuggingFaceKeyConfig field within the Key struct is currently unused and reserved for future Hugging Face inference endpoint deployments. Do not flag this field as missing from OpenAPI documentation or require its presence in the API spec until the feature is actively implemented and used. When the feature is added, update the OpenAPI docs accordingly; otherwise, treat this field as non-breaking and not part of the current API surface.
Applied to files:
core/providers/anthropic/responses.gocore/providers/gemini/gemini_test.gocore/providers/gemini/responses.gocore/providers/gemini/chat.gocore/providers/gemini/utils.gocore/providers/anthropic/types.gocore/schemas/chatcompletions.go
📚 Learning: 2025-12-29T11:54:55.836Z
Learnt from: akshaydeo
Repo: maximhq/bifrost PR: 1153
File: framework/configstore/rdb.go:2221-2246
Timestamp: 2025-12-29T11:54:55.836Z
Learning: In Go reviews, do not flag range-over-int patterns like for i := range n as compile-time errors, assuming Go 1.22+ semantics. Only flag actual range-capable values (slices, arrays, maps, channels, strings) and other compile-time issues. This applies to all Go files across the repository.
Applied to files:
core/providers/anthropic/responses.gocore/providers/gemini/gemini_test.gocore/providers/gemini/responses.gocore/providers/gemini/chat.gocore/providers/gemini/utils.gocore/providers/anthropic/types.gocore/schemas/chatcompletions.go
📚 Learning: 2025-12-19T09:26:54.961Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/providers/utils/utils.go:1050-1051
Timestamp: 2025-12-19T09:26:54.961Z
Learning: Update streaming end-marker handling so HuggingFace is treated as a non-[DONE] provider for backends that do not emit a DONE marker (e.g., meta llama on novita). In core/providers/utils/utils.go, adjust ProviderSendsDoneMarker() (or related logic) to detect providers that may not emit DONE and avoid relying on DONE as the sole end signal. Add tests to cover both DONE-emitting and non-DONE backends, with clear documentation in code comments explaining the rationale and any fallback behavior.
Applied to files:
core/providers/anthropic/responses.gocore/providers/gemini/gemini_test.gocore/providers/gemini/responses.gocore/providers/gemini/chat.gocore/providers/gemini/utils.gocore/providers/anthropic/types.go
📚 Learning: 2026-01-10T11:27:47.535Z
Learnt from: Radheshg04
Repo: maximhq/bifrost PR: 1256
File: core/providers/openai/openai.go:2276-2385
Timestamp: 2026-01-10T11:27:47.535Z
Learning: Validate image generation requests for nil and missing prompts before dispatch. Follow the same pattern used here: core/bifrost.go validates nil/empty prompts, providerUtils.CheckContextAndGetRequestBody returns a structured error when the request converter yields nil, and apply this across all providers (including OpenAI) to avoid sending null bodies.
Applied to files:
core/providers/anthropic/responses.gocore/providers/gemini/gemini_test.gocore/providers/gemini/responses.gocore/providers/gemini/chat.gocore/providers/gemini/utils.gocore/providers/anthropic/types.go
📚 Learning: 2026-01-11T14:08:10.341Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1298
File: core/providers/anthropic/anthropic.go:682-699
Timestamp: 2026-01-11T14:08:10.341Z
Learning: In Anthroplic streaming implementations (and analogous providers), ensure that the final 'summary' chunk, which carries usage information and metadata, is emitted after all delta chunks and uses a chunk index of last_delta_index + 1. This differentiates the summary chunk from content delta chunks. Apply this convention consistently in the anthropic provider code and in similar streaming providers, and consider adding a targeted test to assert the ordering and chunk index logic.
Applied to files:
core/providers/anthropic/responses.gocore/providers/anthropic/types.go
📚 Learning: 2025-12-15T10:16:21.909Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/providers/huggingface/huggingface_test.go:12-63
Timestamp: 2025-12-15T10:16:21.909Z
Learning: In provider tests under core/providers/<provider>/*_test.go, do not require or flag the use of defer for Shutdown(); instead call client.Shutdown() at the end of each test function. This pattern appears consistent across all provider tests. Apply this rule only within this path; for other tests or resources, defer may still be appropriate.
Applied to files:
core/providers/gemini/gemini_test.go
📚 Learning: 2025-12-17T08:44:08.788Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1114
File: core/schemas/chatcompletions.go:224-228
Timestamp: 2025-12-17T08:44:08.788Z
Learning: In core/schemas/chatcompletions.go, ensure the schema structures mirror OpenAI's API specifications exactly. Use the valid values for fields like ChatAudioParameters.Format and ChatAudioParameters.Voice as defined by OpenAI's documentation, and avoid adding additional inline documentation or constants to maintain direct compatibility with OpenAI's API.
Applied to files:
core/schemas/chatcompletions.go
📚 Learning: 2026-01-13T17:10:07.064Z
Learnt from: Radheshg04
Repo: maximhq/bifrost PR: 1312
File: tests/integrations/python/tests/test_openai.py:1166-1258
Timestamp: 2026-01-13T17:10:07.064Z
Learning: In tests under tests/integrations/python, prefer using the OpenAI image generation model 'gpt-image-1' via the config key providers.openai.image_generation for image-generation scenarios. This avoids DALLE-3 parameter limitations (e.g., n>1, quality/size combos). Ensure tests reference this provider in mocks/fixtures and document why this choice is used for test determinism.
Applied to files:
tests/integrations/python/tests/test_google.py
🧬 Code graph analysis (7)
core/providers/anthropic/responses.go (4)
core/schemas/utils.go (1)
Ptr(14-16)core/providers/anthropic/types.go (3)
AnthropicStreamDelta(519-527)AnthropicStopReasonEndTurn(435-435)AnthropicStreamEvent(498-507)core/providers/anthropic/utils.go (1)
ConvertBifrostFinishReasonToAnthropic(88-93)core/schemas/responses.go (1)
ResponsesStreamResponseTypeMCPCallArgumentsDelta(1420-1420)
core/providers/gemini/gemini_test.go (2)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(312-315)core/schemas/utils.go (1)
Ptr(14-16)
core/providers/gemini/responses.go (2)
core/schemas/responses.go (1)
ResponsesReasoning(745-748)core/providers/gemini/types.go (3)
Part(1030-1054)FunctionCall(1236-1246)Content(1016-1024)
core/providers/gemini/utils.go (2)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(312-315)core/schemas/utils.go (1)
SafeExtractOrderedMap(549-572)
core/providers/anthropic/types.go (2)
core/schemas/chatcompletions.go (1)
CacheControl(707-710)core/schemas/responses.go (1)
Citations(421-423)
core/schemas/chatcompletions.go (2)
core/schemas/json_native.go (2)
Marshal(8-10)Unmarshal(18-20)core/schemas/json_wasm.go (2)
Marshal(8-10)Unmarshal(22-24)
tests/integrations/python/tests/test_google.py (1)
core/providers/gemini/types.go (4)
Tool(647-674)Content(1016-1024)Part(1030-1054)FunctionResponse(1249-1271)
🪛 Ruff (0.14.11)
tests/integrations/python/tests/test_google.py
1682-1682: Unused method argument: test_config
(ARG002)
1750-1750: f-string without any placeholders
Remove extraneous f prefix
(F541)
1768-1768: Unused method argument: test_config
(ARG002)
1895-1895: f-string without any placeholders
Remove extraneous f prefix
(F541)
1898-1898: f-string without any placeholders
Remove extraneous f prefix
(F541)
1902-1902: Unused method argument: test_config
(ARG002)
1942-1942: f-string without any placeholders
Remove extraneous f prefix
(F541)
1954-1954: f-string without any placeholders
Remove extraneous f prefix
(F541)
1957-1957: Unused method argument: test_config
(ARG002)
2001-2001: f-string without any placeholders
Remove extraneous f prefix
(F541)
2007-2007: f-string without any placeholders
Remove extraneous f prefix
(F541)
2012-2012: Unused method argument: test_config
(ARG002)
2093-2093: f-string without any placeholders
Remove extraneous f prefix
(F541)
2095-2095: Do not catch blind exception: Exception
(BLE001)
2132-2132: Do not catch blind exception: Exception
(BLE001)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
🔇 Additional comments (8)
core/providers/anthropic/responses.go (1)
934-984: Good: initializeSignatureto empty string on thinking blocks.Setting
contentBlock.Signature = Ptr("")atcontent_block_startis the right behavior (actual signature arrives later viasignature_delta). Based on learnings, this should not be treated as missing/invalid.core/providers/gemini/responses.go (2)
1866-1870: Good: batch path mirrors streaming CallID augmentation.This keeps tool-call matching stable across streaming vs non-streaming conversions.
2443-2444: Nice: includeFunctionResponse.IDto preserve tool-call correlation.This should help Gemini match function responses to calls more reliably.
core/schemas/chatcompletions.go (1)
276-282: API change verified: all callers properly updated to use*AdditionalPropertiesStructinstead of*bool.The type change from
*boolto*AdditionalPropertiesStructhas been completed throughout the codebase. All 30+ usages across providers (Anthropic, Cohere, Gemini, Bedrock), test utilities, and core schemas properly construct&schemas.AdditionalPropertiesStruct{AdditionalPropertiesBool: ...}or{AdditionalPropertiesMap: ...}. No legacy*boolassignments remain.core/providers/anthropic/types.go (1)
219-238: AnthropicContentBlock looks compatible, but please double-check downstream consumers for the new/reshuffled fields (esp. Signature + citations).
Signature being a*stringwithomitemptyis good for the “emit empty string when required” pattern (non-nil pointer). For the stack: ensure any conversion/normalization code (outside this file) doesn’t assume citations is a single shape anymore.core/providers/gemini/gemini_test.go (1)
558-562: Test update matches the newAdditionalPropertiesStructAPI.core/providers/gemini/chat.go (1)
130-160: CallID signature extraction behavior should be validated against Gemini 3 contract (esp. separator choice + base64 variants).
You decode then re-encode the signature, which implicitly “validates” base64 and normalizes it; that’s fine, but please confirm this matches the expected signature format and that tool-call correlation IDs remain stable across tool responses.Also applies to: 318-328
tests/integrations/python/tests/test_google.py (1)
1681-2140: Fix ARG002, F541, and BLE001 lint violations; make signature assertions more defensive.All five new test methods (
test_30athroughtest_30e) have unusedtest_configparameters. Remove it from signatures or mark with_ = test_configif intentional.Several
print(f"...")statements contain no placeholders (e.g.,print(f" → No signature")); replace with plain strings.In
test_30e, the broadexcept Exception as e:catches should either narrow the exception type or include# noqa: BLE001with justification.Additionally, the assertion
assert signatures[0][0] == 0assumes the first function call always carries the signature—Gemini's behavior may vary. Instead, verify that signatures exist somewhere, log their distribution, and defer to integration test feedback rather than strict index pinning.
cd8b2ea to
a4107d0
Compare
5888ee9 to
3609218
Compare
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: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (5)
core/providers/anthropic/responses.go (2)
1165-1191: SetStopSequencetoschemas.Ptr("")for consistency with the default initialization above.The
AnthropicStreamDelta.StopSequencefield has noomitemptytag, so setting it tonilserializes as"stop_sequence": null. The initial Delta on line 1171 sets it toschemas.Ptr(""), so this conditional override should match that pattern to avoid sending null values to the Anthropic API.Proposed fix
if bifrostResp.Response.StopReason != nil { anthropicContentDeltaEvent.Delta = &AnthropicStreamDelta{ StopReason: schemas.Ptr(ConvertBifrostFinishReasonToAnthropic(*bifrostResp.Response.StopReason)), - StopSequence: nil, + StopSequence: schemas.Ptr(""), } }
898-924: Use consistent index preference for syntheticinput_json_deltaevents.
content_block_startevents useOutputIndexwithContentIndexas fallback, but synthetic tool-argument deltas at line 1045 ignoreOutputIndexand always useContentIndex. When both indices are present and differ, downstream synthetic deltas will target the wrong block index.Fix
- deltaEvents := generateSyntheticInputJSONDeltas(argumentsJSON, bifrostResp.ContentIndex) + // Use the same index as content_block_start for consistency + synthIndex := bifrostResp.OutputIndex + if synthIndex == nil { + synthIndex = bifrostResp.ContentIndex + } + deltaEvents := generateSyntheticInputJSONDeltas(argumentsJSON, synthIndex)core/providers/gemini/chat.go (1)
176-202: Ensure these signature sources are mutually exclusive.When
part.FunctionCallhas an embedded signature in CallID that was extracted and stored inpart.ThoughtSignatureby the responses layer, both code paths (lines 130-150 and 176-202) will execute, adding duplicate reasoning details for the same signature. Consider adding a guard to skip the standalone ThoughtSignature path when the signature was already extracted from CallID.core/providers/gemini/responses.go (2)
493-498: Fix possible nil-deref + incorrect signature bytes inOutputItemAddedconversion.The condition checks
bifrostResp.Item.EncryptedContent != nilbut then dereferencesbifrostResp.Item.ResponsesReasoning.EncryptedContent(can be nil) and also assigns the base64 string bytes directly toPart.ThoughtSignatureinstead of base64-decoding it. This can crash or corrupt signatures.Proposed fix
case schemas.ResponsesStreamResponseTypeOutputItemAdded: - if bifrostResp.Item != nil && bifrostResp.Item.ResponsesReasoning != nil && bifrostResp.Item.EncryptedContent != nil { - candidate.Content.Parts = append(candidate.Content.Parts, &Part{ - ThoughtSignature: []byte(*bifrostResp.Item.ResponsesReasoning.EncryptedContent), - }) - } + if bifrostResp.Item != nil && + bifrostResp.Item.ResponsesReasoning != nil && + bifrostResp.Item.ResponsesReasoning.EncryptedContent != nil { + if decoded, err := base64.StdEncoding.DecodeString(*bifrostResp.Item.ResponsesReasoning.EncryptedContent); err == nil { + candidate.Content.Parts = append(candidate.Content.Parts, &Part{ + ThoughtSignature: decoded, + }) + } + }
1057-1103: Streaming:toolUseIDmutation after state bookkeeping breaks ID consistency.
state.ItemIDs[outputIndex]/state.ToolCallIDs[outputIndex]are set before appending the thought signature totoolUseID(Line 1057-1062). The emittedoutput_item.added/function_call_arguments.doneuse the mutated ID, but later closures (e.g.,closeGeminiOpenItems) will use the old ID fromstate.ItemIDs, causing mismatched IDs across events.Proposed fix
toolUseID := part.FunctionCall.ID if toolUseID == "" { toolUseID = part.FunctionCall.Name // Fallback to name as ID } + // Attach thought signature to ID if present (must happen before storing IDs in state maps) + if len(part.ThoughtSignature) > 0 && !strings.Contains(toolUseID, thoughtSignatureSeparator) { + encoded := base64.StdEncoding.EncodeToString(part.ThoughtSignature) + toolUseID = fmt.Sprintf("%s%s%s", toolUseID, thoughtSignatureSeparator, encoded) + } state.ItemIDs[outputIndex] = toolUseID state.ToolCallIDs[outputIndex] = toolUseID state.ToolCallNames[outputIndex] = part.FunctionCall.Name - // Attach thought signature to ID if present - if len(part.ThoughtSignature) > 0 && !strings.Contains(toolUseID, thoughtSignatureSeparator) { - encoded := base64.StdEncoding.EncodeToString(part.ThoughtSignature) - toolUseID = fmt.Sprintf("%s%s%s", toolUseID, thoughtSignatureSeparator, encoded) - } -
🤖 Fix all issues with AI agents
In `@core/schemas/chatcompletions.go`:
- Around line 319-338: The marshaler emits JSON null when AdditionalProperties
is a non-nil pointer whose variant fields are both nil, which prevents omitempty
from dropping the field; update ToolFunctionParameters.UnmarshalJSON to
normalize that case by setting t.AdditionalProperties = nil whenever
t.AdditionalProperties != nil and both
t.AdditionalProperties.AdditionalPropertiesBool and
t.AdditionalProperties.AdditionalPropertiesMap are nil so subsequent marshaling
(AdditionalPropertiesStruct.MarshalJSON) will omit the field instead of emitting
null.
In `@tests/integrations/python/tests/test_google.py`:
- Around line 1681-1766: The assertion signatures[0][0] == 0 is flaky because
parts before function calls can exist; instead record the part index of each
function call when building function_calls (e.g., collect function_call_indices
alongside function_calls in the loop) and assert the thought signature index
equals the first function call's part index (assert signatures[0][0] ==
function_call_indices[0]) so you compare against the first function-call part
index rather than absolute part index 0.
- Around line 1681-2140: Rename the unused parameter test_config to _test_config
in all five test functions test_30a_gemini_3_parallel_function_calls_signatures,
test_30b_gemini_3_sequential_function_calls_signatures,
test_30c_gemini_3_thought_signatures_in_text_responses,
test_30d_gemini_3_thinking_levels, and
test_30e_gemini_3_signature_validation_strict to fix ARG002; remove the
unnecessary f-string prefixes from the two print statements that have no
placeholders (the prints currently using f" Only {len(function_calls)} function
call(s) made" and f" FC: {fc.name}, Signature: {len(sig) if sig else 0}
bytes" should be regular strings or formatted properly without an f if no
placeholders); and replace the bare except Exception as e blocks in test_30e
(the two try/except blocks handling conversation_without_sig and response_2)
with except (google.genai.errors.APIError, ValueError) as e and add a short
inline comment noting these are catching SDK/API and validation errors for
integration test handling.
♻️ Duplicate comments (1)
core/providers/gemini/responses.go (1)
247-286: Standard base64 in embedded IDs: confirm Gemini’s ID charset/length rules + decode robustness.
Right now the signature embedded into CallID/toolUseID usesbase64.StdEncodingand extraction usesbase64.StdEncoding.DecodeStringonly. If Gemini constrainsfunction_call.id(or any “tool ID” field) to URL-safe characters / max length,+,/,=(and long strings) can break requests; also if any path produces URL-safe base64, decoding will fail and the code silently drops the signature in some places.Gemini API / Google GenAI: what are the character-set and length constraints for function_call.id (tool call ID)? Are '+' '/' '=' allowed, and what is the max length?If constraints exist, consider:
- Using
base64.RawURLEncoding(or another allowed encoding) only for the embedded-ID representation.- Adding a small helper to decode either standard or URL-safe base64 (handling missing padding) and using it consistently in all “extract from CallID” sites.
Also applies to: 457-477, 1057-1062, 1866-1871, 2377-2401
🧹 Nitpick comments (3)
core/providers/cohere/utils.go (1)
75-86: Minor: make the object case anelse iffor clarity (bool and object are mutually exclusive).Proposed refactor
// Extract additionalProperties if addPropsVal, ok := paramsMap["additionalProperties"].(bool); ok { result.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ AdditionalPropertiesBool: &addPropsVal, } - } - - if addPropsVal, ok := schemas.SafeExtractOrderedMap(paramsMap["additionalProperties"]); ok { + } else if addPropsVal, ok := schemas.SafeExtractOrderedMap(paramsMap["additionalProperties"]); ok { result.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ AdditionalPropertiesMap: &addPropsVal, } }core/providers/anthropic/utils.go (1)
768-778: Consider usingelse iffor mutually exclusive branches.The two conditions are functionally mutually exclusive (a value can't be both a
booland an ordered map), but usingelse ifwould make this explicit and marginally more efficient by avoiding the second type assertion when the first succeeds.♻️ Suggested improvement
if additionalProps, ok := schemaMap["additionalProperties"].(bool); ok { jsonSchema.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ AdditionalPropertiesBool: &additionalProps, } - } - - if additionalProps, ok := schemas.SafeExtractOrderedMap(schemaMap["additionalProperties"]); ok { + } else if additionalProps, ok := schemas.SafeExtractOrderedMap(schemaMap["additionalProperties"]); ok { jsonSchema.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ AdditionalPropertiesMap: &additionalProps, } }core/providers/gemini/utils.go (1)
1146-1155: Consider usingelse iffor mutually exclusive branches.Same pattern as in
core/providers/anthropic/utils.go- the two conditions are mutually exclusive, and usingelse ifwould make this explicit.♻️ Suggested improvement
if additionalProps, ok := normalizedSchemaMap["additionalProperties"].(bool); ok { jsonSchema.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ AdditionalPropertiesBool: &additionalProps, } - } - - if additionalProps, ok := schemas.SafeExtractOrderedMap(normalizedSchemaMap["additionalProperties"]); ok { + } else if additionalProps, ok := schemas.SafeExtractOrderedMap(normalizedSchemaMap["additionalProperties"]); ok { jsonSchema.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ AdditionalPropertiesMap: &additionalProps, } }
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (16)
core/changelog.mdcore/internal/testutil/structured_outputs.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.gocore/providers/anthropic/utils.gocore/providers/cohere/utils.gocore/providers/gemini/chat.gocore/providers/gemini/gemini_test.gocore/providers/gemini/responses.gocore/providers/gemini/types.gocore/providers/gemini/utils.gocore/schemas/chatcompletions.gocore/schemas/responses.gotests/integrations/python/tests/test_google.pytransports/bifrost-http/integrations/router.gotransports/changelog.md
💤 Files with no reviewable changes (1)
- transports/bifrost-http/integrations/router.go
🚧 Files skipped from review as they are similar to previous changes (3)
- core/internal/testutil/structured_outputs.go
- core/providers/gemini/types.go
- transports/changelog.md
🧰 Additional context used
📓 Path-based instructions (1)
**
⚙️ CodeRabbit configuration file
always check the stack if there is one for the current PR. do not give localized reviews for the PR, always see all changes in the light of the whole stack of PRs (if there is a stack, if there is no stack you can continue to make localized suggestions/reviews)
Files:
core/schemas/responses.gocore/providers/gemini/chat.gocore/providers/anthropic/utils.gocore/providers/anthropic/responses.gocore/schemas/chatcompletions.gocore/providers/gemini/gemini_test.gocore/providers/anthropic/types.gotests/integrations/python/tests/test_google.pycore/providers/gemini/utils.gocore/providers/gemini/responses.gocore/providers/cohere/utils.gocore/changelog.md
🧠 Learnings (10)
📚 Learning: 2025-12-09T17:07:42.007Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/schemas/account.go:9-18
Timestamp: 2025-12-09T17:07:42.007Z
Learning: In core/schemas/account.go, the HuggingFaceKeyConfig field within the Key struct is currently unused and reserved for future Hugging Face inference endpoint deployments. Do not flag this field as missing from OpenAPI documentation or require its presence in the API spec until the feature is actively implemented and used. When the feature is added, update the OpenAPI docs accordingly; otherwise, treat this field as non-breaking and not part of the current API surface.
Applied to files:
core/schemas/responses.gocore/providers/gemini/chat.gocore/providers/anthropic/utils.gocore/providers/anthropic/responses.gocore/schemas/chatcompletions.gocore/providers/gemini/gemini_test.gocore/providers/anthropic/types.gocore/providers/gemini/utils.gocore/providers/gemini/responses.gocore/providers/cohere/utils.go
📚 Learning: 2025-12-29T11:54:55.836Z
Learnt from: akshaydeo
Repo: maximhq/bifrost PR: 1153
File: framework/configstore/rdb.go:2221-2246
Timestamp: 2025-12-29T11:54:55.836Z
Learning: In Go reviews, do not flag range-over-int patterns like for i := range n as compile-time errors, assuming Go 1.22+ semantics. Only flag actual range-capable values (slices, arrays, maps, channels, strings) and other compile-time issues. This applies to all Go files across the repository.
Applied to files:
core/schemas/responses.gocore/providers/gemini/chat.gocore/providers/anthropic/utils.gocore/providers/anthropic/responses.gocore/schemas/chatcompletions.gocore/providers/gemini/gemini_test.gocore/providers/anthropic/types.gocore/providers/gemini/utils.gocore/providers/gemini/responses.gocore/providers/cohere/utils.go
📚 Learning: 2026-01-14T04:40:11.480Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1312
File: framework/modelcatalog/pricing.go:276-426
Timestamp: 2026-01-14T04:40:11.480Z
Learning: In the Bifrost codebase, ImageUsage and other usage types guarantee that TotalTokens is populated (computed as InputTokens + OutputTokens if providers don’t supply TotalTokens). Reviewers can rely on this invariant and should not assume TotalTokens may be missing when input/output tokens exist. When implementing tiering logic or token-based decisions, you can safely use TotalTokens without extra null/zero guards, provided you’re in a context where InputTokens and OutputTokens are present. If a branch might discard tokens, ensure the invariant is preserved or add explicit checks only where the inputs are confirmed to be valid.
Applied to files:
core/schemas/responses.gocore/providers/gemini/chat.gocore/providers/anthropic/utils.gocore/providers/anthropic/responses.gocore/schemas/chatcompletions.gocore/providers/gemini/gemini_test.gocore/providers/anthropic/types.gocore/providers/gemini/utils.gocore/providers/gemini/responses.gocore/providers/cohere/utils.go
📚 Learning: 2025-12-19T09:26:54.961Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/providers/utils/utils.go:1050-1051
Timestamp: 2025-12-19T09:26:54.961Z
Learning: Update streaming end-marker handling so HuggingFace is treated as a non-[DONE] provider for backends that do not emit a DONE marker (e.g., meta llama on novita). In core/providers/utils/utils.go, adjust ProviderSendsDoneMarker() (or related logic) to detect providers that may not emit DONE and avoid relying on DONE as the sole end signal. Add tests to cover both DONE-emitting and non-DONE backends, with clear documentation in code comments explaining the rationale and any fallback behavior.
Applied to files:
core/providers/gemini/chat.gocore/providers/anthropic/utils.gocore/providers/anthropic/responses.gocore/providers/gemini/gemini_test.gocore/providers/anthropic/types.gocore/providers/gemini/utils.gocore/providers/gemini/responses.gocore/providers/cohere/utils.go
📚 Learning: 2026-01-10T11:27:47.535Z
Learnt from: Radheshg04
Repo: maximhq/bifrost PR: 1256
File: core/providers/openai/openai.go:2276-2385
Timestamp: 2026-01-10T11:27:47.535Z
Learning: Validate image generation requests for nil and missing prompts before dispatch. Follow the same pattern used here: core/bifrost.go validates nil/empty prompts, providerUtils.CheckContextAndGetRequestBody returns a structured error when the request converter yields nil, and apply this across all providers (including OpenAI) to avoid sending null bodies.
Applied to files:
core/providers/gemini/chat.gocore/providers/anthropic/utils.gocore/providers/anthropic/responses.gocore/providers/gemini/gemini_test.gocore/providers/anthropic/types.gocore/providers/gemini/utils.gocore/providers/gemini/responses.gocore/providers/cohere/utils.go
📚 Learning: 2026-01-11T14:08:10.341Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1298
File: core/providers/anthropic/anthropic.go:682-699
Timestamp: 2026-01-11T14:08:10.341Z
Learning: In Anthroplic streaming implementations (and analogous providers), ensure that the final 'summary' chunk, which carries usage information and metadata, is emitted after all delta chunks and uses a chunk index of last_delta_index + 1. This differentiates the summary chunk from content delta chunks. Apply this convention consistently in the anthropic provider code and in similar streaming providers, and consider adding a targeted test to assert the ordering and chunk index logic.
Applied to files:
core/providers/anthropic/utils.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.go
📚 Learning: 2026-01-13T13:36:35.221Z
Learnt from: TejasGhatte
Repo: maximhq/bifrost PR: 1319
File: core/providers/anthropic/responses.go:937-937
Timestamp: 2026-01-13T13:36:35.221Z
Learning: In core/providers/anthropic/responses.go, when handling Anthropic API streaming responses, ensure that content_block_start events include a signature field set to an empty string (e.g., contentBlock.Signature = ""). The actual signature is delivered later via signature_delta events. This behavior is per Anthropic's specification and should not be treated as an error. This guideline should apply to all Anthropic response handling files under core/providers/anthropic/ and similar go files that process streaming blocks.
Applied to files:
core/providers/anthropic/utils.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.go
📚 Learning: 2025-12-17T08:44:08.788Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1114
File: core/schemas/chatcompletions.go:224-228
Timestamp: 2025-12-17T08:44:08.788Z
Learning: In core/schemas/chatcompletions.go, ensure the schema structures mirror OpenAI's API specifications exactly. Use the valid values for fields like ChatAudioParameters.Format and ChatAudioParameters.Voice as defined by OpenAI's documentation, and avoid adding additional inline documentation or constants to maintain direct compatibility with OpenAI's API.
Applied to files:
core/schemas/chatcompletions.go
📚 Learning: 2025-12-15T10:16:21.909Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/providers/huggingface/huggingface_test.go:12-63
Timestamp: 2025-12-15T10:16:21.909Z
Learning: In provider tests under core/providers/<provider>/*_test.go, do not require or flag the use of defer for Shutdown(); instead call client.Shutdown() at the end of each test function. This pattern appears consistent across all provider tests. Apply this rule only within this path; for other tests or resources, defer may still be appropriate.
Applied to files:
core/providers/gemini/gemini_test.go
📚 Learning: 2026-01-13T17:10:07.064Z
Learnt from: Radheshg04
Repo: maximhq/bifrost PR: 1312
File: tests/integrations/python/tests/test_openai.py:1166-1258
Timestamp: 2026-01-13T17:10:07.064Z
Learning: In tests under tests/integrations/python, prefer using the OpenAI image generation model 'gpt-image-1' via the config key providers.openai.image_generation for image-generation scenarios. This avoids DALLE-3 parameter limitations (e.g., n>1, quality/size combos). Ensure tests reference this provider in mocks/fixtures and document why this choice is used for test determinism.
Applied to files:
tests/integrations/python/tests/test_google.py
🧬 Code graph analysis (9)
core/schemas/responses.go (1)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(312-315)
core/providers/anthropic/utils.go (2)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(312-315)core/schemas/utils.go (1)
SafeExtractOrderedMap(549-572)
core/providers/anthropic/responses.go (2)
core/schemas/utils.go (1)
Ptr(14-16)core/providers/anthropic/types.go (3)
AnthropicStreamDelta(519-527)AnthropicStopReasonEndTurn(435-435)AnthropicStreamEvent(498-507)
core/schemas/chatcompletions.go (2)
core/schemas/json_wasm.go (2)
Marshal(8-10)Unmarshal(22-24)core/schemas/json_native.go (2)
Marshal(8-10)Unmarshal(18-20)
core/providers/gemini/gemini_test.go (2)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(312-315)core/schemas/utils.go (1)
Ptr(14-16)
core/providers/anthropic/types.go (2)
core/schemas/chatcompletions.go (1)
CacheControl(707-710)core/schemas/responses.go (1)
Citations(421-423)
tests/integrations/python/tests/test_google.py (2)
tests/integrations/python/tests/utils/common.py (1)
skip_if_no_api_key(1830-1841)core/providers/gemini/types.go (4)
Tool(647-674)Content(1016-1024)Part(1030-1054)FunctionResponse(1249-1271)
core/providers/gemini/utils.go (2)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(312-315)core/schemas/utils.go (1)
SafeExtractOrderedMap(549-572)
core/providers/cohere/utils.go (2)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(312-315)core/schemas/utils.go (1)
SafeExtractOrderedMap(549-572)
🪛 Ruff (0.14.11)
tests/integrations/python/tests/test_google.py
1682-1682: Unused method argument: test_config
(ARG002)
1750-1750: f-string without any placeholders
Remove extraneous f prefix
(F541)
1768-1768: Unused method argument: test_config
(ARG002)
1895-1895: f-string without any placeholders
Remove extraneous f prefix
(F541)
1898-1898: f-string without any placeholders
Remove extraneous f prefix
(F541)
1902-1902: Unused method argument: test_config
(ARG002)
1942-1942: f-string without any placeholders
Remove extraneous f prefix
(F541)
1954-1954: f-string without any placeholders
Remove extraneous f prefix
(F541)
1957-1957: Unused method argument: test_config
(ARG002)
2001-2001: f-string without any placeholders
Remove extraneous f prefix
(F541)
2007-2007: f-string without any placeholders
Remove extraneous f prefix
(F541)
2012-2012: Unused method argument: test_config
(ARG002)
2093-2093: f-string without any placeholders
Remove extraneous f prefix
(F541)
2095-2095: Do not catch blind exception: Exception
(BLE001)
2132-2132: Do not catch blind exception: Exception
(BLE001)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Graphite / mergeability_check
🔇 Additional comments (12)
core/changelog.md (1)
1-6: Changelog entries look consistent with the PR scope; verify no duplicates across the Graphite stack.Since
#1319is part of a stack, ensure sibling PRs (#1313, etc.) aren’t also adding the same entries (avoid double-release notes).core/providers/anthropic/responses.go (1)
934-946: Emptysignatureon thinkingcontent_block_startis correct (don’t remove).Keeping
contentBlock.Signature = schemas.Ptr("")on start aligns with Anthropic streaming behavior; the real signature arrives viasignature_delta. Based on learnings, this should remain as-is.Also applies to: 951-959, 979-984
core/providers/anthropic/types.go (2)
219-238: ExpandedAnthropicContentBlockshape looks consistent with new features (signature/citations).
248-336: Citations union type is good; verify required fields match Anthropic’s emitted schema (esp.cited_text).This is an external-contract surface (Anthropic + sonic marshaling), so please sanity-check with real responses / fixtures that
AnthropicTextCitationrequired/optional fields align (e.g.,cited_textalways present, and type-specific fields appear as expected).core/schemas/chatcompletions.go (1)
276-282: Public API shift verified—all callsites successfully migrated from*boolto*AdditionalPropertiesStruct. No remaining code treatsToolFunctionParameters.AdditionalPropertiesas a simple pointer-to-bool. All 8+ usages across providers (Anthropic, Cohere, Gemini), utilities, and tests properly initialize the union type with exactly one variant set. The implementation correctly handles JSON round-tripping: null values marshal as null and unmarshal to a struct with both fields nil.core/schemas/responses.go (2)
132-140: LGTM: Type alignment for AdditionalProperties.The change from
*boolto*AdditionalPropertiesStructcorrectly aligns this struct with the new union type that supports both boolean and map representations. This matches theAdditionalPropertiesStructdefinition incore/schemas/chatcompletions.go:311-314.
421-423: LGTM: Simple Citations type addition.The new
Citationsstruct is well-defined with proper JSON tags and optional semantics via the pointer type.core/providers/gemini/gemini_test.go (1)
558-561: LGTM: Test correctly updated for new AdditionalPropertiesStruct type.The test properly uses the new wrapper struct with
AdditionalPropertiesBoolfield, maintaining test coverage for the structured outputs conversion with the updated type system.core/providers/gemini/chat.go (4)
94-102: Acknowledged: Thought text handling withcontinue.Based on the past review discussion, TejasGhatte confirmed that
part.Text,part.Thought, andpart.ThoughtSignaturedon't appear together in the same part from the Gemini API. Thecontinuehere is intentional to skip regular text processing for thought content.
130-160: LGTM: Thought signature extraction from CallID.The logic correctly:
- Detects the embedded signature separator in CallID
- Base64-decodes the signature portion
- Uses the base ID for the tool call
- Re-encodes and stores the signature in reasoningDetails with proper type and ID linkage
318-350: LGTM: Streaming path correctly mirrors non-streaming logic.The signature extraction from CallID in the streaming path follows the same pattern as the non-streaming path, ensuring consistent behavior.
388-388: LGTM: Delta content check includes ReasoningDetails.The
hasDeltaContentcheck correctly includeslen(delta.ReasoningDetails) > 0to ensure chunks with only reasoning content are not skipped.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
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: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
core/providers/anthropic/responses.go (2)
898-924: Keep syntheticinput_json_deltaindices consistent with OutputIndex-first indexing.You now set
streamResp.IndexfrombifrostResp.OutputIndex(Line 898-924), but synthetic deltas still usebifrostResp.ContentIndex(Line 1045). If those diverge, the deltas will attach to the wrong content block.Proposed fix
- deltaEvents := generateSyntheticInputJSONDeltas(argumentsJSON, bifrostResp.ContentIndex) + // Use the same index we used for content_block_start to keep deltas aligned. + deltaEvents := generateSyntheticInputJSONDeltas(argumentsJSON, streamResp.Index) events = append(events, deltaEvents...)Also applies to: 1013-1047
1165-1192: FixStopSequenceto always emitnullinstead of empty string inmessage_delta.The
StopSequencefield inAnthropicStreamDeltalacksomitempty, sonilserializes as"stop_sequence": null. Per Anthropic's specification, when no custom stop sequence triggered the stop (e.g., for"end_turn","max_tokens"),stop_sequencemust benull, not an empty string. Currently, line 1169 initializesStopSequencetoschemas.Ptr(""), which only gets overwritten tonilifbifrostResp.Response.StopReason != nil(line 1187). IfStopReasonis unexpectedlynilfor aCompletedresponse, the empty string would escape to the client. InitializeStopSequencetonilinstead and only set it to a non-null value when an actual stop sequence was matched.core/providers/gemini/responses.go (1)
1039-1061: Fix ID mismatch: you mutatetoolUseIDafter saving it into state maps.At Line 1044-1047 you store
toolUseIDintostate.ItemIDs/state.ToolCallIDs, then at Line 1057-1061 you append the signature and overwritetoolUseID. This will cause later closures (e.g.,closeGeminiOpenItemsusingstate.ItemIDs) to emitoutput_item.donewith the old ID, not the ID used inoutput_item.added/arguments.done.Proposed fix
toolUseID := part.FunctionCall.ID if toolUseID == "" { toolUseID = part.FunctionCall.Name // Fallback to name as ID } - state.ItemIDs[outputIndex] = toolUseID - state.ToolCallIDs[outputIndex] = toolUseID - state.ToolCallNames[outputIndex] = part.FunctionCall.Name - - // Attach thought signature to ID if present - if len(part.ThoughtSignature) > 0 && !strings.Contains(toolUseID, thoughtSignatureSeparator) { - encoded := base64.StdEncoding.EncodeToString(part.ThoughtSignature) - toolUseID = fmt.Sprintf("%s%s%s", toolUseID, thoughtSignatureSeparator, encoded) - } + // Attach thought signature to ID if present + if len(part.ThoughtSignature) > 0 && !strings.Contains(toolUseID, thoughtSignatureSeparator) { + encoded := base64.StdEncoding.EncodeToString(part.ThoughtSignature) + toolUseID = fmt.Sprintf("%s%s%s", toolUseID, thoughtSignatureSeparator, encoded) + } + + state.ItemIDs[outputIndex] = toolUseID + state.ToolCallIDs[outputIndex] = toolUseID + state.ToolCallNames[outputIndex] = part.FunctionCall.Name
🤖 Fix all issues with AI agents
In `@core/providers/gemini/chat.go`:
- Around line 342-350: The streaming-path block that appends a
schemas.ChatReasoningDetails for an extracted signature (variables extractedSig,
thoughtSig, reasoningDetails) is missing the ID field; update that append to
include ID: schemas.Ptr(fmt.Sprintf("tool_call_%s", callID)) (matching the
non-streaming path) so the appended schemas.ChatReasoningDetails has Index,
Type, Signature and ID set for correlation with the tool call.
In `@tests/integrations/python/tests/test_google.py`:
- Around line 1767-1900: The test
test_30b_gemini_3_sequential_function_calls_signatures should be made
lint-clean: add assertions that response_1.candidates and response_2.candidates
are non-empty before indexing (e.g., assert response_1.candidates and assert
response_2.candidates) to avoid potential IndexErrors, remove any unnecessary
f-string prefixes where the string contains no placeholders (convert f"…" to
"…"), and replace the unused test_config parameter by adding
`@pytest.mark.usefixtures`("test_config") to the test decorator and removing the
test_config argument from the function signature; update references to
response_1/response_2 and printing strings accordingly to avoid ARG002/F541
warnings.
- Around line 2011-2140: The test test_30e_gemini_3_signature_validation_strict
currently uses broad except Exception blocks and lacks asserts on API responses;
update it to (1) assert response_1.candidates and response_2.candidates
immediately after each generate_content call to ensure candidates exist (refer
to response_1 and response_2), and (2) replace broad except Exception handlers
around the WITH/without-signature calls with either specific SDK exception types
thrown by client.models.generate_content (catch that specific exception class)
or keep the broad except but add an explicit BLE001 suppression comment (noqa)
on that except to satisfy Ruff; ensure you reference the try/except blocks that
wrap the client.models.generate_content calls and the test function name
test_30e_gemini_3_signature_validation_strict when making the changes.
- Around line 1681-1766: The test
test_30a_gemini_3_parallel_function_calls_signatures incorrectly asserts
signatures[0][0] == 0 assuming the first function_call is at part index 0;
instead find the actual index of the first function_call in
response.candidates[0].content.parts (e.g., record first_fc_index when you
append to function_calls inside the loop) and assert signatures[0][0] ==
first_fc_index so the check validates that the thought signature is on the first
function_call part regardless of leading text/thinking parts.
- Around line 1901-2010: Several print statements in
test_30c_gemini_3_thought_signatures_in_text_responses and
test_30d_gemini_3_thinking_levels use f-strings but contain no placeholders
(triggering Ruff F541); change those f-prefixed strings to plain strings. Locate
the prints in the two test functions (e.g., the " Summary:" print in test_30c
and the " ✓ Minimal thinking level (expected: minimal/no thinking)" and "
! High thinking but no thought tokens reported" prints in test_30d) and remove
the leading "f" so they become normal string literals while leaving f-strings
with actual placeholders (like prints referencing idx, level, thought_tokens,
etc.) unchanged.
♻️ Duplicate comments (2)
core/schemas/chatcompletions.go (1)
312-367: Guard against emittingadditionalProperties: nullwhen unmarshalling receives null.
AdditionalPropertiesStruct.MarshalJSON()returnsnullwhen both fields are nil. Since the field hasomitemptytag inToolFunctionParameters, a non-nil pointer with empty contents will serialize to"additionalProperties": null— which violates JSON Schema (additionalProperties must be a boolean or schema object, not null).This occurs when unmarshalling JSON with
"additionalProperties": null. The struct pointer remains non-nil with both fields cleared. Add normalization afterToolFunctionParameters.UnmarshalJSON()to collapse emptyAdditionalPropertiesStructback to nil:Normalization code
func (t *ToolFunctionParameters) UnmarshalJSON(data []byte) error { // ... existing unmarshal logic ... *t = ToolFunctionParameters(temp) + if t.AdditionalProperties != nil && + t.AdditionalProperties.AdditionalPropertiesBool == nil && + t.AdditionalProperties.AdditionalPropertiesMap == nil { + t.AdditionalProperties = nil + } return nil }Apply the same normalization pattern in any other types that use
AdditionalPropertiesStructwith theomitemptytag (e.g., inresponses.go).core/providers/gemini/responses.go (1)
1057-1061: (Already raised before) Confirm base64 encoding is valid for Gemini function-call IDs.You’re appending
base64.StdEncodingintoCallID(Line 1059-1061, 1868-1870), which introduces+,/,=. If Gemini enforces an ID charset/length constraint, this will break tool calls.Gemini API FunctionCall.id / tool call id constraints: allowed characters and max length. Is it safe to embed standard base64 (+/=) into the ID?Also applies to: 1866-1870
🧹 Nitpick comments (5)
core/providers/cohere/utils.go (1)
75-86: Preferelse if+ avoid shadowing foradditionalPropertiesextraction.This is clearer and prevents accidental overwrite if
SafeExtractOrderedMapever starts accepting more input kinds.Proposed diff
// Extract additionalProperties if addPropsVal, ok := paramsMap["additionalProperties"].(bool); ok { result.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ AdditionalPropertiesBool: &addPropsVal, } - } - - if addPropsVal, ok := schemas.SafeExtractOrderedMap(paramsMap["additionalProperties"]); ok { + } else if addPropsMap, ok := schemas.SafeExtractOrderedMap(paramsMap["additionalProperties"]); ok { result.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ - AdditionalPropertiesMap: &addPropsVal, + AdditionalPropertiesMap: &addPropsMap, } }core/schemas/responses.go (1)
421-424:Citationstype is fine, but the name is very generic inschemas.If this is Responses-specific config, consider a more specific exported name (to avoid future collisions like
ResponsesCitations/CitationsConfig).core/providers/anthropic/utils.go (1)
768-778: Useelse iffor mutually exclusive conditions.The two branches handling
additionalProperties(boolean vs. map) are mutually exclusive, but they're written as sequentialifstatements. This works correctly becauseSafeExtractOrderedMapwill returnfalsefor a boolean value, but usingelse ifmakes the intent clearer and avoids the unnecessary type check when the boolean branch succeeds.♻️ Suggested improvement
if additionalProps, ok := schemaMap["additionalProperties"].(bool); ok { jsonSchema.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ AdditionalPropertiesBool: &additionalProps, } - } - - if additionalProps, ok := schemas.SafeExtractOrderedMap(schemaMap["additionalProperties"]); ok { + } else if additionalProps, ok := schemas.SafeExtractOrderedMap(schemaMap["additionalProperties"]); ok { jsonSchema.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ AdditionalPropertiesMap: &additionalProps, } }core/providers/gemini/chat.go (1)
360-368: Standalone ThoughtSignature in streaming also lacks ID correlation.Similar to the CallID extraction path, standalone ThoughtSignature handling in streaming doesn't set the
IDfield, unlike the non-streaming counterpart at lines 177-202. Consider adding ID tracking for consistency if the signature relates to a tool call.core/providers/gemini/utils.go (1)
1145-1155: Useelse iffor mutually exclusive conditions.Same pattern issue as in
core/providers/anthropic/utils.go- the boolean and map branches are mutually exclusive but written as sequentialifstatements.♻️ Suggested improvement
// Extract additionalProperties if additionalProps, ok := normalizedSchemaMap["additionalProperties"].(bool); ok { jsonSchema.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ AdditionalPropertiesBool: &additionalProps, } - } - - if additionalProps, ok := schemas.SafeExtractOrderedMap(normalizedSchemaMap["additionalProperties"]); ok { + } else if additionalProps, ok := schemas.SafeExtractOrderedMap(normalizedSchemaMap["additionalProperties"]); ok { jsonSchema.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ AdditionalPropertiesMap: &additionalProps, } }
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (16)
core/changelog.mdcore/internal/testutil/structured_outputs.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.gocore/providers/anthropic/utils.gocore/providers/cohere/utils.gocore/providers/gemini/chat.gocore/providers/gemini/gemini_test.gocore/providers/gemini/responses.gocore/providers/gemini/types.gocore/providers/gemini/utils.gocore/schemas/chatcompletions.gocore/schemas/responses.gotests/integrations/python/tests/test_google.pytransports/bifrost-http/integrations/router.gotransports/changelog.md
💤 Files with no reviewable changes (1)
- transports/bifrost-http/integrations/router.go
🚧 Files skipped from review as they are similar to previous changes (2)
- core/providers/gemini/types.go
- transports/changelog.md
🧰 Additional context used
📓 Path-based instructions (1)
**
⚙️ CodeRabbit configuration file
always check the stack if there is one for the current PR. do not give localized reviews for the PR, always see all changes in the light of the whole stack of PRs (if there is a stack, if there is no stack you can continue to make localized suggestions/reviews)
Files:
core/providers/gemini/chat.gocore/providers/gemini/utils.gocore/providers/anthropic/responses.gocore/providers/anthropic/utils.gocore/providers/cohere/utils.gocore/providers/gemini/gemini_test.gocore/schemas/responses.gocore/internal/testutil/structured_outputs.gotests/integrations/python/tests/test_google.pycore/schemas/chatcompletions.gocore/changelog.mdcore/providers/anthropic/types.gocore/providers/gemini/responses.go
🧠 Learnings (11)
📚 Learning: 2025-12-09T17:07:42.007Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/schemas/account.go:9-18
Timestamp: 2025-12-09T17:07:42.007Z
Learning: In core/schemas/account.go, the HuggingFaceKeyConfig field within the Key struct is currently unused and reserved for future Hugging Face inference endpoint deployments. Do not flag this field as missing from OpenAPI documentation or require its presence in the API spec until the feature is actively implemented and used. When the feature is added, update the OpenAPI docs accordingly; otherwise, treat this field as non-breaking and not part of the current API surface.
Applied to files:
core/providers/gemini/chat.gocore/providers/gemini/utils.gocore/providers/anthropic/responses.gocore/providers/anthropic/utils.gocore/providers/cohere/utils.gocore/providers/gemini/gemini_test.gocore/schemas/responses.gocore/internal/testutil/structured_outputs.gocore/schemas/chatcompletions.gocore/providers/anthropic/types.gocore/providers/gemini/responses.go
📚 Learning: 2025-12-29T11:54:55.836Z
Learnt from: akshaydeo
Repo: maximhq/bifrost PR: 1153
File: framework/configstore/rdb.go:2221-2246
Timestamp: 2025-12-29T11:54:55.836Z
Learning: In Go reviews, do not flag range-over-int patterns like for i := range n as compile-time errors, assuming Go 1.22+ semantics. Only flag actual range-capable values (slices, arrays, maps, channels, strings) and other compile-time issues. This applies to all Go files across the repository.
Applied to files:
core/providers/gemini/chat.gocore/providers/gemini/utils.gocore/providers/anthropic/responses.gocore/providers/anthropic/utils.gocore/providers/cohere/utils.gocore/providers/gemini/gemini_test.gocore/schemas/responses.gocore/internal/testutil/structured_outputs.gocore/schemas/chatcompletions.gocore/providers/anthropic/types.gocore/providers/gemini/responses.go
📚 Learning: 2026-01-14T04:40:11.480Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1312
File: framework/modelcatalog/pricing.go:276-426
Timestamp: 2026-01-14T04:40:11.480Z
Learning: In the Bifrost codebase, ImageUsage and other usage types guarantee that TotalTokens is populated (computed as InputTokens + OutputTokens if providers don’t supply TotalTokens). Reviewers can rely on this invariant and should not assume TotalTokens may be missing when input/output tokens exist. When implementing tiering logic or token-based decisions, you can safely use TotalTokens without extra null/zero guards, provided you’re in a context where InputTokens and OutputTokens are present. If a branch might discard tokens, ensure the invariant is preserved or add explicit checks only where the inputs are confirmed to be valid.
Applied to files:
core/providers/gemini/chat.gocore/providers/gemini/utils.gocore/providers/anthropic/responses.gocore/providers/anthropic/utils.gocore/providers/cohere/utils.gocore/providers/gemini/gemini_test.gocore/schemas/responses.gocore/internal/testutil/structured_outputs.gocore/schemas/chatcompletions.gocore/providers/anthropic/types.gocore/providers/gemini/responses.go
📚 Learning: 2025-12-19T09:26:54.961Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/providers/utils/utils.go:1050-1051
Timestamp: 2025-12-19T09:26:54.961Z
Learning: Update streaming end-marker handling so HuggingFace is treated as a non-[DONE] provider for backends that do not emit a DONE marker (e.g., meta llama on novita). In core/providers/utils/utils.go, adjust ProviderSendsDoneMarker() (or related logic) to detect providers that may not emit DONE and avoid relying on DONE as the sole end signal. Add tests to cover both DONE-emitting and non-DONE backends, with clear documentation in code comments explaining the rationale and any fallback behavior.
Applied to files:
core/providers/gemini/chat.gocore/providers/gemini/utils.gocore/providers/anthropic/responses.gocore/providers/anthropic/utils.gocore/providers/cohere/utils.gocore/providers/gemini/gemini_test.gocore/providers/anthropic/types.gocore/providers/gemini/responses.go
📚 Learning: 2026-01-10T11:27:47.535Z
Learnt from: Radheshg04
Repo: maximhq/bifrost PR: 1256
File: core/providers/openai/openai.go:2276-2385
Timestamp: 2026-01-10T11:27:47.535Z
Learning: Validate image generation requests for nil and missing prompts before dispatch. Follow the same pattern used here: core/bifrost.go validates nil/empty prompts, providerUtils.CheckContextAndGetRequestBody returns a structured error when the request converter yields nil, and apply this across all providers (including OpenAI) to avoid sending null bodies.
Applied to files:
core/providers/gemini/chat.gocore/providers/gemini/utils.gocore/providers/anthropic/responses.gocore/providers/anthropic/utils.gocore/providers/cohere/utils.gocore/providers/gemini/gemini_test.gocore/providers/anthropic/types.gocore/providers/gemini/responses.go
📚 Learning: 2026-01-13T13:36:35.221Z
Learnt from: TejasGhatte
Repo: maximhq/bifrost PR: 1319
File: core/providers/anthropic/responses.go:937-937
Timestamp: 2026-01-13T13:36:35.221Z
Learning: In core/providers/anthropic/responses.go, when handling Anthropic API streaming responses, ensure that content_block_start events include a signature field set to an empty string (e.g., contentBlock.Signature = ""). The actual signature is delivered later via signature_delta events. This behavior is per Anthropic's specification and should not be treated as an error. This guideline should apply to all Anthropic response handling files under core/providers/anthropic/ and similar go files that process streaming blocks.
Applied to files:
core/providers/anthropic/responses.gocore/providers/anthropic/utils.gocore/providers/anthropic/types.go
📚 Learning: 2026-01-11T14:08:10.341Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1298
File: core/providers/anthropic/anthropic.go:682-699
Timestamp: 2026-01-11T14:08:10.341Z
Learning: In Anthroplic streaming implementations (and analogous providers), ensure that the final 'summary' chunk, which carries usage information and metadata, is emitted after all delta chunks and uses a chunk index of last_delta_index + 1. This differentiates the summary chunk from content delta chunks. Apply this convention consistently in the anthropic provider code and in similar streaming providers, and consider adding a targeted test to assert the ordering and chunk index logic.
Applied to files:
core/providers/anthropic/responses.gocore/providers/anthropic/utils.gocore/providers/anthropic/types.go
📚 Learning: 2025-12-15T10:16:21.909Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/providers/huggingface/huggingface_test.go:12-63
Timestamp: 2025-12-15T10:16:21.909Z
Learning: In provider tests under core/providers/<provider>/*_test.go, do not require or flag the use of defer for Shutdown(); instead call client.Shutdown() at the end of each test function. This pattern appears consistent across all provider tests. Apply this rule only within this path; for other tests or resources, defer may still be appropriate.
Applied to files:
core/providers/gemini/gemini_test.go
📚 Learning: 2025-12-19T08:29:20.286Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1095
File: core/internal/testutil/count_tokens.go:30-67
Timestamp: 2025-12-19T08:29:20.286Z
Learning: In core/internal/testutil test files, enforce using GetTestRetryConfigForScenario() to obtain a generic retry config, then construct a typed retry config (e.g., CountTokensRetryConfig, EmbeddingRetryConfig, TranscriptionRetryConfig) with an empty Conditions slice. Copy only MaxAttempts, BaseDelay, MaxDelay, OnRetry, and OnFinalFail from the generic config. This convention should be consistently applied across all test files in this directory.
Applied to files:
core/internal/testutil/structured_outputs.go
📚 Learning: 2026-01-13T17:10:07.064Z
Learnt from: Radheshg04
Repo: maximhq/bifrost PR: 1312
File: tests/integrations/python/tests/test_openai.py:1166-1258
Timestamp: 2026-01-13T17:10:07.064Z
Learning: In tests under tests/integrations/python, prefer using the OpenAI image generation model 'gpt-image-1' via the config key providers.openai.image_generation for image-generation scenarios. This avoids DALLE-3 parameter limitations (e.g., n>1, quality/size combos). Ensure tests reference this provider in mocks/fixtures and document why this choice is used for test determinism.
Applied to files:
tests/integrations/python/tests/test_google.py
📚 Learning: 2025-12-17T08:44:08.788Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1114
File: core/schemas/chatcompletions.go:224-228
Timestamp: 2025-12-17T08:44:08.788Z
Learning: In core/schemas/chatcompletions.go, ensure the schema structures mirror OpenAI's API specifications exactly. Use the valid values for fields like ChatAudioParameters.Format and ChatAudioParameters.Voice as defined by OpenAI's documentation, and avoid adding additional inline documentation or constants to maintain direct compatibility with OpenAI's API.
Applied to files:
core/schemas/chatcompletions.go
🧬 Code graph analysis (9)
core/providers/gemini/chat.go (3)
core/schemas/chatcompletions.go (3)
ChatReasoningDetails(857-865)BifrostReasoningDetailsTypeText(853-853)BifrostReasoningDetailsTypeEncrypted(852-852)core/schemas/utils.go (1)
Ptr(14-16)ui/lib/types/logs.ts (1)
ReasoningDetails(127-134)
core/providers/gemini/utils.go (2)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(312-315)core/schemas/utils.go (1)
SafeExtractOrderedMap(549-572)
core/providers/anthropic/responses.go (3)
core/schemas/utils.go (1)
Ptr(14-16)core/providers/anthropic/types.go (2)
AnthropicStreamDelta(519-527)AnthropicStreamEvent(498-507)core/providers/anthropic/utils.go (1)
ConvertBifrostFinishReasonToAnthropic(88-93)
core/providers/anthropic/utils.go (2)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(312-315)core/schemas/utils.go (1)
SafeExtractOrderedMap(549-572)
core/providers/cohere/utils.go (2)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(312-315)core/schemas/utils.go (1)
SafeExtractOrderedMap(549-572)
core/providers/gemini/gemini_test.go (2)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(312-315)core/schemas/utils.go (1)
Ptr(14-16)
core/schemas/responses.go (1)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(312-315)
core/internal/testutil/structured_outputs.go (2)
core/providers/gemini/types.go (1)
Type(825-825)core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(312-315)
core/providers/anthropic/types.go (2)
core/schemas/chatcompletions.go (1)
CacheControl(707-710)core/schemas/responses.go (1)
Citations(421-423)
🪛 Ruff (0.14.11)
tests/integrations/python/tests/test_google.py
1682-1682: Unused method argument: test_config
(ARG002)
1750-1750: f-string without any placeholders
Remove extraneous f prefix
(F541)
1768-1768: Unused method argument: test_config
(ARG002)
1895-1895: f-string without any placeholders
Remove extraneous f prefix
(F541)
1898-1898: f-string without any placeholders
Remove extraneous f prefix
(F541)
1902-1902: Unused method argument: test_config
(ARG002)
1942-1942: f-string without any placeholders
Remove extraneous f prefix
(F541)
1954-1954: f-string without any placeholders
Remove extraneous f prefix
(F541)
1957-1957: Unused method argument: test_config
(ARG002)
2001-2001: f-string without any placeholders
Remove extraneous f prefix
(F541)
2007-2007: f-string without any placeholders
Remove extraneous f prefix
(F541)
2012-2012: Unused method argument: test_config
(ARG002)
2093-2093: f-string without any placeholders
Remove extraneous f prefix
(F541)
2095-2095: Do not catch blind exception: Exception
(BLE001)
2132-2132: Do not catch blind exception: Exception
(BLE001)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Graphite / mergeability_check
🔇 Additional comments (10)
core/changelog.md (1)
1-6: Changelog entries look consistent with the PR’s intent.Given this PR is in a stack, just ensure these bullets won’t be duplicated/conflicting with adjacent PR changelogs in the stack merge.
core/schemas/chatcompletions.go (1)
276-308: ToolFunctionParameters: migration + xAI string-JSON support looks coherent.Good direction for provider compatibility (bool-or-object additionalProperties + parameters-as-string).
core/schemas/responses.go (1)
132-140: ResponsesTextConfigFormatJSONSchema: AdditionalPropertiesStruct adoption is consistent with the schema migration.Just ensure downstream provider builders populate this as either bool or object and avoid allocating an empty union struct (to prevent
additionalProperties: null).core/providers/gemini/gemini_test.go (1)
558-561: LGTM!The test correctly uses the new
AdditionalPropertiesStructtype withAdditionalPropertiesBoolto represent the booleanfalsevalue, aligning with the updated schema definition incore/schemas/chatcompletions.go.core/providers/gemini/chat.go (2)
94-102: Thought text handling correctly separates reasoning from content.The logic properly routes thought/reasoning text to
reasoningDetailswith typeBifrostReasoningDetailsTypeTextinstead of mixing it with regular content blocks. Thecontinueensures subsequent content processing is skipped for thought blocks.Based on the previous discussion where the developer confirmed that
part.Thought,part.Text, andpart.ThoughtSignaturedon't appear together in the same part, this implementation is correct.
388-391: LGTM!Good update to include
ReasoningDetailsin the delta content check, ensuring streaming chunks with only reasoning content are properly emitted.core/internal/testutil/structured_outputs.go (2)
421-426: LGTM!The test correctly constructs
AdditionalPropertiesStructwith the boolean value wrapped inAdditionalPropertiesBool, consistent with the schema changes across the codebase.
537-542: LGTM!Streaming test correctly mirrors the non-streaming test's usage of
AdditionalPropertiesStruct.core/providers/anthropic/responses.go (1)
934-984: Emptysignatureinit for thinking blocks oncontent_block_startis correct (Line 937, 953, 983).This matches Anthropic streaming behavior: signature arrives later via
signature_delta. Based on learnings, this should not be “optimized away”.core/providers/anthropic/types.go (1)
248-336: Citations union (configvs array) marshal/unmarshal looks solid.Mutual exclusivity checks + clearing the opposite field in
UnmarshalJSON(Line 322-333) prevents stale-state issues on reused instances.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
a4107d0 to
b0cf7c7
Compare
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 (1)
core/providers/gemini/responses.go (1)
2404-2415: Logic inconsistency: Look-ahead should be conditional.This block unconditionally checks the next message and overrides any thought signature already extracted from
CallID. This differs from the similar logic at lines 269-286 which uses anelseclause to only look ahead when no signature was found in the CallID.Consider making this conditional to maintain consistency:
🔧 Suggested fix
+ // Only look ahead if we didn't find a signature in the CallID + if len(part.ThoughtSignature) == 0 { // Preserve thought signature from ResponsesReasoning message (required for Gemini 3 Pro) // Look ahead to see if the next message is a reasoning message with encrypted content if i+1 < len(messages) { nextMsg := messages[i+1] if nextMsg.Type != nil && *nextMsg.Type == schemas.ResponsesMessageTypeReasoning && nextMsg.ResponsesReasoning != nil && nextMsg.ResponsesReasoning.EncryptedContent != nil { decodedSig, err := base64.StdEncoding.DecodeString(*nextMsg.ResponsesReasoning.EncryptedContent) if err == nil { part.ThoughtSignature = decodedSig } } } + }
🤖 Fix all issues with AI agents
In `@core/providers/anthropic/responses.go`:
- Around line 919-924: The code chooses streamResp.Index using OutputIndex first
but the synthetic input_json_delta events still use bifrostResp.ContentIndex,
causing mismatched indices; compute a single chosenIndex (prefer
bifrostResp.OutputIndex, fallback to bifrostResp.ContentIndex), assign it to
streamResp.Index, and use that same chosenIndex when emitting synthetic events
(e.g., input_json_delta/content_block_delta) so content_block_start and
subsequent deltas share the identical index; apply the same change at the other
occurrence mentioned (around the 1043–1047 block).
In `@core/providers/gemini/responses.go`:
- Around line 2395-2402: The code decodes a tool signature using
base64.StdEncoding.DecodeString but the signature was URL-safe encoded; replace
the decoder call to use base64.RawURLEncoding (e.g.,
base64.RawURLEncoding.DecodeString(thoughtSig)) when assigning
part.ThoughtSignature and keep the existing error handling (clearing thoughtSig
on decode error) intact so signatures encoded without padding or URL-safe
characters are decoded correctly.
- Around line 1867-1871: The code appends a base64 signature to callID using
base64.StdEncoding.EncodeToString which produces '+', '/', '=' characters that
violate Gemini tool-ID constraints; change the encoding to a URL-safe variant
(e.g., base64.RawURLEncoding.EncodeToString) when constructing thoughtSig from
part.ThoughtSignature so the resulting callID (combined with
thoughtSignatureSeparator) contains only URL-safe characters, matching the
streaming-path fix that handled the same issue.
♻️ Duplicate comments (7)
core/schemas/chatcompletions.go (1)
324-379: Avoid emittingadditionalProperties: nullwhen callers construct an “empty” struct.
AdditionalPropertiesStruct.MarshalJSON()returnsnullwhen both fields are nil; if any code setsToolFunctionParameters.AdditionalProperties = &AdditionalPropertiesStruct{}, JSON becomes"additionalProperties": null(likely invalid JSON Schema).One defensive option
func (a AdditionalPropertiesStruct) MarshalJSON() ([]byte, error) { @@ - // If both are nil, return null - return Marshal(nil) + // If both are nil, callers should omit the field (set pointer to nil). + return nil, fmt.Errorf("additionalProperties cannot be null; omit the field instead") }tests/integrations/python/tests/test_google.py (2)
1771-1904: Add candidate/content guards before indexing intest_30b_*.
This currently indexesresponse_1.candidates[0]/response_2.candidates[0]without asserting candidates exist; failures will show up asIndexErrorinstead of a useful assertion message. Alsotest_configis unused (ARG002).Proposed diff
`@skip_if_no_api_key`("gemini") -def test_30b_gemini_3_sequential_function_calls_signatures(self, test_config): +@pytest.mark.usefixtures("test_config") +def test_30b_gemini_3_sequential_function_calls_signatures(self): @@ response_1 = client.models.generate_content( @@ ) + assert response_1.candidates, "Response should have candidates" @@ for part in response_1.candidates[0].content.parts: @@ response_2 = client.models.generate_content( @@ ) + assert response_2.candidates, "Response should have candidates"
2015-2144: Fix unusedtest_configparameter + add candidates guard + replace broad exception handlers.
Unused
test_configparameter (ARG002): Use@pytest.mark.usefixtures("test_config")and remove it from the method signature, or rename to_test_config.Missing candidates guard: Add
assert response_1.candidates, "Response should have candidates"before line 2047 where the code accessesresponse_1.candidates[0].content.parts, to prevent IndexError if the response has no candidates.Broad
except Exceptionhandlers (BLE001) at lines 2099 and 2136: Sincegoogle.api_core.exceptionsis not imported, either:
- Import and catch specific exception types:
from google.api_core import exceptionsand useexcept (exceptions.InvalidArgument, ValueError) as e:- Or add
# noqa: BLE001if intentionally catching all exceptions in integration tests.For HTTP 400 validation errors, the SDK raises
google.api_core.exceptions.InvalidArgument; for client-side validation failures, it raisesValueErrororTypeError.Proposed diff (shape)
`@skip_if_no_api_key`("gemini") -def test_30e_gemini_3_signature_validation_strict(self, test_config): +@pytest.mark.usefixtures("test_config") +def test_30e_gemini_3_signature_validation_strict(self): @@ response_1 = client.models.generate_content( @@ ) + assert response_1.candidates, "Response should have candidates" @@ for part in response_1.candidates[0].content.parts: @@ - except Exception as e: + except (ValueError, TypeError) as e: # or import google.api_core.exceptions pytest.fail(f"Request with signature should not fail: {e}") @@ - except Exception as e: + except (ValueError, TypeError) as e: error_msg = str(e).lower()core/providers/gemini/chat.go (1)
342-350: Missing ID field in streaming path signature extraction.The non-streaming path (lines 154-159) sets
ID: schemas.Ptr(fmt.Sprintf("tool_call_%s", callID))for reasoning details extracted from CallID, but this streaming path doesn't set theIDfield. This inconsistency could make it harder to correlate signatures with their corresponding tool calls in streaming responses.🔧 Suggested fix
// If we extracted a signature from CallID, add it to reasoning details if len(extractedSig) > 0 { thoughtSig := base64.StdEncoding.EncodeToString(extractedSig) reasoningDetails = append(reasoningDetails, schemas.ChatReasoningDetails{ Index: len(reasoningDetails), Type: schemas.BifrostReasoningDetailsTypeEncrypted, Signature: &thoughtSig, + ID: schemas.Ptr(fmt.Sprintf("tool_call_%s", callID)), }) }core/providers/gemini/responses.go (3)
1057-1061: Critical: Use URL-safe base64 encoding for tool IDs.Gemini tool IDs must match
^[a-zA-Z0-9_-]{1,64}$. Usingbase64.StdEncodingproduces+,/, and=characters that will cause Gemini to reject these tool IDs. Usebase64.RawURLEncodingwhich produces only URL-safe characters (-and_).🔧 Suggested fix
// Attach thought signature to ID if present if len(part.ThoughtSignature) > 0 && !strings.Contains(toolUseID, thoughtSignatureSeparator) { - encoded := base64.StdEncoding.EncodeToString(part.ThoughtSignature) + encoded := base64.RawURLEncoding.EncodeToString(part.ThoughtSignature) toolUseID = fmt.Sprintf("%s%s%s", toolUseID, thoughtSignatureSeparator, encoded) }
457-477: Use URL-safe base64 decoding for signatures extracted from tool IDs.The signature in
CallIDis encoded using base64 for embedding in tool IDs. Since tool IDs require URL-safe characters, the decoding should usebase64.RawURLEncodingto match the encoding scheme.🔧 Suggested fix
if thoughtSig != "" { - if decodedSig, err := base64.StdEncoding.DecodeString(thoughtSig); err == nil { + if decodedSig, err := base64.RawURLEncoding.DecodeString(thoughtSig); err == nil { functionCallPart.ThoughtSignature = decodedSig } }
246-262: Use URL-safe base64 decoding to match encoding used for tool IDs.Per Gemini's tool ID constraints (
^[a-zA-Z0-9_-]{1,64}$), signatures embedded in tool IDs must be URL-safe encoded. The decoding here should usebase64.RawURLEncodingto match, otherwise it will fail when receiving properly URL-safe encoded signatures.🔧 Suggested fix
- if decodedSig, err := base64.StdEncoding.DecodeString(parts[1]); err == nil { + if decodedSig, err := base64.RawURLEncoding.DecodeString(parts[1]); err == nil {
🧹 Nitpick comments (8)
tests/integrations/python/tests/test_google.py (3)
1681-1770: Reduce flake risk + fix unused fixture arg intest_30a_*.
test_configis unused (Ruff ARG002). Prefer@pytest.mark.usefixtures("test_config")and drop the arg, or rename to_test_config.- If the model returns 0 function calls, the current else-branch fails with a misleading “should have thought signature” assertion; explicitly assert/guard
len(function_calls) > 0(orpytest.skip(...)if Gemini didn’t tool-call).Proposed diff
`@skip_if_no_api_key`("gemini") -def test_30a_gemini_3_parallel_function_calls_signatures(self, test_config): +@pytest.mark.usefixtures("test_config") +def test_30a_gemini_3_parallel_function_calls_signatures(self): @@ # Validate parallel function calls assert response.candidates, "Response should have candidates" @@ for idx, part in enumerate(response.candidates[0].content.parts): @@ - # According to Gemini docs: only the FIRST function call should have the signature + assert len(function_calls) > 0, "Expected at least one tool call; model may have answered without tools" + # According to Gemini docs: only the FIRST function call should have the signature if len(function_calls) > 1: @@ - else: + else: print(f" Only {len(function_calls)} function call(s) made") # If only one call, it should still have a signature assert len(signatures) > 0, "Function call should have thought signature"
1905-1960: Consider asserting at least one of thought/text parts (test currently mostly informational).
Right now this will pass even if the model returns an empty/odd structure as long ascandidatesexists; if that’s intentional, OK, but otherwise add a minimal invariant like “has any thought/text part”.
1961-2014: Consider minimal invariants per thinking level (or explicitly mark as observational).
As written, this doesn’t assert anything about level differences; if this is meant to validate behavior changes across the stack, consider at least one stable assertion (or convert topytest.skip-style observational logging).core/providers/cohere/utils.go (1)
75-86: Preferelse iffor the map case (clear mutual exclusivity).
Right now the map block can overwrite the bool block (even if unlikely);else ifmakes intent explicit.Proposed diff
// Extract additionalProperties if addPropsVal, ok := paramsMap["additionalProperties"].(bool); ok { result.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ AdditionalPropertiesBool: &addPropsVal, } - } - - if addPropsVal, ok := schemas.SafeExtractOrderedMap(paramsMap["additionalProperties"]); ok { + } else if addPropsVal, ok := schemas.SafeExtractOrderedMap(paramsMap["additionalProperties"]); ok { result.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ AdditionalPropertiesMap: &addPropsVal, } }core/providers/gemini/types.go (1)
22-24: Delimiter constant looks fine; consider guarding against ID collisions.If tool IDs can contain
"_ts_", decoding may split incorrectly. Consider centralizing encode/decode helpers that either escape the base ID or use an unambiguous encoding scheme (e.g., always append base64 signature with a fixed prefix length).core/providers/gemini/gemini_test.go (1)
558-562: Test update matches the newAdditionalPropertiesStructAPI.One small improvement: assert that the converted Gemini
responseJsonSchemastill hasadditionalProperties: falseto ensure the wrapper is being serialized/mapped as intended (and not silently dropped).core/providers/anthropic/utils.go (1)
768-778: Consider usingelse iffor mutual exclusivity clarity.The current two separate
ifblocks work correctly becauseSafeExtractOrderedMapreturnsfalsefor boolean values. However, usingelse ifwould make the mutual exclusivity explicit and slightly more efficient by avoiding the second type check when the first succeeds.♻️ Optional: Use else-if for clarity
if additionalProps, ok := schemaMap["additionalProperties"].(bool); ok { jsonSchema.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ AdditionalPropertiesBool: &additionalProps, } - } - - if additionalProps, ok := schemas.SafeExtractOrderedMap(schemaMap["additionalProperties"]); ok { + } else if additionalProps, ok := schemas.SafeExtractOrderedMap(schemaMap["additionalProperties"]); ok { jsonSchema.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ AdditionalPropertiesMap: &additionalProps, } }core/providers/gemini/utils.go (1)
1145-1155: AdditionalProperties handling matches cross-provider pattern.The dual-path handling for boolean vs. map AdditionalProperties is consistent with the Anthropic provider implementation. Same optional suggestion applies: consider using
else iffor explicit mutual exclusivity.♻️ Optional: Use else-if for clarity (same as Anthropic)
if additionalProps, ok := normalizedSchemaMap["additionalProperties"].(bool); ok { jsonSchema.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ AdditionalPropertiesBool: &additionalProps, } - } - - if additionalProps, ok := schemas.SafeExtractOrderedMap(normalizedSchemaMap["additionalProperties"]); ok { + } else if additionalProps, ok := schemas.SafeExtractOrderedMap(normalizedSchemaMap["additionalProperties"]); ok { jsonSchema.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ AdditionalPropertiesMap: &additionalProps, } }
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (17)
core/changelog.mdcore/internal/testutil/structured_outputs.gocore/providers/anthropic/chat.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.gocore/providers/anthropic/utils.gocore/providers/cohere/utils.gocore/providers/gemini/chat.gocore/providers/gemini/gemini_test.gocore/providers/gemini/responses.gocore/providers/gemini/types.gocore/providers/gemini/utils.gocore/schemas/chatcompletions.gocore/schemas/responses.gotests/integrations/python/tests/test_google.pytransports/bifrost-http/integrations/router.gotransports/changelog.md
💤 Files with no reviewable changes (1)
- transports/bifrost-http/integrations/router.go
🚧 Files skipped from review as they are similar to previous changes (2)
- transports/changelog.md
- core/changelog.md
🧰 Additional context used
📓 Path-based instructions (1)
**
⚙️ CodeRabbit configuration file
always check the stack if there is one for the current PR. do not give localized reviews for the PR, always see all changes in the light of the whole stack of PRs (if there is a stack, if there is no stack you can continue to make localized suggestions/reviews)
Files:
core/providers/gemini/gemini_test.gocore/providers/gemini/chat.gocore/providers/anthropic/chat.gocore/providers/anthropic/responses.gocore/providers/gemini/types.gocore/providers/anthropic/utils.gotests/integrations/python/tests/test_google.pycore/providers/gemini/utils.gocore/schemas/chatcompletions.gocore/internal/testutil/structured_outputs.gocore/schemas/responses.gocore/providers/gemini/responses.gocore/providers/cohere/utils.gocore/providers/anthropic/types.go
🧠 Learnings (12)
📚 Learning: 2025-12-09T17:07:42.007Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/schemas/account.go:9-18
Timestamp: 2025-12-09T17:07:42.007Z
Learning: In core/schemas/account.go, the HuggingFaceKeyConfig field within the Key struct is currently unused and reserved for future Hugging Face inference endpoint deployments. Do not flag this field as missing from OpenAPI documentation or require its presence in the API spec until the feature is actively implemented and used. When the feature is added, update the OpenAPI docs accordingly; otherwise, treat this field as non-breaking and not part of the current API surface.
Applied to files:
core/providers/gemini/gemini_test.gocore/providers/gemini/chat.gocore/providers/anthropic/chat.gocore/providers/anthropic/responses.gocore/providers/gemini/types.gocore/providers/anthropic/utils.gocore/providers/gemini/utils.gocore/schemas/chatcompletions.gocore/internal/testutil/structured_outputs.gocore/schemas/responses.gocore/providers/gemini/responses.gocore/providers/cohere/utils.gocore/providers/anthropic/types.go
📚 Learning: 2025-12-29T11:54:55.836Z
Learnt from: akshaydeo
Repo: maximhq/bifrost PR: 1153
File: framework/configstore/rdb.go:2221-2246
Timestamp: 2025-12-29T11:54:55.836Z
Learning: In Go reviews, do not flag range-over-int patterns like for i := range n as compile-time errors, assuming Go 1.22+ semantics. Only flag actual range-capable values (slices, arrays, maps, channels, strings) and other compile-time issues. This applies to all Go files across the repository.
Applied to files:
core/providers/gemini/gemini_test.gocore/providers/gemini/chat.gocore/providers/anthropic/chat.gocore/providers/anthropic/responses.gocore/providers/gemini/types.gocore/providers/anthropic/utils.gocore/providers/gemini/utils.gocore/schemas/chatcompletions.gocore/internal/testutil/structured_outputs.gocore/schemas/responses.gocore/providers/gemini/responses.gocore/providers/cohere/utils.gocore/providers/anthropic/types.go
📚 Learning: 2026-01-14T04:40:11.480Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1312
File: framework/modelcatalog/pricing.go:276-426
Timestamp: 2026-01-14T04:40:11.480Z
Learning: In the Bifrost codebase, ImageUsage and other usage types guarantee that TotalTokens is populated (computed as InputTokens + OutputTokens if providers don’t supply TotalTokens). Reviewers can rely on this invariant and should not assume TotalTokens may be missing when input/output tokens exist. When implementing tiering logic or token-based decisions, you can safely use TotalTokens without extra null/zero guards, provided you’re in a context where InputTokens and OutputTokens are present. If a branch might discard tokens, ensure the invariant is preserved or add explicit checks only where the inputs are confirmed to be valid.
Applied to files:
core/providers/gemini/gemini_test.gocore/providers/gemini/chat.gocore/providers/anthropic/chat.gocore/providers/anthropic/responses.gocore/providers/gemini/types.gocore/providers/anthropic/utils.gocore/providers/gemini/utils.gocore/schemas/chatcompletions.gocore/internal/testutil/structured_outputs.gocore/schemas/responses.gocore/providers/gemini/responses.gocore/providers/cohere/utils.gocore/providers/anthropic/types.go
📚 Learning: 2025-12-15T10:16:21.909Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/providers/huggingface/huggingface_test.go:12-63
Timestamp: 2025-12-15T10:16:21.909Z
Learning: In provider tests under core/providers/<provider>/*_test.go, do not require or flag the use of defer for Shutdown(); instead call client.Shutdown() at the end of each test function. This pattern appears consistent across all provider tests. Apply this rule only within this path; for other tests or resources, defer may still be appropriate.
Applied to files:
core/providers/gemini/gemini_test.go
📚 Learning: 2025-12-19T09:26:54.961Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/providers/utils/utils.go:1050-1051
Timestamp: 2025-12-19T09:26:54.961Z
Learning: Update streaming end-marker handling so HuggingFace is treated as a non-[DONE] provider for backends that do not emit a DONE marker (e.g., meta llama on novita). In core/providers/utils/utils.go, adjust ProviderSendsDoneMarker() (or related logic) to detect providers that may not emit DONE and avoid relying on DONE as the sole end signal. Add tests to cover both DONE-emitting and non-DONE backends, with clear documentation in code comments explaining the rationale and any fallback behavior.
Applied to files:
core/providers/gemini/gemini_test.gocore/providers/gemini/chat.gocore/providers/anthropic/chat.gocore/providers/anthropic/responses.gocore/providers/gemini/types.gocore/providers/anthropic/utils.gocore/providers/gemini/utils.gocore/providers/gemini/responses.gocore/providers/cohere/utils.gocore/providers/anthropic/types.go
📚 Learning: 2026-01-10T11:27:47.535Z
Learnt from: Radheshg04
Repo: maximhq/bifrost PR: 1256
File: core/providers/openai/openai.go:2276-2385
Timestamp: 2026-01-10T11:27:47.535Z
Learning: Validate image generation requests for nil and missing prompts before dispatch. Follow the same pattern used here: core/bifrost.go validates nil/empty prompts, providerUtils.CheckContextAndGetRequestBody returns a structured error when the request converter yields nil, and apply this across all providers (including OpenAI) to avoid sending null bodies.
Applied to files:
core/providers/gemini/gemini_test.gocore/providers/gemini/chat.gocore/providers/anthropic/chat.gocore/providers/anthropic/responses.gocore/providers/gemini/types.gocore/providers/anthropic/utils.gocore/providers/gemini/utils.gocore/providers/gemini/responses.gocore/providers/cohere/utils.gocore/providers/anthropic/types.go
📚 Learning: 2025-12-12T10:28:54.988Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 978
File: core/providers/anthropic/chat.go:207-216
Timestamp: 2025-12-12T10:28:54.988Z
Learning: In core/providers/anthropic/chat.go, within ToAnthropicChatRequest, ensure the mapping from ReasoningDetails to Anthropic thinking blocks preserves all fields from ReasoningDetails, including when Text is nil or empty. Do not drop or coerce missing Text; propagate nil/empty values to maintain complete bidirectional mapping of Anthropic events in Bifrost. Add tests to verify that ReasoningDetails with Text nil or "" round-trip without loss, and update code to handle nullable Text without dropping data. If there are multiple blocks, preserve order and all fields during mapping.
Applied to files:
core/providers/anthropic/chat.go
📚 Learning: 2026-01-11T14:08:10.341Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1298
File: core/providers/anthropic/anthropic.go:682-699
Timestamp: 2026-01-11T14:08:10.341Z
Learning: In Anthroplic streaming implementations (and analogous providers), ensure that the final 'summary' chunk, which carries usage information and metadata, is emitted after all delta chunks and uses a chunk index of last_delta_index + 1. This differentiates the summary chunk from content delta chunks. Apply this convention consistently in the anthropic provider code and in similar streaming providers, and consider adding a targeted test to assert the ordering and chunk index logic.
Applied to files:
core/providers/anthropic/chat.gocore/providers/anthropic/responses.gocore/providers/anthropic/utils.gocore/providers/anthropic/types.go
📚 Learning: 2026-01-13T13:36:35.221Z
Learnt from: TejasGhatte
Repo: maximhq/bifrost PR: 1319
File: core/providers/anthropic/responses.go:937-937
Timestamp: 2026-01-13T13:36:35.221Z
Learning: In core/providers/anthropic/responses.go, when handling Anthropic API streaming responses, ensure that content_block_start events include a signature field set to an empty string (e.g., contentBlock.Signature = ""). The actual signature is delivered later via signature_delta events. This behavior is per Anthropic's specification and should not be treated as an error. This guideline should apply to all Anthropic response handling files under core/providers/anthropic/ and similar go files that process streaming blocks.
Applied to files:
core/providers/anthropic/chat.gocore/providers/anthropic/responses.gocore/providers/anthropic/utils.gocore/providers/anthropic/types.go
📚 Learning: 2026-01-13T17:10:07.064Z
Learnt from: Radheshg04
Repo: maximhq/bifrost PR: 1312
File: tests/integrations/python/tests/test_openai.py:1166-1258
Timestamp: 2026-01-13T17:10:07.064Z
Learning: In tests under tests/integrations/python, prefer using the OpenAI image generation model 'gpt-image-1' via the config key providers.openai.image_generation for image-generation scenarios. This avoids DALLE-3 parameter limitations (e.g., n>1, quality/size combos). Ensure tests reference this provider in mocks/fixtures and document why this choice is used for test determinism.
Applied to files:
tests/integrations/python/tests/test_google.py
📚 Learning: 2025-12-17T08:44:08.788Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1114
File: core/schemas/chatcompletions.go:224-228
Timestamp: 2025-12-17T08:44:08.788Z
Learning: In core/schemas/chatcompletions.go, ensure the schema structures mirror OpenAI's API specifications exactly. Use the valid values for fields like ChatAudioParameters.Format and ChatAudioParameters.Voice as defined by OpenAI's documentation, and avoid adding additional inline documentation or constants to maintain direct compatibility with OpenAI's API.
Applied to files:
core/schemas/chatcompletions.go
📚 Learning: 2025-12-19T08:29:20.286Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1095
File: core/internal/testutil/count_tokens.go:30-67
Timestamp: 2025-12-19T08:29:20.286Z
Learning: In core/internal/testutil test files, enforce using GetTestRetryConfigForScenario() to obtain a generic retry config, then construct a typed retry config (e.g., CountTokensRetryConfig, EmbeddingRetryConfig, TranscriptionRetryConfig) with an empty Conditions slice. Copy only MaxAttempts, BaseDelay, MaxDelay, OnRetry, and OnFinalFail from the generic config. This convention should be consistently applied across all test files in this directory.
Applied to files:
core/internal/testutil/structured_outputs.go
🧬 Code graph analysis (11)
core/providers/gemini/gemini_test.go (2)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(324-327)core/schemas/utils.go (1)
Ptr(14-16)
core/providers/gemini/chat.go (3)
core/schemas/chatcompletions.go (3)
ChatReasoningDetails(869-877)BifrostReasoningDetailsTypeText(865-865)BifrostReasoningDetailsTypeEncrypted(864-864)core/schemas/utils.go (1)
Ptr(14-16)ui/lib/types/logs.ts (1)
ReasoningDetails(127-134)
core/providers/anthropic/chat.go (1)
core/providers/anthropic/types.go (1)
AnthropicContentBlockTypeToolResult(209-209)
core/providers/anthropic/responses.go (3)
core/schemas/utils.go (1)
Ptr(14-16)core/providers/anthropic/types.go (2)
AnthropicStreamDelta(519-527)AnthropicStreamEvent(498-507)core/providers/anthropic/utils.go (1)
ConvertBifrostFinishReasonToAnthropic(88-93)
core/providers/anthropic/utils.go (2)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(324-327)core/schemas/utils.go (1)
SafeExtractOrderedMap(549-572)
core/providers/gemini/utils.go (2)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(324-327)core/schemas/utils.go (1)
SafeExtractOrderedMap(549-572)
core/internal/testutil/structured_outputs.go (1)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(324-327)
core/schemas/responses.go (1)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(324-327)
core/providers/gemini/responses.go (2)
core/schemas/responses.go (3)
ResponsesToolMessage(475-495)ResponsesMessageTypeReasoning(312-312)ResponsesReasoning(745-748)core/providers/gemini/types.go (5)
Part(1030-1054)FunctionCall(1236-1246)Type(825-825)Content(1016-1024)FunctionResponse(1249-1271)
core/providers/cohere/utils.go (2)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(324-327)core/schemas/utils.go (1)
SafeExtractOrderedMap(549-572)
core/providers/anthropic/types.go (2)
core/schemas/chatcompletions.go (1)
CacheControl(719-722)core/schemas/responses.go (1)
Citations(421-423)
🪛 Ruff (0.14.11)
tests/integrations/python/tests/test_google.py
1682-1682: Unused method argument: test_config
(ARG002)
1772-1772: Unused method argument: test_config
(ARG002)
1906-1906: Unused method argument: test_config
(ARG002)
1961-1961: Unused method argument: test_config
(ARG002)
2016-2016: Unused method argument: test_config
(ARG002)
2099-2099: Do not catch blind exception: Exception
(BLE001)
2136-2136: Do not catch blind exception: Exception
(BLE001)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
🔇 Additional comments (18)
tests/integrations/python/tests/test_google.py (2)
661-661: Nice cleanup: avoid f-string without placeholders.
1597-1599: Nice cleanup: consistent static header printing.core/schemas/chatcompletions.go (2)
276-282: Public schema change looks consistent:additionalPropertiescan now be bool OR object.
Good direction for compatibility with JSON Schema producers that emit object-valuedadditionalProperties.
286-320: Good normalization: preventsadditionalProperties: nullfrom bypassingomitempty.
This keeps round-trips cleaner by converting “present but empty” into “absent”.core/providers/anthropic/types.go (1)
219-237: ContentBlock extensions look coherent (signature/tool/web-search fields).
One note: if you rely on sendingsignature: ""in some flows, ensure the pointer is set to a non-nil empty string (omitempty won’t drop it in that case). Based on learnings, signature may arrive later via deltas.core/providers/anthropic/responses.go (2)
937-937: Signature initialization to empty string for thinking blocks is consistent with streaming expectations.Keeping
contentBlock.Signature = schemas.Ptr("")oncontent_block_startmatches the expected “signature arrives later via signature_delta” behavior. Based on learnings, this is correct.Also applies to: 953-954, 983-984
1169-1172: FixStopSequence: nilat line 1189—will serialize asstop_sequence: null.
AnthropicStreamDelta.StopSequencehas a non-omitempty JSON tag (json:"stop_sequence"), so assigningnilwill serialize asnullin the response. If the Anthropic API or clients expect a string value, this breaks the contract. Useschemas.Ptr("")instead to match the behavior at line 1171.Proposed fix
- StopSequence: nil, + StopSequence: schemas.Ptr(""),Likely an incorrect or invalid review comment.
core/providers/anthropic/chat.go (1)
177-181: Good: useAnthropicContentBlockTypeToolResultinstead of a raw string.This reduces drift from the canonical type constants.
core/schemas/responses.go (2)
131-140: LGTM! AdditionalProperties type change aligns with cross-provider standardization.The change from
*boolto*AdditionalPropertiesStructenables this field to represent either a boolean or an object/map, which is consistent with the JSON Schema specification whereadditionalPropertiescan be either. This aligns with similar changes incore/providers/anthropic/utils.goandcore/providers/gemini/utils.go.
421-423: NewCitationsstruct looks good.Simple and focused struct for citation enablement. Consider whether this struct should live closer to where it's consumed (e.g., alongside other citation-related types) or if there are plans to expand it with additional fields.
core/providers/gemini/chat.go (3)
94-102: Thought/reasoning handling looks correct.The
continuestatement here is appropriate since the author confirmed thatpart.Text,part.Thought, andpart.ThoughtSignaturedon't appear together in the same part from Gemini's API.
130-160: Signature extraction from CallID is well-implemented.The Gemini 3 behavior of embedding thought signatures in CallIDs is handled correctly:
- Detects the separator in CallID
- Base64 decodes the signature portion
- Uses the base ID for the tool call
- Creates a properly linked reasoning detail with
tool_call_<id>format
388-388: Delta content check is correct.The updated check properly considers
ReasoningDetailsalong with other delta content types, aligning with the removal of the separateReasoningfield.core/internal/testutil/structured_outputs.go (2)
421-427: Test utility correctly updated for AdditionalPropertiesStruct.The test properly wraps the boolean
additionalPropsin the newAdditionalPropertiesStructwrapper type, maintaining consistency with the schema changes.
537-542: Streaming test also correctly updated.Consistent with the non-streaming test update above.
core/providers/gemini/responses.go (3)
268-286: LGTM!The conditional look-ahead logic (using
else) correctly avoids checking the next message when a signature was already extracted from the CallID. TheStdEncodingusage here is appropriate sinceEncryptedContentis a JSON field, not a tool ID.
1063-1099: LGTM!The addedEvent and doneEvent construction is consistent, both using the same augmented
toolUseID. The guard against double-appending signatures (line 1058) properly prevents ID corruption on round-trips.
2440-2448: LGTM!The
FunctionResponse.IDis correctly set to the fullCallID, preserving the embedded thought signature for round-trip compatibility with Gemini's API.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
3609218 to
9e1faa8
Compare
b0cf7c7 to
c33a2d1
Compare
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: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
core/providers/anthropic/responses.go (1)
1043-1047: Index mismatch: synthetic deltas still useContentIndexinstead of the computed index.The
content_block_startevents at lines 919-924 prioritizeOutputIndex, but syntheticinput_json_deltaevents at line 1045 still usebifrostResp.ContentIndex. This causes mismatched indices betweencontent_block_startandcontent_block_deltaevents whenOutputIndex ≠ ContentIndex.🐛 Proposed fix
if shouldGenerateDeltas && argumentsJSON != "" { // Generate synthetic input_json_delta events by chunking the JSON - deltaEvents := generateSyntheticInputJSONDeltas(argumentsJSON, bifrostResp.ContentIndex) + // Use the same index as content_block_start for consistency + index := bifrostResp.OutputIndex + if index == nil { + index = bifrostResp.ContentIndex + } + deltaEvents := generateSyntheticInputJSONDeltas(argumentsJSON, index) events = append(events, deltaEvents...) }
🤖 Fix all issues with AI agents
In `@tests/integrations/python/tests/test_google.py`:
- Around line 1822-1839: Before indexing into
response_1.candidates[0].content.parts, add explicit assertions that response_1
has a non-empty candidates list and that the first candidate has a content with
a non-empty parts list to produce clear failures instead of
IndexError/AttributeError; e.g., assert response_1.candidates and
len(response_1.candidates) > 0 and hasattr(response_1.candidates[0], 'content')
and getattr(response_1.candidates[0].content, 'parts', None), then proceed to
iterate and set first_fc/first_signature as before. Apply the same pattern
(assert candidates exists/non-empty and content.parts exists/non-empty) to the
other blocks mentioned (around lines 1880-1904, 1924-1928, 2052-2066)
referencing the same symbols (response_1, response, candidates, content, parts,
first_fc, first_signature) so tests fail with clear assertion messages.
♻️ Duplicate comments (4)
core/schemas/chatcompletions.go (1)
324-350: Prevent accidental emission ofadditionalProperties: nullwhen callers construct an “empty” struct.
AdditionalPropertiesStruct.MarshalJSON()still returnsnullwhen both fields are nil; if any code ever doesAdditionalProperties: &AdditionalPropertiesStruct{}, JSON output will includeadditionalProperties: null(some JSON Schema consumers reject this). Consider either (a) treating “both nil” as{}(empty schema) or (b) returning an error to force callers to omit the field. Also consider marshaling the map by value.Safer MarshalJSON behavior (one possible direction)
func (a AdditionalPropertiesStruct) MarshalJSON() ([]byte, error) { @@ // If map is set, marshal as object if a.AdditionalPropertiesMap != nil { - return Marshal(a.AdditionalPropertiesMap) + return Marshal(*a.AdditionalPropertiesMap) } - // If both are nil, return null - return Marshal(nil) + // If both are nil, avoid emitting `null` (prefer empty schema or omit field). + return Marshal(OrderedMap{}) }#!/bin/bash # Find places that may construct an empty AdditionalPropertiesStruct pointer rg -n --type=go 'AdditionalProperties:\s*&schemas\.AdditionalPropertiesStruct\s*\{\s*\}' -S rg -n --type=go 'AdditionalProperties:\s*&AdditionalPropertiesStruct\s*\{\s*\}' -Stests/integrations/python/tests/test_google.py (2)
1681-1683: Fix Ruff ARG002 without losing thetest_configfixture.
These tests accepttest_configbut don’t use it. Prefer@pytest.mark.usefixtures("test_config")and drop the arg (vs renaming, which would change fixture injection).Proposed diff
`@skip_if_no_api_key`("gemini") -def test_30a_gemini_3_parallel_function_calls_signatures(self, test_config): +@pytest.mark.usefixtures("test_config") +def test_30a_gemini_3_parallel_function_calls_signatures(self): @@ `@skip_if_no_api_key`("gemini") -def test_30b_gemini_3_sequential_function_calls_signatures(self, test_config): +@pytest.mark.usefixtures("test_config") +def test_30b_gemini_3_sequential_function_calls_signatures(self): @@ `@skip_if_no_api_key`("gemini") -def test_30c_gemini_3_thought_signatures_in_text_responses(self, test_config): +@pytest.mark.usefixtures("test_config") +def test_30c_gemini_3_thought_signatures_in_text_responses(self): @@ `@skip_if_no_api_key`("gemini") -def test_30d_gemini_3_thinking_levels(self, test_config): +@pytest.mark.usefixtures("test_config") +def test_30d_gemini_3_thinking_levels(self): @@ `@skip_if_no_api_key`("gemini") -def test_30e_gemini_3_signature_validation_strict(self, test_config): +@pytest.mark.usefixtures("test_config") +def test_30e_gemini_3_signature_validation_strict(self):Also applies to: 1771-1773, 1905-1907, 1960-1962, 2015-2017
2099-2142: Avoid broadexcept Exception(or explicitly suppress BLE001) intest_30e.
If this is intentionally broad for an integration test, add# noqa: BLE001. Otherwise, catch the SDK’s concrete exception types.Option A (keep broad catch, suppress Ruff)
- except Exception as e: + except Exception as e: # noqa: BLE001 pytest.fail(f"Request with signature should not fail: {e}") @@ - except Exception as e: + except Exception as e: # noqa: BLE001 error_msg = str(e).lower()google genai (google-genai) python sdk: what exception types does client.models.generate_content raise? (API/validation errors)core/providers/gemini/chat.go (1)
342-350: Streaming path missingIDfield in reasoning details for extracted signatures.The non-streaming path (lines 154-159) sets
ID: schemas.Ptr(fmt.Sprintf("tool_call_%s", callID))for reasoning details extracted from CallID, but the streaming path here doesn't set theIDfield. This inconsistency could make it harder to correlate signatures with their corresponding tool calls in streaming responses.🔧 Suggested fix
// If we extracted a signature from CallID, add it to reasoning details if len(extractedSig) > 0 { thoughtSig := base64.StdEncoding.EncodeToString(extractedSig) reasoningDetails = append(reasoningDetails, schemas.ChatReasoningDetails{ Index: len(reasoningDetails), Type: schemas.BifrostReasoningDetailsTypeEncrypted, Signature: &thoughtSig, + ID: schemas.Ptr(fmt.Sprintf("tool_call_%s", callID)), }) }
🧹 Nitpick comments (8)
core/changelog.md (2)
1-1: Use imperative mood for consistency.The entry uses past tense ("added") while the other entries follow the imperative mood convention ("add", "improve", "enhance"). For consistency with conventional commits format, use "add" instead of "added".
📝 Proposed fix
-chore: added case-insensitive helper methods for header and query parameter lookups in HTTPRequest +chore: add case-insensitive helper methods for header and query parameter lookups in HTTPRequest
6-6: Clarify the Anthropic streaming fix description.The description "anthropic streaming events through integration" is vague. According to the PR summary, there are multiple specific Anthropic changes (OutputIndex/ContentIndex prioritization, signature fields in thinking blocks, stop reason handling). Consider being more specific about what was fixed to improve changelog clarity.
Example improvements
Consider splitting or clarifying the entry:
-fix: anthropic streaming events through integration +fix: improve anthropic streaming with proper index handling and stop reason supportOr if these are distinct fixes, consider separate entries:
- fix: prioritize OutputIndex over ContentIndex in Anthropic streaming - fix: add signature fields to Anthropic thinking content blocks - fix: add stop reason handling in Anthropic message_stop eventscore/providers/anthropic/utils.go (1)
768-778: Useelse ifto clarify mutually exclusive branches.The two
ifblocks at lines 768-771 and 774-777 handle mutually exclusive cases (boolean vs. map), but they're written as sequentialifstatements. While functionally correct (sinceSafeExtractOrderedMapwon't match a boolean), usingelse ifwould make the intent clearer and prevent accidental overwrites if the type-checking logic changes.♻️ Suggested refactor
if additionalProps, ok := schemaMap["additionalProperties"].(bool); ok { jsonSchema.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ AdditionalPropertiesBool: &additionalProps, } - } - - if additionalProps, ok := schemas.SafeExtractOrderedMap(schemaMap["additionalProperties"]); ok { + } else if additionalProps, ok := schemas.SafeExtractOrderedMap(schemaMap["additionalProperties"]); ok { jsonSchema.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ AdditionalPropertiesMap: &additionalProps, } }core/providers/cohere/utils.go (1)
76-86: Useelse ifto clarify mutually exclusive branches.Same pattern issue as in
anthropic/utils.go- the boolean and map extraction are mutually exclusive but written as sequentialifstatements. Usingelse ifwould be clearer.♻️ Suggested refactor
// Extract additionalProperties if addPropsVal, ok := paramsMap["additionalProperties"].(bool); ok { result.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ AdditionalPropertiesBool: &addPropsVal, } - } - - if addPropsVal, ok := schemas.SafeExtractOrderedMap(paramsMap["additionalProperties"]); ok { + } else if addPropsVal, ok := schemas.SafeExtractOrderedMap(paramsMap["additionalProperties"]); ok { result.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ AdditionalPropertiesMap: &addPropsVal, } }core/providers/gemini/chat.go (1)
318-328: Consider extracting signature extraction logic to a helper function.The signature extraction from CallID (lines 130-140 in non-stream, lines 318-328 in stream) is duplicated. Consider extracting to a helper function like
extractSignatureFromCallID(callID string) (baseID string, signature []byte)to improve maintainability.core/providers/gemini/utils.go (1)
1145-1155: Consider usingelse iffor mutually exclusive type handling.The two
ifblocks handle the boolean and object cases foradditionalProperties. While logically correct (a value can only be one type), usingelse ifon line 1151 would make the mutual exclusivity explicit and skip the unnecessarySafeExtractOrderedMapcall when the boolean branch matches.♻️ Suggested refactor
// Extract additionalProperties if additionalProps, ok := normalizedSchemaMap["additionalProperties"].(bool); ok { jsonSchema.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ AdditionalPropertiesBool: &additionalProps, } - } - - if additionalProps, ok := schemas.SafeExtractOrderedMap(normalizedSchemaMap["additionalProperties"]); ok { + } else if additionalProps, ok := schemas.SafeExtractOrderedMap(normalizedSchemaMap["additionalProperties"]); ok { jsonSchema.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ AdditionalPropertiesMap: &additionalProps, } }core/providers/gemini/responses.go (1)
268-286: Inconsistent base64 encoding: uses StdEncoding for EncryptedContent but RawURLEncoding for CallID signatures.Line 278 uses
base64.StdEncoding.DecodeStringto decodeEncryptedContentfrom reasoning messages, while line 256 usesbase64.RawURLEncoding.DecodeStringfor signatures embedded in CallIDs. This is intentional sinceEncryptedContentcomes from a different source (Gemini API's native format), but consider adding a comment to clarify the distinction.Suggested clarification comment
} else { // Otherwise, look ahead to see if the next message is a reasoning message with encrypted content // (thought signature for this function call) if i+1 < len(bifrostResp.Output) { nextMsg := bifrostResp.Output[i+1] if nextMsg.Type != nil && *nextMsg.Type == schemas.ResponsesMessageTypeReasoning && nextMsg.ResponsesReasoning != nil && nextMsg.ResponsesReasoning.EncryptedContent != nil { + // EncryptedContent uses standard base64 (from Gemini API native format) decodedSig, err := base64.StdEncoding.DecodeString(*nextMsg.ResponsesReasoning.EncryptedContent)core/providers/anthropic/types.go (1)
293-336: AnthropicCitations dual-mode marshal/unmarshal handles request vs response formats correctly.The implementation properly distinguishes between:
- Request format:
{enabled: true}marshaled viaConfig- Response format:
[{type: "...", cited_text: "...", ...}]marshaled viaTextCitationsHowever, there's a subtle issue in
UnmarshalJSON: the order of attempts matters for robustness.Consider swapping unmarshal order for better error handling
Currently, the code tries array first, then config. Since an empty array
[]would successfully unmarshal as[]AnthropicTextCitation(empty slice), and config objects like{enabled: true}would fail array unmarshal, the current order works. However, consider adding a check for ambiguous cases:func (ac *AnthropicCitations) UnmarshalJSON(data []byte) error { + // Handle null case + if string(data) == "null" { + return nil + } + // Try to unmarshal as array of citations var textCitations []AnthropicTextCitation if err := sonic.Unmarshal(data, &textCitations); err == nil {This would handle null JSON values gracefully.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (17)
core/changelog.mdcore/internal/testutil/structured_outputs.gocore/providers/anthropic/chat.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.gocore/providers/anthropic/utils.gocore/providers/cohere/utils.gocore/providers/gemini/chat.gocore/providers/gemini/gemini_test.gocore/providers/gemini/responses.gocore/providers/gemini/types.gocore/providers/gemini/utils.gocore/schemas/chatcompletions.gocore/schemas/responses.gotests/integrations/python/tests/test_google.pytransports/bifrost-http/integrations/router.gotransports/changelog.md
💤 Files with no reviewable changes (1)
- transports/bifrost-http/integrations/router.go
🚧 Files skipped from review as they are similar to previous changes (3)
- core/internal/testutil/structured_outputs.go
- transports/changelog.md
- core/providers/gemini/gemini_test.go
🧰 Additional context used
📓 Path-based instructions (1)
**
⚙️ CodeRabbit configuration file
always check the stack if there is one for the current PR. do not give localized reviews for the PR, always see all changes in the light of the whole stack of PRs (if there is a stack, if there is no stack you can continue to make localized suggestions/reviews)
Files:
core/providers/anthropic/chat.gocore/providers/gemini/utils.gocore/providers/gemini/types.gocore/schemas/chatcompletions.gocore/schemas/responses.gocore/providers/gemini/responses.gocore/providers/anthropic/utils.gocore/providers/gemini/chat.gocore/changelog.mdcore/providers/anthropic/responses.gocore/providers/anthropic/types.gocore/providers/cohere/utils.gotests/integrations/python/tests/test_google.py
🧠 Learnings (12)
📚 Learning: 2025-12-09T17:07:42.007Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/schemas/account.go:9-18
Timestamp: 2025-12-09T17:07:42.007Z
Learning: In core/schemas/account.go, the HuggingFaceKeyConfig field within the Key struct is currently unused and reserved for future Hugging Face inference endpoint deployments. Do not flag this field as missing from OpenAPI documentation or require its presence in the API spec until the feature is actively implemented and used. When the feature is added, update the OpenAPI docs accordingly; otherwise, treat this field as non-breaking and not part of the current API surface.
Applied to files:
core/providers/anthropic/chat.gocore/providers/gemini/utils.gocore/providers/gemini/types.gocore/schemas/chatcompletions.gocore/schemas/responses.gocore/providers/gemini/responses.gocore/providers/anthropic/utils.gocore/providers/gemini/chat.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.gocore/providers/cohere/utils.go
📚 Learning: 2025-12-29T11:54:55.836Z
Learnt from: akshaydeo
Repo: maximhq/bifrost PR: 1153
File: framework/configstore/rdb.go:2221-2246
Timestamp: 2025-12-29T11:54:55.836Z
Learning: In Go reviews, do not flag range-over-int patterns like for i := range n as compile-time errors, assuming Go 1.22+ semantics. Only flag actual range-capable values (slices, arrays, maps, channels, strings) and other compile-time issues. This applies to all Go files across the repository.
Applied to files:
core/providers/anthropic/chat.gocore/providers/gemini/utils.gocore/providers/gemini/types.gocore/schemas/chatcompletions.gocore/schemas/responses.gocore/providers/gemini/responses.gocore/providers/anthropic/utils.gocore/providers/gemini/chat.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.gocore/providers/cohere/utils.go
📚 Learning: 2026-01-14T04:40:11.480Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1312
File: framework/modelcatalog/pricing.go:276-426
Timestamp: 2026-01-14T04:40:11.480Z
Learning: In the Bifrost codebase, ImageUsage and other usage types guarantee that TotalTokens is populated (computed as InputTokens + OutputTokens if providers don’t supply TotalTokens). Reviewers can rely on this invariant and should not assume TotalTokens may be missing when input/output tokens exist. When implementing tiering logic or token-based decisions, you can safely use TotalTokens without extra null/zero guards, provided you’re in a context where InputTokens and OutputTokens are present. If a branch might discard tokens, ensure the invariant is preserved or add explicit checks only where the inputs are confirmed to be valid.
Applied to files:
core/providers/anthropic/chat.gocore/providers/gemini/utils.gocore/providers/gemini/types.gocore/schemas/chatcompletions.gocore/schemas/responses.gocore/providers/gemini/responses.gocore/providers/anthropic/utils.gocore/providers/gemini/chat.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.gocore/providers/cohere/utils.go
📚 Learning: 2025-12-12T10:28:54.988Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 978
File: core/providers/anthropic/chat.go:207-216
Timestamp: 2025-12-12T10:28:54.988Z
Learning: In core/providers/anthropic/chat.go, within ToAnthropicChatRequest, ensure the mapping from ReasoningDetails to Anthropic thinking blocks preserves all fields from ReasoningDetails, including when Text is nil or empty. Do not drop or coerce missing Text; propagate nil/empty values to maintain complete bidirectional mapping of Anthropic events in Bifrost. Add tests to verify that ReasoningDetails with Text nil or "" round-trip without loss, and update code to handle nullable Text without dropping data. If there are multiple blocks, preserve order and all fields during mapping.
Applied to files:
core/providers/anthropic/chat.go
📚 Learning: 2025-12-19T09:26:54.961Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/providers/utils/utils.go:1050-1051
Timestamp: 2025-12-19T09:26:54.961Z
Learning: Update streaming end-marker handling so HuggingFace is treated as a non-[DONE] provider for backends that do not emit a DONE marker (e.g., meta llama on novita). In core/providers/utils/utils.go, adjust ProviderSendsDoneMarker() (or related logic) to detect providers that may not emit DONE and avoid relying on DONE as the sole end signal. Add tests to cover both DONE-emitting and non-DONE backends, with clear documentation in code comments explaining the rationale and any fallback behavior.
Applied to files:
core/providers/anthropic/chat.gocore/providers/gemini/utils.gocore/providers/gemini/types.gocore/providers/gemini/responses.gocore/providers/anthropic/utils.gocore/providers/gemini/chat.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.gocore/providers/cohere/utils.go
📚 Learning: 2026-01-10T11:27:47.535Z
Learnt from: Radheshg04
Repo: maximhq/bifrost PR: 1256
File: core/providers/openai/openai.go:2276-2385
Timestamp: 2026-01-10T11:27:47.535Z
Learning: Validate image generation requests for nil and missing prompts before dispatch. Follow the same pattern used here: core/bifrost.go validates nil/empty prompts, providerUtils.CheckContextAndGetRequestBody returns a structured error when the request converter yields nil, and apply this across all providers (including OpenAI) to avoid sending null bodies.
Applied to files:
core/providers/anthropic/chat.gocore/providers/gemini/utils.gocore/providers/gemini/types.gocore/providers/gemini/responses.gocore/providers/anthropic/utils.gocore/providers/gemini/chat.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.gocore/providers/cohere/utils.go
📚 Learning: 2026-01-11T14:08:10.341Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1298
File: core/providers/anthropic/anthropic.go:682-699
Timestamp: 2026-01-11T14:08:10.341Z
Learning: In Anthroplic streaming implementations (and analogous providers), ensure that the final 'summary' chunk, which carries usage information and metadata, is emitted after all delta chunks and uses a chunk index of last_delta_index + 1. This differentiates the summary chunk from content delta chunks. Apply this convention consistently in the anthropic provider code and in similar streaming providers, and consider adding a targeted test to assert the ordering and chunk index logic.
Applied to files:
core/providers/anthropic/chat.gocore/providers/anthropic/utils.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.go
📚 Learning: 2026-01-13T13:36:35.221Z
Learnt from: TejasGhatte
Repo: maximhq/bifrost PR: 1319
File: core/providers/anthropic/responses.go:937-937
Timestamp: 2026-01-13T13:36:35.221Z
Learning: In core/providers/anthropic/responses.go, when handling Anthropic API streaming responses, ensure that content_block_start events include a signature field set to an empty string (e.g., contentBlock.Signature = ""). The actual signature is delivered later via signature_delta events. This behavior is per Anthropic's specification and should not be treated as an error. This guideline should apply to all Anthropic response handling files under core/providers/anthropic/ and similar go files that process streaming blocks.
Applied to files:
core/providers/anthropic/chat.gocore/providers/anthropic/utils.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.go
📚 Learning: 2026-01-14T06:57:42.750Z
Learnt from: TejasGhatte
Repo: maximhq/bifrost PR: 1319
File: core/providers/anthropic/types.go:248-336
Timestamp: 2026-01-14T06:57:42.750Z
Learning: For Anthropic citation types (page_location, char_location, content_block_location), ensure there is an optional string field file_id to reference uploaded files. Update the Go structs modeling these citations to include FileID *string (or string with omitempty) and document its optionality in comments, so code consuming these types can handle absence of file_id gracefully.
Applied to files:
core/providers/anthropic/chat.gocore/providers/anthropic/utils.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.go
📚 Learning: 2026-01-14T06:57:42.750Z
Learnt from: TejasGhatte
Repo: maximhq/bifrost PR: 1319
File: core/providers/anthropic/types.go:248-336
Timestamp: 2026-01-14T06:57:42.750Z
Learning: In core/providers/anthropic/types.go, ensure the web_search_result_location citation type includes a string field named 'url' alongside the existing fields 'encrypted_index', 'title', and 'cited_text'. If the field is missing, add it with type string and appropriate struct tags (e.g., json and/or db tags) and update any related serialization or usage accordingly.
Applied to files:
core/providers/anthropic/chat.gocore/providers/anthropic/utils.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.go
📚 Learning: 2025-12-17T08:44:08.788Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1114
File: core/schemas/chatcompletions.go:224-228
Timestamp: 2025-12-17T08:44:08.788Z
Learning: In core/schemas/chatcompletions.go, ensure the schema structures mirror OpenAI's API specifications exactly. Use the valid values for fields like ChatAudioParameters.Format and ChatAudioParameters.Voice as defined by OpenAI's documentation, and avoid adding additional inline documentation or constants to maintain direct compatibility with OpenAI's API.
Applied to files:
core/schemas/chatcompletions.go
📚 Learning: 2026-01-13T17:10:07.064Z
Learnt from: Radheshg04
Repo: maximhq/bifrost PR: 1312
File: tests/integrations/python/tests/test_openai.py:1166-1258
Timestamp: 2026-01-13T17:10:07.064Z
Learning: In tests under tests/integrations/python, prefer using the OpenAI image generation model 'gpt-image-1' via the config key providers.openai.image_generation for image-generation scenarios. This avoids DALLE-3 parameter limitations (e.g., n>1, quality/size combos). Ensure tests reference this provider in mocks/fixtures and document why this choice is used for test determinism.
Applied to files:
tests/integrations/python/tests/test_google.py
🧬 Code graph analysis (10)
core/providers/anthropic/chat.go (1)
core/providers/anthropic/types.go (1)
AnthropicContentBlockTypeToolResult(209-209)
core/providers/gemini/utils.go (2)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(324-327)core/schemas/utils.go (1)
SafeExtractOrderedMap(549-572)
core/schemas/chatcompletions.go (2)
core/schemas/json_native.go (2)
Marshal(8-10)Unmarshal(18-20)core/schemas/json_wasm.go (2)
Marshal(8-10)Unmarshal(22-24)
core/schemas/responses.go (1)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(324-327)
core/providers/gemini/responses.go (2)
core/providers/gemini/types.go (3)
Part(1030-1054)FunctionCall(1236-1246)FunctionResponse(1249-1271)core/schemas/utils.go (1)
Ptr(14-16)
core/providers/anthropic/utils.go (2)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(324-327)core/schemas/utils.go (1)
SafeExtractOrderedMap(549-572)
core/providers/gemini/chat.go (5)
core/schemas/chatcompletions.go (3)
ChatReasoningDetails(869-877)BifrostReasoningDetailsTypeText(865-865)BifrostReasoningDetailsTypeEncrypted(864-864)ui/app/page.tsx (1)
Index(5-7)core/providers/gemini/types.go (3)
Type(825-825)Role(25-25)Content(1016-1024)core/schemas/utils.go (1)
Ptr(14-16)ui/lib/types/logs.ts (1)
ReasoningDetails(127-134)
core/providers/anthropic/responses.go (3)
core/schemas/utils.go (1)
Ptr(14-16)core/providers/anthropic/types.go (2)
AnthropicStreamDelta(519-527)AnthropicStreamEvent(498-507)core/providers/anthropic/utils.go (1)
ConvertBifrostFinishReasonToAnthropic(88-93)
core/providers/cohere/utils.go (2)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(324-327)core/schemas/utils.go (1)
SafeExtractOrderedMap(549-572)
tests/integrations/python/tests/test_google.py (2)
tests/integrations/python/tests/utils/common.py (1)
skip_if_no_api_key(1830-1841)core/providers/gemini/types.go (4)
Tool(647-674)Content(1016-1024)Part(1030-1054)FunctionResponse(1249-1271)
🪛 Ruff (0.14.11)
tests/integrations/python/tests/test_google.py
1682-1682: Unused method argument: test_config
(ARG002)
1772-1772: Unused method argument: test_config
(ARG002)
1906-1906: Unused method argument: test_config
(ARG002)
1961-1961: Unused method argument: test_config
(ARG002)
2016-2016: Unused method argument: test_config
(ARG002)
2099-2099: Do not catch blind exception: Exception
(BLE001)
2136-2136: Do not catch blind exception: Exception
(BLE001)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (16)
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
🔇 Additional comments (28)
tests/integrations/python/tests/test_google.py (2)
661-674: Good: removed unnecessary f-string in PDF test output.
No further changes needed here.
1595-1601: Good: static token-usage header print.
No further changes needed here.core/schemas/chatcompletions.go (1)
276-320: Nice:additionalPropertiesnow supports both boolean and object, with null→omitted normalization.
The post-unmarshal normalization avoidsadditionalProperties: nullleaking viaomitemptywhen the struct is reused.core/providers/gemini/types.go (1)
22-24: Separator collision is already handled safely. The implementation correctly usesstrings.SplitN(id, thoughtSignatureSeparator, 2)at all parsing sites, which prevents signatures containing_ts_from breaking the split. All usages in responses.go and chat.go follow this pattern consistently, so no changes are needed.core/providers/anthropic/chat.go (1)
178-178: LGTM - Good use of constant instead of magic string.This change correctly uses the
AnthropicContentBlockTypeToolResultconstant, consistent with other type assignments in this file (lines 156, 191, 230, etc.).core/schemas/responses.go (2)
131-140: LGTM - Type change aligns with broader AdditionalProperties refactoring.The change from
*boolto*AdditionalPropertiesStructforAdditionalPropertiesis consistent with the schema updates across the codebase to support both boolean and object types for JSON schema'sadditionalProperties.
421-423: No action required — theCitationsstruct is actively used.The struct is referenced in
core/providers/anthropic/types.go(line 298, 328) as*schemas.Citationswithin theAnthropicCitationstype, where it handles citation configuration during JSON marshaling/unmarshaling for Anthropic API requests. This is a valid schema definition pattern where central schema types are used by provider-specific implementations.Likely an incorrect or invalid review comment.
core/providers/gemini/chat.go (3)
94-102: LGTM - Thought/reasoning text handling.The logic correctly separates thought content (where
part.Thoughtis true) intoreasoningDetailsrather than regular content blocks.
130-160: LGTM - Signature extraction from CallID with proper ID correlation.The non-streaming path correctly:
- Extracts embedded signatures from CallID using the separator
- Uses the base ID (without signature) for the tool call
- Adds reasoning details with an ID linking to the tool call
388-388: LGTM - Updated delta content check.The
hasDeltaContentcheck correctly includesReasoningDetailsto ensure chunks with only reasoning content are emitted.core/providers/anthropic/responses.go (7)
898-924: Index prioritization pattern looks correct.The consistent use of
OutputIndexwith fallback toContentIndexfor Anthropic global indexing is properly implemented across both computer tool and regular content block handling paths.
937-937: Empty signature initialization is correct per Anthropic spec.Based on learnings, Anthropic's streaming API requires an empty
signaturefield incontent_block_startevents for thinking blocks. The actual signature value arrives later viasignature_deltaevents.
946-993: Consistent signature handling across reasoning/thinking block variants.The signature initialization at lines 953 and 983 follows the same pattern established for thinking blocks, correctly handling edge cases where reasoning content may be misclassified or thinking is enabled for the first block.
1054-1098: Consistent OutputIndex prioritization across delta event handlers.The
OutputIndex-first pattern withContentIndexfallback is consistently applied acrossOutputTextDelta,FunctionCallArgumentsDelta, andReasoningSummaryTextDeltahandlers, aligning with thecontent_block_startindexing.
1118-1161: Correct index handling inOutputItemDoneevent emission.The
OutputIndex-first pattern is properly applied for both computer tool completion and general content block completion paths.
1165-1192: StopSequence handling correctly differentiates default vs explicit stop reasons.The code intentionally uses
StopSequence: schemas.Ptr("")for the default case (line 1171) andStopSequence: nilwhen an explicitStopReasonis provided from the response (line 1189). This ensures the field is present in the defaultend_turncase but omitted when a specific stop reason (liketool_use) is set.
1194-1224: MCP call handlers follow consistent indexing pattern.Both
MCPCallArgumentsDeltaandMCPCallCompletedevent handlers properly apply theOutputIndex-first pattern withContentIndexfallback.core/providers/gemini/responses.go (8)
246-261: LGTM - Thought signature extraction from CallID is correctly implemented.The code properly extracts the thought signature using
base64.RawURLEncoding.DecodeStringwhich matches the encoding used elsewhere when embedding signatures into CallIDs. TheSplitNwith limit 2 ensures only the first separator is used for splitting.
457-477: LGTM - Streaming path correctly extracts and decodes thought signatures.The streaming function call handling properly:
- Extracts signature from CallID using the separator
- Preserves the full CallID (including signature) in
functionCall.ID- Decodes using
base64.RawURLEncodingmatching the encoding pathThis is now consistent with the non-streaming path.
1057-1061: LGTM - Signature embedding correctly uses RawURLEncoding and guards against double-appending.The guard
!strings.Contains(toolUseID, thoughtSignatureSeparator)prevents double-appending signatures, andbase64.RawURLEncodingproduces URL-safe characters compliant with Gemini's tool ID constraints.
1065-1099: LGTM - Function call streaming events correctly propagate the augmented tool ID.Both the
addedEventanddoneEventuse the sametoolUseID(with embedded signature), ensuring consistent ID correlation across the streaming lifecycle.
1866-1870: LGTM - Non-streaming path now matches streaming with guard and RawURLEncoding.The fix addresses the past review comment by:
- Using
base64.RawURLEncoding.EncodeToString(URL-safe)- Including the guard
!strings.Contains(callID, thoughtSignatureSeparator)to prevent double-appending
1903-1915: Good defensive handling for extracting function name from response ID.The code properly handles the case where
FunctionResponse.Nameis empty by falling back to extracting it from the ID, stripping the thought signature suffix if present. This ensures the function name is always populated.
2388-2412: LGTM - Function call conversion correctly extracts and decodes thought signatures.The code:
- Extracts signature from CallID when separator is present
- Preserves the full CallID for the FunctionCall.ID
- Decodes using
base64.RawURLEncoding.DecodeStringmatching the encoding path- Silently handles decode errors by clearing
thoughtSig
2450-2455: LGTM - FunctionResponse correctly preserves the full CallID.The
FunctionResponse.IDis set to the full CallID, maintaining consistency with how function calls embed their thought signatures.core/providers/anthropic/types.go (3)
219-238: LGTM - AnthropicContentBlock fields properly extended.The new fields (
URL,EncryptedContent,PageAge) support web search results, and theTypefield correctly no longer usesomitemptysince it's always required. TheCitationsfield type change from a simple config to*AnthropicCitationsenables the dual request/response handling.Based on learnings, the
URLfield for web search results andEncryptedContentfor redacted thinking align with Anthropic's API schema.
248-291: Well-structured citation types covering all Anthropic citation variants.The
AnthropicTextCitationstruct comprehensively covers all five citation types with appropriate optional fields:
char_location: character indices + document fieldspage_location: page numbers + document fieldscontent_block_location: block indices + document fieldsweb_search_result_location: encrypted_index, title, urlsearch_result_location: search_result_index, source, block indicesBased on learnings, the
FileIDfield for document-based citations andURLfield for web search citations match the official Anthropic API schema.
304-316: MarshalJSON correctly validates mutual exclusivity.The validation at line 305-307 prevents invalid state where both
ConfigandTextCitationsare set, and the fallback tonilwhen both are unset is appropriate.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
c33a2d1 to
973d8fb
Compare
9e1faa8 to
4cf1e95
Compare
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: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
core/providers/gemini/responses.go (1)
1057-1100: Fix mismatched streaming item IDs when appending thought signature totoolUseID.You populate
state.ItemIDs/state.ToolCallIDswith the pre-signaturetoolUseID, then conditionally mutatetoolUseID(Lines 1057-1061). Later, close/cleanup paths usestate.ItemIDs[...]to emitoutput_item.done, which can disagree with theItemIDemitted inoutput_item.added/function_call_arguments.done. This can break client correlation.Proposed fix
toolUseID := part.FunctionCall.ID if toolUseID == "" { toolUseID = part.FunctionCall.Name // Fallback to name as ID } - state.ItemIDs[outputIndex] = toolUseID - state.ToolCallIDs[outputIndex] = toolUseID - state.ToolCallNames[outputIndex] = part.FunctionCall.Name - // Convert args to JSON string argsJSON := "" if part.FunctionCall.Args != nil { if argsBytes, err := sonic.Marshal(part.FunctionCall.Args); err == nil { argsJSON = string(argsBytes) } } state.ToolArgumentBuffers[outputIndex] = argsJSON // Attach thought signature to ID if present if len(part.ThoughtSignature) > 0 && !strings.Contains(toolUseID, thoughtSignatureSeparator) { encoded := base64.RawURLEncoding.EncodeToString(part.ThoughtSignature) toolUseID = fmt.Sprintf("%s%s%s", toolUseID, thoughtSignatureSeparator, encoded) } + + // Persist the final ID (post-augmentation) so subsequent done/close events match. + state.ItemIDs[outputIndex] = toolUseID + state.ToolCallIDs[outputIndex] = toolUseID + state.ToolCallNames[outputIndex] = part.FunctionCall.Name
🤖 Fix all issues with AI agents
In `@core/providers/anthropic/responses.go`:
- Around line 62-64: The global sync.Map webSearchItemIDs can leak when streams
error or clients disconnect; modify the code so keys are tracked per-stream and
cleaned up on release: when you add entries to webSearchItemIDs (the current add
at the WebSearch tool registration point), also append the key to the
stream-scoped state (e.g., a []string field on the anthropic responses stream
state), and then in releaseAnthropicResponsesStreamState iterate that per-stream
key list and call webSearchItemIDs.Delete(key) to remove them; alternatively, if
you prefer keeping the global map, ensure releaseAnthropicResponsesStreamState
is updated to detect and delete any webSearchItemIDs associated with the current
stream (matching whatever ID you store) and call that cleanup on all
terminal/error/disconnect paths (including where output_item.done would have
deleted) so entries cannot persist after stream termination.
♻️ Duplicate comments (4)
tests/integrations/python/tests/test_google.py (1)
1827-1839: Missing defensive asserts before indexing into response structure.The code accesses
response_1.candidates[0].content.partsat line 1827 without first asserting thatcandidates,content, andpartsare present. This could lead to confusingIndexErrororAttributeErrorinstead of clear test failure messages.Suggested fix
response_1 = client.models.generate_content( model=model, contents="Search for information about Python programming and then process the results.", config=types.GenerateContentConfig( tools=[search_tool, process_tool], thinking_config=types.ThinkingConfig( thinking_level="medium" ) ) ) + assert response_1.candidates, "Response should have candidates" + assert response_1.candidates[0].content, "Candidate should have content" + assert response_1.candidates[0].content.parts, "Content should have parts" + # Extract first function call and signature first_fc = None first_signature = None for part in response_1.candidates[0].content.parts:core/providers/gemini/chat.go (2)
94-103: Non-stream:continuecan dropThoughtSignatureif it co-exists with thought text.This is the same risk previously discussed: if Gemini ever returns thought text and a
ThoughtSignaturein the samePart, the signature won’t be captured.Proposed fix (process signature after the thought-text handling)
- if part.Text != "" && part.Thought { + if part.Text != "" && part.Thought { reasoningDetails = append(reasoningDetails, schemas.ChatReasoningDetails{ Index: len(reasoningDetails), Type: schemas.BifrostReasoningDetailsTypeText, Text: &part.Text, }) - continue } + // Allow ThoughtSignature to co-exist with thought text. + if part.ThoughtSignature != nil { + thoughtSig := base64.StdEncoding.EncodeToString(part.ThoughtSignature) + reasoningDetails = append(reasoningDetails, schemas.ChatReasoningDetails{ + Index: len(reasoningDetails), + Type: schemas.BifrostReasoningDetailsTypeEncrypted, + Signature: &thoughtSig, + }) + } + + if part.Text != "" && part.Thought { + continue + }
342-350: Streaming: addreasoningDetails[].IDwhen signature is extracted from CallID (to match non-stream).Non-stream sets
ID: tool_call_<callID>for extracted signature (Line 158-159). Streaming extraction appends an encrypted reasoning detail withoutID, making correlation harder.Proposed fix
if len(extractedSig) > 0 { thoughtSig := base64.StdEncoding.EncodeToString(extractedSig) reasoningDetails = append(reasoningDetails, schemas.ChatReasoningDetails{ Index: len(reasoningDetails), Type: schemas.BifrostReasoningDetailsTypeEncrypted, Signature: &thoughtSig, + ID: schemas.Ptr(fmt.Sprintf("tool_call_%s", callID)), }) }core/providers/anthropic/responses.go (1)
2912-2934: Third instance of duplicated WebSearch sanitization.This is the third location with identical WebSearch sanitization logic. As noted earlier, this should be extracted to a shared helper function.
🧹 Nitpick comments (7)
tests/integrations/python/tests/test_google.py (2)
2099-2100: Consider catching specific SDK exceptions instead of bareException.The bare
except Exceptioncatches at lines 2099 and 2136 could mask unexpected errors. For better test diagnostics, catch specific exception types from the Google GenAI SDK.Suggested approach
+ from google.genai.errors import APIError + # Step 2: Test WITH signature (should succeed) try: # ... existing code ... - except Exception as e: + except (APIError, ValueError) as e: # Catch SDK-specific errors pytest.fail(f"Request with signature should not fail: {e}") # Step 3: Test WITHOUT signature try: # ... existing code ... - except Exception as e: + except (APIError, ValueError) as e: # Catch SDK-specific errors error_msg = str(e).lower() if "signature" in error_msg or "400" in error_msg or "validation" in error_msg: print(f" ✓ Request without signature failed as expected: {type(e).__name__}") else: print(f" ? Request failed with unexpected error: {e}")Alternatively, add
# noqa: BLE001comments if the broad catch is intentional for test resilience.Also applies to: 2136-2141
2052-2066: Add defensive asserts before accessing nested response structure.Line 2056 accesses
response_1.candidates[0].content.partswithout verifying the response structure exists. Add assertions to provide clear failure messages.Suggested fix
response_1 = client.models.generate_content( model=model, contents="Calculate 45 * 23 using the calculator tool.", config=types.GenerateContentConfig( tools=[calculator_tool], thinking_config=types.ThinkingConfig( thinking_level="medium" ) ) ) + assert response_1.candidates, "Response should have candidates" + assert response_1.candidates[0].content, "Candidate should have content" + assert response_1.candidates[0].content.parts, "Content should have parts" + # Extract function call and signature fc = None sig = None for part in response_1.candidates[0].content.parts:core/providers/cohere/utils.go (1)
75-86: Consider usingelse iffor cleaner mutual exclusion.The logic is correct since a value can't be both
boolandmap[string]interface{}simultaneously. However, usingelse ifwould make the mutual exclusivity explicit and avoid an unnecessary type check.♻️ Suggested improvement
// Extract additionalProperties if addPropsVal, ok := paramsMap["additionalProperties"].(bool); ok { result.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ AdditionalPropertiesBool: &addPropsVal, } - } - - if addPropsVal, ok := schemas.SafeExtractOrderedMap(paramsMap["additionalProperties"]); ok { + } else if addPropsVal, ok := schemas.SafeExtractOrderedMap(paramsMap["additionalProperties"]); ok { result.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ AdditionalPropertiesMap: &addPropsVal, } }core/providers/gemini/utils.go (1)
1146-1155: Consider usingelse iffor cleaner mutual exclusion (same as Cohere).Same pattern as
core/providers/cohere/utils.go. The logic is correct, butelse ifwould make the intent clearer.♻️ Suggested improvement
// Extract additionalProperties if additionalProps, ok := normalizedSchemaMap["additionalProperties"].(bool); ok { jsonSchema.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ AdditionalPropertiesBool: &additionalProps, } - } - - if additionalProps, ok := schemas.SafeExtractOrderedMap(normalizedSchemaMap["additionalProperties"]); ok { + } else if additionalProps, ok := schemas.SafeExtractOrderedMap(normalizedSchemaMap["additionalProperties"]); ok { jsonSchema.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ AdditionalPropertiesMap: &additionalProps, } }core/providers/anthropic/types.go (1)
303-336:AnthropicCitationsunion marshal: guard against empty-but-non-nilTextCitationscausing false “both set”.Right now
Config != nil && TextCitations != nilwill error even ifTextCitationsis an empty slice that wasmake(...)’d accidentally. Low risk, but this type looks public and easy to misuse.Possible tweak
func (ac *AnthropicCitations) MarshalJSON() ([]byte, error) { - if ac.Config != nil && ac.TextCitations != nil { + if ac.Config != nil && len(ac.TextCitations) > 0 { return nil, fmt.Errorf("AnthropicCitations: both Config and TextCitations are set; only one should be non-nil") } if ac.Config != nil { return sonic.Marshal(ac.Config) } - if ac.TextCitations != nil { + if len(ac.TextCitations) > 0 { return sonic.Marshal(ac.TextCitations) } return sonic.Marshal(nil) }core/providers/anthropic/utils.go (1)
768-778: Useelse ifto make mutual exclusivity explicit.The two independent
ifstatements work correctly becauseschemaMap["additionalProperties"]can only be aboolOR a map, never both. However, usingelse ifwould make this mutual exclusivity clearer and prevent any future confusion.Suggested improvement
if additionalProps, ok := schemaMap["additionalProperties"].(bool); ok { jsonSchema.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ AdditionalPropertiesBool: &additionalProps, } - } - - if additionalProps, ok := schemas.SafeExtractOrderedMap(schemaMap["additionalProperties"]); ok { + } else if additionalProps, ok := schemas.SafeExtractOrderedMap(schemaMap["additionalProperties"]); ok { jsonSchema.AdditionalProperties = &schemas.AdditionalPropertiesStruct{ AdditionalPropertiesMap: &additionalProps, } }core/providers/anthropic/responses.go (1)
1054-1077: Extract duplicated WebSearch sanitization logic into a helper function.The same sanitization logic (removing
blocked_domainswhenallowed_domainsis present) is duplicated in three locations:
- Here (lines 1054-1077)
- WebSearch completion handling (lines 1182-1196)
convertBifrostFunctionCallToAnthropicToolUse(lines 2912-2932)Suggested helper function
// sanitizeWebSearchArguments removes blocked_domains if allowed_domains is present // Anthropic only allows one domain filter type at a time func sanitizeWebSearchArguments(argumentsJSON string) string { var toolArgs map[string]interface{} if err := json.Unmarshal([]byte(argumentsJSON), &toolArgs); err != nil { return argumentsJSON } _, hasAllowed := toolArgs["allowed_domains"] _, hasBlocked := toolArgs["blocked_domains"] if hasAllowed && hasBlocked { delete(toolArgs, "blocked_domains") if sanitizedBytes, err := json.Marshal(toolArgs); err == nil { return string(sanitizedBytes) } } return argumentsJSON }Then replace all three occurrences with:
if bifrostResp.Item.ResponsesToolMessage.Name != nil && *bifrostResp.Item.ResponsesToolMessage.Name == "WebSearch" { argumentsJSON = sanitizeWebSearchArguments(argumentsJSON) }
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (17)
core/changelog.mdcore/internal/testutil/structured_outputs.gocore/providers/anthropic/chat.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.gocore/providers/anthropic/utils.gocore/providers/cohere/utils.gocore/providers/gemini/chat.gocore/providers/gemini/gemini_test.gocore/providers/gemini/responses.gocore/providers/gemini/types.gocore/providers/gemini/utils.gocore/schemas/chatcompletions.gocore/schemas/responses.gotests/integrations/python/tests/test_google.pytransports/bifrost-http/integrations/router.gotransports/changelog.md
💤 Files with no reviewable changes (1)
- transports/bifrost-http/integrations/router.go
✅ Files skipped from review due to trivial changes (1)
- core/changelog.md
🚧 Files skipped from review as they are similar to previous changes (2)
- core/providers/gemini/types.go
- transports/changelog.md
🧰 Additional context used
📓 Path-based instructions (1)
**
⚙️ CodeRabbit configuration file
always check the stack if there is one for the current PR. do not give localized reviews for the PR, always see all changes in the light of the whole stack of PRs (if there is a stack, if there is no stack you can continue to make localized suggestions/reviews)
Files:
core/providers/anthropic/chat.gocore/providers/cohere/utils.gocore/providers/gemini/gemini_test.gocore/providers/anthropic/utils.gocore/providers/gemini/responses.gocore/schemas/responses.gocore/providers/gemini/chat.gotests/integrations/python/tests/test_google.pycore/providers/anthropic/responses.gocore/providers/gemini/utils.gocore/internal/testutil/structured_outputs.gocore/schemas/chatcompletions.gocore/providers/anthropic/types.go
🧠 Learnings (15)
📚 Learning: 2025-12-09T17:07:42.007Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/schemas/account.go:9-18
Timestamp: 2025-12-09T17:07:42.007Z
Learning: In core/schemas/account.go, the HuggingFaceKeyConfig field within the Key struct is currently unused and reserved for future Hugging Face inference endpoint deployments. Do not flag this field as missing from OpenAPI documentation or require its presence in the API spec until the feature is actively implemented and used. When the feature is added, update the OpenAPI docs accordingly; otherwise, treat this field as non-breaking and not part of the current API surface.
Applied to files:
core/providers/anthropic/chat.gocore/providers/cohere/utils.gocore/providers/gemini/gemini_test.gocore/providers/anthropic/utils.gocore/providers/gemini/responses.gocore/schemas/responses.gocore/providers/gemini/chat.gocore/providers/anthropic/responses.gocore/providers/gemini/utils.gocore/internal/testutil/structured_outputs.gocore/schemas/chatcompletions.gocore/providers/anthropic/types.go
📚 Learning: 2025-12-29T11:54:55.836Z
Learnt from: akshaydeo
Repo: maximhq/bifrost PR: 1153
File: framework/configstore/rdb.go:2221-2246
Timestamp: 2025-12-29T11:54:55.836Z
Learning: In Go reviews, do not flag range-over-int patterns like for i := range n as compile-time errors, assuming Go 1.22+ semantics. Only flag actual range-capable values (slices, arrays, maps, channels, strings) and other compile-time issues. This applies to all Go files across the repository.
Applied to files:
core/providers/anthropic/chat.gocore/providers/cohere/utils.gocore/providers/gemini/gemini_test.gocore/providers/anthropic/utils.gocore/providers/gemini/responses.gocore/schemas/responses.gocore/providers/gemini/chat.gocore/providers/anthropic/responses.gocore/providers/gemini/utils.gocore/internal/testutil/structured_outputs.gocore/schemas/chatcompletions.gocore/providers/anthropic/types.go
📚 Learning: 2026-01-14T04:40:11.480Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1312
File: framework/modelcatalog/pricing.go:276-426
Timestamp: 2026-01-14T04:40:11.480Z
Learning: In the Bifrost codebase, ImageUsage and other usage types guarantee that TotalTokens is populated (computed as InputTokens + OutputTokens if providers don’t supply TotalTokens). Reviewers can rely on this invariant and should not assume TotalTokens may be missing when input/output tokens exist. When implementing tiering logic or token-based decisions, you can safely use TotalTokens without extra null/zero guards, provided you’re in a context where InputTokens and OutputTokens are present. If a branch might discard tokens, ensure the invariant is preserved or add explicit checks only where the inputs are confirmed to be valid.
Applied to files:
core/providers/anthropic/chat.gocore/providers/cohere/utils.gocore/providers/gemini/gemini_test.gocore/providers/anthropic/utils.gocore/providers/gemini/responses.gocore/schemas/responses.gocore/providers/gemini/chat.gocore/providers/anthropic/responses.gocore/providers/gemini/utils.gocore/internal/testutil/structured_outputs.gocore/schemas/chatcompletions.gocore/providers/anthropic/types.go
📚 Learning: 2025-12-12T10:28:54.988Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 978
File: core/providers/anthropic/chat.go:207-216
Timestamp: 2025-12-12T10:28:54.988Z
Learning: In core/providers/anthropic/chat.go, within ToAnthropicChatRequest, ensure the mapping from ReasoningDetails to Anthropic thinking blocks preserves all fields from ReasoningDetails, including when Text is nil or empty. Do not drop or coerce missing Text; propagate nil/empty values to maintain complete bidirectional mapping of Anthropic events in Bifrost. Add tests to verify that ReasoningDetails with Text nil or "" round-trip without loss, and update code to handle nullable Text without dropping data. If there are multiple blocks, preserve order and all fields during mapping.
Applied to files:
core/providers/anthropic/chat.go
📚 Learning: 2025-12-19T09:26:54.961Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/providers/utils/utils.go:1050-1051
Timestamp: 2025-12-19T09:26:54.961Z
Learning: Update streaming end-marker handling so HuggingFace is treated as a non-[DONE] provider for backends that do not emit a DONE marker (e.g., meta llama on novita). In core/providers/utils/utils.go, adjust ProviderSendsDoneMarker() (or related logic) to detect providers that may not emit DONE and avoid relying on DONE as the sole end signal. Add tests to cover both DONE-emitting and non-DONE backends, with clear documentation in code comments explaining the rationale and any fallback behavior.
Applied to files:
core/providers/anthropic/chat.gocore/providers/cohere/utils.gocore/providers/gemini/gemini_test.gocore/providers/anthropic/utils.gocore/providers/gemini/responses.gocore/providers/gemini/chat.gocore/providers/anthropic/responses.gocore/providers/gemini/utils.gocore/providers/anthropic/types.go
📚 Learning: 2026-01-10T11:27:47.535Z
Learnt from: Radheshg04
Repo: maximhq/bifrost PR: 1256
File: core/providers/openai/openai.go:2276-2385
Timestamp: 2026-01-10T11:27:47.535Z
Learning: Validate image generation requests for nil and missing prompts before dispatch. Follow the same pattern used here: core/bifrost.go validates nil/empty prompts, providerUtils.CheckContextAndGetRequestBody returns a structured error when the request converter yields nil, and apply this across all providers (including OpenAI) to avoid sending null bodies.
Applied to files:
core/providers/anthropic/chat.gocore/providers/cohere/utils.gocore/providers/gemini/gemini_test.gocore/providers/anthropic/utils.gocore/providers/gemini/responses.gocore/providers/gemini/chat.gocore/providers/anthropic/responses.gocore/providers/gemini/utils.gocore/providers/anthropic/types.go
📚 Learning: 2026-01-11T14:08:10.341Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1298
File: core/providers/anthropic/anthropic.go:682-699
Timestamp: 2026-01-11T14:08:10.341Z
Learning: In Anthroplic streaming implementations (and analogous providers), ensure that the final 'summary' chunk, which carries usage information and metadata, is emitted after all delta chunks and uses a chunk index of last_delta_index + 1. This differentiates the summary chunk from content delta chunks. Apply this convention consistently in the anthropic provider code and in similar streaming providers, and consider adding a targeted test to assert the ordering and chunk index logic.
Applied to files:
core/providers/anthropic/chat.gocore/providers/anthropic/utils.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.go
📚 Learning: 2026-01-13T13:36:35.221Z
Learnt from: TejasGhatte
Repo: maximhq/bifrost PR: 1319
File: core/providers/anthropic/responses.go:937-937
Timestamp: 2026-01-13T13:36:35.221Z
Learning: In core/providers/anthropic/responses.go, when handling Anthropic API streaming responses, ensure that content_block_start events include a signature field set to an empty string (e.g., contentBlock.Signature = ""). The actual signature is delivered later via signature_delta events. This behavior is per Anthropic's specification and should not be treated as an error. This guideline should apply to all Anthropic response handling files under core/providers/anthropic/ and similar go files that process streaming blocks.
Applied to files:
core/providers/anthropic/chat.gocore/providers/anthropic/utils.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.go
📚 Learning: 2026-01-14T06:57:42.750Z
Learnt from: TejasGhatte
Repo: maximhq/bifrost PR: 1319
File: core/providers/anthropic/types.go:248-336
Timestamp: 2026-01-14T06:57:42.750Z
Learning: For Anthropic citation types (page_location, char_location, content_block_location), ensure there is an optional string field file_id to reference uploaded files. Update the Go structs modeling these citations to include FileID *string (or string with omitempty) and document its optionality in comments, so code consuming these types can handle absence of file_id gracefully.
Applied to files:
core/providers/anthropic/chat.gocore/providers/anthropic/utils.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.go
📚 Learning: 2026-01-14T06:57:42.750Z
Learnt from: TejasGhatte
Repo: maximhq/bifrost PR: 1319
File: core/providers/anthropic/types.go:248-336
Timestamp: 2026-01-14T06:57:42.750Z
Learning: In core/providers/anthropic/types.go, ensure the web_search_result_location citation type includes a string field named 'url' alongside the existing fields 'encrypted_index', 'title', and 'cited_text'. If the field is missing, add it with type string and appropriate struct tags (e.g., json and/or db tags) and update any related serialization or usage accordingly.
Applied to files:
core/providers/anthropic/chat.gocore/providers/anthropic/utils.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.go
📚 Learning: 2026-01-14T10:53:44.658Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1326
File: core/providers/gemini/gemini.go:1679-1754
Timestamp: 2026-01-14T10:53:44.658Z
Learning: Validate image generation inputs in core/bifrost.go before invoking any provider handler. Ensure in all provider implementations (e.g., core/providers/gemini/gemini.go) that the request and request.Input are non-nil before use, to prevent nil dereferences and provide clear error handling. Apply this invariant broadly to all providers and add tests for nil input scenarios.
Applied to files:
core/providers/anthropic/chat.gocore/providers/cohere/utils.gocore/providers/gemini/gemini_test.gocore/providers/anthropic/utils.gocore/providers/gemini/responses.gocore/providers/gemini/chat.gocore/providers/anthropic/responses.gocore/providers/gemini/utils.gocore/providers/anthropic/types.go
📚 Learning: 2025-12-15T10:16:21.909Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/providers/huggingface/huggingface_test.go:12-63
Timestamp: 2025-12-15T10:16:21.909Z
Learning: In provider tests under core/providers/<provider>/*_test.go, do not require or flag the use of defer for Shutdown(); instead call client.Shutdown() at the end of each test function. This pattern appears consistent across all provider tests. Apply this rule only within this path; for other tests or resources, defer may still be appropriate.
Applied to files:
core/providers/gemini/gemini_test.go
📚 Learning: 2026-01-13T17:10:07.064Z
Learnt from: Radheshg04
Repo: maximhq/bifrost PR: 1312
File: tests/integrations/python/tests/test_openai.py:1166-1258
Timestamp: 2026-01-13T17:10:07.064Z
Learning: In tests under tests/integrations/python, prefer using the OpenAI image generation model 'gpt-image-1' via the config key providers.openai.image_generation for image-generation scenarios. This avoids DALLE-3 parameter limitations (e.g., n>1, quality/size combos). Ensure tests reference this provider in mocks/fixtures and document why this choice is used for test determinism.
Applied to files:
tests/integrations/python/tests/test_google.py
📚 Learning: 2025-12-19T08:29:20.286Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1095
File: core/internal/testutil/count_tokens.go:30-67
Timestamp: 2025-12-19T08:29:20.286Z
Learning: In core/internal/testutil test files, enforce using GetTestRetryConfigForScenario() to obtain a generic retry config, then construct a typed retry config (e.g., CountTokensRetryConfig, EmbeddingRetryConfig, TranscriptionRetryConfig) with an empty Conditions slice. Copy only MaxAttempts, BaseDelay, MaxDelay, OnRetry, and OnFinalFail from the generic config. This convention should be consistently applied across all test files in this directory.
Applied to files:
core/internal/testutil/structured_outputs.go
📚 Learning: 2025-12-17T08:44:08.788Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1114
File: core/schemas/chatcompletions.go:224-228
Timestamp: 2025-12-17T08:44:08.788Z
Learning: In core/schemas/chatcompletions.go, ensure the schema structures mirror OpenAI's API specifications exactly. Use the valid values for fields like ChatAudioParameters.Format and ChatAudioParameters.Voice as defined by OpenAI's documentation, and avoid adding additional inline documentation or constants to maintain direct compatibility with OpenAI's API.
Applied to files:
core/schemas/chatcompletions.go
🧬 Code graph analysis (12)
core/providers/anthropic/chat.go (2)
core/providers/gemini/types.go (1)
Type(825-825)core/providers/anthropic/types.go (1)
AnthropicContentBlockTypeToolResult(209-209)
core/providers/cohere/utils.go (2)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(324-327)core/schemas/utils.go (1)
SafeExtractOrderedMap(549-572)
core/providers/gemini/gemini_test.go (2)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(324-327)core/schemas/utils.go (1)
Ptr(14-16)
core/providers/anthropic/utils.go (2)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(324-327)core/schemas/utils.go (1)
SafeExtractOrderedMap(549-572)
core/providers/gemini/responses.go (3)
core/schemas/responses.go (1)
ResponsesToolMessage(475-495)core/providers/gemini/types.go (3)
Part(1030-1054)FunctionCall(1236-1246)FunctionResponse(1249-1271)core/schemas/utils.go (1)
Ptr(14-16)
core/schemas/responses.go (1)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(324-327)
core/providers/gemini/chat.go (2)
core/schemas/chatcompletions.go (3)
ChatReasoningDetails(869-877)BifrostReasoningDetailsTypeText(865-865)BifrostReasoningDetailsTypeEncrypted(864-864)core/schemas/utils.go (1)
Ptr(14-16)
tests/integrations/python/tests/test_google.py (2)
tests/integrations/python/tests/utils/common.py (1)
skip_if_no_api_key(1830-1841)core/providers/gemini/types.go (3)
Content(1016-1024)Part(1030-1054)FunctionResponse(1249-1271)
core/providers/anthropic/responses.go (4)
core/schemas/utils.go (1)
Ptr(14-16)core/providers/anthropic/types.go (2)
AnthropicStreamEvent(498-507)AnthropicStreamDelta(519-527)core/schemas/responses.go (1)
ResponsesToolMessage(475-495)core/providers/anthropic/utils.go (1)
ConvertBifrostFinishReasonToAnthropic(88-93)
core/providers/gemini/utils.go (2)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(324-327)core/schemas/utils.go (1)
SafeExtractOrderedMap(549-572)
core/schemas/chatcompletions.go (2)
core/schemas/json_native.go (2)
Marshal(8-10)Unmarshal(18-20)core/schemas/json_wasm.go (2)
Marshal(8-10)Unmarshal(22-24)
core/providers/anthropic/types.go (2)
core/schemas/chatcompletions.go (1)
CacheControl(719-722)core/schemas/responses.go (1)
Citations(421-423)
🪛 Ruff (0.14.11)
tests/integrations/python/tests/test_google.py
1682-1682: Unused method argument: test_config
(ARG002)
1772-1772: Unused method argument: test_config
(ARG002)
1906-1906: Unused method argument: test_config
(ARG002)
1961-1961: Unused method argument: test_config
(ARG002)
2016-2016: Unused method argument: test_config
(ARG002)
2099-2099: Do not catch blind exception: Exception
(BLE001)
2136-2136: Do not catch blind exception: Exception
(BLE001)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
🔇 Additional comments (23)
tests/integrations/python/tests/test_google.py (3)
1681-1770: Well-structured test for parallel function call signatures.The signature assertion fix correctly uses
function_call_part_indices[0]instead of a hardcoded index, handling cases where thought/text parts precede function calls. The test properly validates Gemini 3's behavior of attaching thought signatures only to the first function call in parallel scenarios.Consider adding a defensive assert for
response.candidates[0].content.partsbefore iteration (line 1742) to provide clearer error messages on unexpected response structure, though the candidates assertion provides baseline protection.
1905-1959: LGTM!The test properly validates thought signatures in text responses with appropriate assertions for response structure at lines 1925-1927. The implementation correctly iterates through parts to identify thought/text parts and their associated signatures.
1960-2013: LGTM!The test correctly validates different thinking levels (
minimal,low,medium,high) and checks for thought tokens in usage metadata. The parametric approach ensures comprehensive coverage of Gemini 3's thinking configuration options.core/schemas/chatcompletions.go (3)
276-282: LGTM - Type change to support both boolean and objectadditionalProperties.The field type change from
*boolto*AdditionalPropertiesStructcorrectly enables the schema to represent bothadditionalProperties: falseandadditionalProperties: { "type": "string" }patterns from JSON Schema.
297-302: LGTM - Normalization ensuresomitemptyworks correctly.Both parsing paths (string and object formats) properly normalize an empty
AdditionalPropertiesStructtonil, preventing"additionalProperties": nullin marshaled output.Also applies to: 313-318
324-379: LGTM - Well-structured union type implementation.The
AdditionalPropertiesStructcorrectly:
- Validates mutual exclusivity in
MarshalJSON- Handles explicit
nullinUnmarshalJSON- Clears the opposite field when setting one variant (prevents stale state on struct reuse)
core/schemas/responses.go (2)
131-140: LGTM - Consistent type update forAdditionalProperties.The field type change aligns with
ToolFunctionParameters.AdditionalPropertiesinchatcompletions.go, ensuring consistent handling of both boolean and object representations across the codebase.
421-423: LGTM - Simple struct for citation configuration.The
Citationsstruct is appropriately minimal with just anEnabledfield.core/internal/testutil/structured_outputs.go (2)
421-427: LGTM - Test correctly uses newAdditionalPropertiesStructwrapper.The test properly wraps the boolean value in the new struct type, maintaining compatibility with the updated schema definitions.
537-543: LGTM - Streaming test updated consistently.Same correct pattern applied to the streaming test case.
core/providers/gemini/responses.go (4)
246-286: Signature extraction + “consume next reasoning message” logic looks coherent; verify edge cases with multiple tool calls.The CallID-embedded signature decode (RawURLEncoding) and fallback to the next reasoning
EncryptedContent(StdEncoding) is consistent. TheconsumedIndicesguard should prevent emitting the signature reasoning message twice.I’d just sanity-check scenarios with multiple adjacent tool calls to ensure a single reasoning message can’t be “claimed” by the wrong call (since the fallback is “next message”).
457-477: Streaming conversion: good separation between CallID and decodedThoughtSignature.Extracting the signature substring from CallID, decoding it (RawURLEncoding), and attaching it to
functionCallPart.ThoughtSignatureis the right direction and keeps the full CallID intact.
1866-1915: Nice: avoid double-appending signatures and strip signature for function-response “name” fallback.
- The
!strings.Contains(callID, thoughtSignatureSeparator)guard prevents duplicate..._ts_..._ts_....- The function response fallback that strips the signature from
part.FunctionResponse.IDbefore assigningResponsesToolMessage.Nameshould help keep “name” stable for correlation.
2388-2412: Round-trip handling for CallID-embedded signatures is consistent.Decoding the signature from
ResponsesToolMessage.CallIDviabase64.RawURLEncodingand attaching it toPart.ThoughtSignaturekeeps the “signature transport” consistent across conversions.Also applies to: 2454-2455
core/providers/gemini/chat.go (1)
130-160: CallID signature extraction + ID normalization is consistent and readable.Stripping the embedded signature for the tool call ID while keeping the decoded signature in
reasoningDetails(with a stabletool_call_<baseId>reference) is a good approach.Also applies to: 191-198
core/providers/gemini/gemini_test.go (1)
558-562: Test update matches the newAdditionalPropertiesStructshape.core/providers/anthropic/types.go (1)
219-237: Expanded content blocks + citation structs look consistent with the intended schema.The added
Signature/Datafields and the richer citation types (includingfile_idandurl) fit the stated goal of better thought-signature and citations support.Also applies to: 248-292
core/providers/anthropic/chat.go (1)
177-180: Good: useAnthropicContentBlockTypeToolResultconstant instead of string literal.core/providers/anthropic/responses.go (5)
902-928: Consistent OutputIndex-first indexing pattern applied.The changes correctly prioritize
OutputIndexoverContentIndexfor Anthropic SSE events, with appropriate fallbacks. This ensures consistent global indexing across content_block_start and subsequent delta events.
938-941: Signature initialization follows Anthropic streaming spec.Setting
Signatureto an empty string incontent_block_startfor thinking blocks is correct per Anthropic's API specification. The actual signature value arrives later viasignature_deltaevents. Based on learnings, this is required behavior for the Anthropic SDK.
1113-1126: WebSearch delta suppression correctly implemented.The pattern of skipping argument deltas for WebSearch tools and emitting sanitized synthetic deltas on completion is correctly implemented. This ensures Anthropic receives valid arguments without conflicting domain filters.
Note: This implementation depends on proper cleanup of
webSearchItemIDs, which was flagged in an earlier comment.
1169-1226: WebSearch completion handling follows correct event sequence.The implementation correctly generates sanitized synthetic delta events followed by
content_block_stopfor WebSearch tools. The tracking cleanup at line 1222 properly removes the entry fromwebSearchItemIDs.
1278-1301: StopReason handling correctly differentiates default and explicit cases.The implementation correctly handles two cases:
- Default: Sets
StopReasontoend_turnwith emptyStopSequence- Explicit: Converts from Bifrost format with
nilStopSequenceThis maintains proper Anthropic API compatibility.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
4cf1e95 to
69a68b6
Compare
973d8fb to
363249d
Compare
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: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
core/providers/anthropic/responses.go (1)
1278-1299: Usenilinstead ofschemas.Ptr("")forStopSequenceto match Anthropic SSE schema.Per Anthropic's streaming API spec,
stop_sequenceshould benull(not empty string) when no custom stop sequence triggered the model. SinceStopSequenceinAnthropicStreamDeltalacks theomitemptyJSON tag, it will always be marshaled. Line 1280 currently sets it toschemas.Ptr(""), which serializes as"stop_sequence": "", while line 1298 correctly usesnil. Both should benilunless an actual stop sequence value is present.
🤖 Fix all issues with AI agents
In `@core/providers/anthropic/types.go`:
- Around line 248-336: The marshal/unmarshal logic must normalize empty values
so "unset" behaves like omitted: in MarshalJSON, treat TextCitations with length
0 as nil and treat Config pointer whose dereferenced value equals the zero-value
of schemas.Citations as nil (use reflect.DeepEqual or a clear isEmpty helper),
change the mutual-check to consider "set" only when Config is
non-nil-and-not-empty and TextCitations has len>0, then marshal the non-empty
one (and return sonic.Marshal(nil) only when both are empty/nil); in
UnmarshalJSON, after successful unmarshalling set TextCitations to nil when
len==0 and set Config to nil when the unmarshalled config equals the zero-value
before assigning, and keep the existing attempt-order (array first, then config)
and the final error message in UnmarshalJSON (functions:
AnthropicCitations.MarshalJSON and AnthropicCitations.UnmarshalJSON).
♻️ Duplicate comments (5)
core/providers/anthropic/responses.go (1)
62-65: GlobalwebSearchItemIDscan leak across interrupted streams.This is still a process-global
sync.Mapwith cleanup only on the “happy path” (output_item.donefor WebSearch). If a stream errors / client disconnects before that, entries can remain indefinitely.#!/bin/bash # Find all deletes/cleanup paths for webSearchItemIDs and where stream teardown happens. rg -n --type go 'webSearchItemIDs\.(Store|Delete|Load)' -C2 rg -n --type go 'releaseAnthropicResponsesStreamState|acquireAnthropicResponsesStreamState' -C2 rg -n --type go 'ToAnthropicResponsesStreamResponse\(' -C2core/providers/gemini/chat.go (2)
94-103: Non-stream path can still dropThoughtSignaturedue tocontinue.If Gemini ever returns a part with both
Thought=truetext andThoughtSignature(or other data you handle later), thecontinueskips the signature logic. This was previously raised and still applies to the current structure.Proposed diff (remove `continue`, keep processing other fields)
- // Handle thought/reasoning text separately - add to reasoning details - if part.Text != "" && part.Thought { - reasoningDetails = append(reasoningDetails, schemas.ChatReasoningDetails{ - Index: len(reasoningDetails), - Type: schemas.BifrostReasoningDetailsTypeText, - Text: &part.Text, - }) - continue - } - - // Handle regular text - if part.Text != "" { - contentBlocks = append(contentBlocks, schemas.ChatContentBlock{ - Type: schemas.ChatContentBlockTypeText, - Text: &part.Text, - }) - } + // Handle text vs thought text, but don't `continue` (signatures can co-exist). + if part.Text != "" { + if part.Thought { + reasoningDetails = append(reasoningDetails, schemas.ChatReasoningDetails{ + Index: len(reasoningDetails), + Type: schemas.BifrostReasoningDetailsTypeText, + Text: &part.Text, + }) + } else { + contentBlocks = append(contentBlocks, schemas.ChatContentBlock{ + Type: schemas.ChatContentBlockTypeText, + Text: &part.Text, + }) + } + }Gemini GenerateContentResponse Part fields: can a single part contain thought=true text and thoughtSignature and/or functionCall simultaneously?Also applies to: 176-202
342-350: Streaming extracted-signature reasoningDetails is missing correlationID.Non-stream adds
ID: tool_call_<callID>when a signature is extracted from CallID; streaming doesn’t, so consumers can’t reliably associate signature ↔ tool call. This was previously raised and appears to have regressed / still be present in the current code.Proposed diff
if len(extractedSig) > 0 { thoughtSig := base64.StdEncoding.EncodeToString(extractedSig) reasoningDetails = append(reasoningDetails, schemas.ChatReasoningDetails{ Index: len(reasoningDetails), Type: schemas.BifrostReasoningDetailsTypeEncrypted, Signature: &thoughtSig, + ID: schemas.Ptr(fmt.Sprintf("tool_call_%s", callID)), }) }core/schemas/chatcompletions.go (1)
331-379: Consider hardening MarshalJSON to avoid emittingadditionalProperties: nullfrom empty/invalid struct values.Even with the
ToolFunctionParametersnormalization, other callers could still create a non-nil*AdditionalPropertiesStructwith both variants nil and accidentally serializenull. Consider either:
- returning an error when both variants are nil, or
- normalizing empty/invalid instances to “omit field” at the parent level (where
omitemptycan apply).tests/integrations/python/tests/test_google.py (1)
1681-2144: Fix Ruff violations + add basic response asserts in the new Gemini 3 tests.
- ARG002:
test_configis unused intest_30a–test_30e. Easiest fix is renaming to_test_config(or drop the arg and use@pytest.mark.usefixtures("test_config")).- BLE001: replace broad
except Exceptionintest_30ewith SDK-specific exceptions, or add# noqa: BLE001on thoseexceptlines.- Add
assert response_*.candidates(and ideallycontent.parts) before indexing intest_30b/test_30eto avoid IndexError-style failures.Proposed diff (minimal, lint-focused)
@@ - def test_30a_gemini_3_parallel_function_calls_signatures(self, test_config): + def test_30a_gemini_3_parallel_function_calls_signatures(self, _test_config): @@ - def test_30b_gemini_3_sequential_function_calls_signatures(self, test_config): + def test_30b_gemini_3_sequential_function_calls_signatures(self, _test_config): @@ - # Extract first function call and signature + assert response_1.candidates, "Response should have candidates" + assert response_1.candidates[0].content, "Candidate should have content" + assert response_1.candidates[0].content.parts, "Candidate content should have parts" + # Extract first function call and signature @@ - def test_30c_gemini_3_thought_signatures_in_text_responses(self, test_config): + def test_30c_gemini_3_thought_signatures_in_text_responses(self, _test_config): @@ - def test_30d_gemini_3_thinking_levels(self, test_config): + def test_30d_gemini_3_thinking_levels(self, _test_config): @@ - def test_30e_gemini_3_signature_validation_strict(self, test_config): + def test_30e_gemini_3_signature_validation_strict(self, _test_config): @@ - for part in response_1.candidates[0].content.parts: + assert response_1.candidates, "Response should have candidates" + assert response_1.candidates[0].content, "Candidate should have content" + assert response_1.candidates[0].content.parts, "Candidate content should have parts" + for part in response_1.candidates[0].content.parts: @@ - except Exception as e: + except Exception as e: # noqa: BLE001 pytest.fail(f"Request with signature should not fail: {e}") @@ - except Exception as e: + except Exception as e: # noqa: BLE001 error_msg = str(e).lower()
🧹 Nitpick comments (4)
core/providers/anthropic/types.go (1)
219-238: Good field additions for thinking/web_search_result blocks; watchomitempty+ pointer semantics.
Signature/Data+ web search fields look consistent with the rest of the model, and using pointers keepsomitemptybehavior predictable. 👍
One practical gotcha: any code that setsCitations: &AnthropicCitations{}(instead of leaving itnil) will still serialize acitationskey (likely asnull), becauseomitemptyonly omits nil pointers. Consider guarding in constructors / converters to keep it nil unless you intend to emit it.core/providers/anthropic/responses.go (1)
1054-1077: Deduplicate WebSearch-args sanitization + fix misleading comment (“remove both”).The same “if allowed_domains and blocked_domains both exist, drop blocked_domains” logic appears in multiple places, which risks drift. Also the comment says “remove both allowed_domains and blocked_domains” but the code removes only
blocked_domains.Consider a shared helper (keeps behavior consistent across streaming + non-stream conversions):
Proposed diff (sketch)
+func sanitizeAnthropicWebSearchArgs(argumentsJSON string) string { + var toolArgs map[string]interface{} + if err := json.Unmarshal([]byte(argumentsJSON), &toolArgs); err != nil { + return argumentsJSON + } + _, hasAllowed := toolArgs["allowed_domains"] + _, hasBlocked := toolArgs["blocked_domains"] + if hasAllowed && hasBlocked { + delete(toolArgs, "blocked_domains") + if b, err := json.Marshal(toolArgs); err == nil { + return string(b) + } + } + return argumentsJSON +}Then replace each inline sanitization block with:
-// Sanitize websearch tool arguments to remove both allowed_domains and blocked_domains -// Anthropic only allows one or the other, not both +// Sanitize WebSearch tool arguments: if both allowed_domains and blocked_domains exist, drop blocked_domains. if shouldGenerateDeltas && argumentsJSON != "" { if bifrostResp.Item.ResponsesToolMessage.Name != nil && *bifrostResp.Item.ResponsesToolMessage.Name == "WebSearch" { - ... inline sanitize ... + argumentsJSON = sanitizeAnthropicWebSearchArgs(argumentsJSON) } }Also applies to: 1169-1197, 2912-2935
core/providers/gemini/responses.go (1)
2414-2425: Look-ahead may overwrite signature already extracted from CallID.The look-ahead logic at lines 2414-2425 unconditionally sets
part.ThoughtSignaturefrom the next reasoning message, potentially overwriting a valid signature that was already extracted from theCallIDat lines 2405-2412.Consider adding a guard to preserve the CallID-extracted signature:
♻️ Suggested fix
- // Preserve thought signature from ResponsesReasoning message (required for Gemini 3 Pro) - // Look ahead to see if the next message is a reasoning message with encrypted content - if i+1 < len(messages) { + // Preserve thought signature from ResponsesReasoning message (required for Gemini 3 Pro) + // Look ahead only if we didn't already extract a signature from CallID + if len(part.ThoughtSignature) == 0 && i+1 < len(messages) { nextMsg := messages[i+1] if nextMsg.Type != nil && *nextMsg.Type == schemas.ResponsesMessageTypeReasoning && nextMsg.ResponsesReasoning != nil && nextMsg.ResponsesReasoning.EncryptedContent != nil { decodedSig, err := base64.StdEncoding.DecodeString(*nextMsg.ResponsesReasoning.EncryptedContent) if err == nil { part.ThoughtSignature = decodedSig } } }core/providers/anthropic/utils.go (1)
768-778: AdditionalProperties wrapping is correct; consider making exclusivity explicit + verify request-side parity.
- Minor: make the second check an
else ifto encode “one-of” more clearly (and avoid accidental overwrites if future types broaden).- Please also verify the Responses → Anthropic path preserves
AdditionalPropertiesMapwhen present (this change is only the Anthropic → Responses direction).
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (17)
core/changelog.mdcore/internal/testutil/structured_outputs.gocore/providers/anthropic/chat.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.gocore/providers/anthropic/utils.gocore/providers/cohere/utils.gocore/providers/gemini/chat.gocore/providers/gemini/gemini_test.gocore/providers/gemini/responses.gocore/providers/gemini/types.gocore/providers/gemini/utils.gocore/schemas/chatcompletions.gocore/schemas/responses.gotests/integrations/python/tests/test_google.pytransports/bifrost-http/integrations/router.gotransports/changelog.md
💤 Files with no reviewable changes (1)
- transports/bifrost-http/integrations/router.go
✅ Files skipped from review due to trivial changes (1)
- core/changelog.md
🚧 Files skipped from review as they are similar to previous changes (5)
- core/providers/cohere/utils.go
- core/providers/anthropic/chat.go
- core/schemas/responses.go
- core/providers/gemini/types.go
- core/internal/testutil/structured_outputs.go
🧰 Additional context used
📓 Path-based instructions (1)
**
⚙️ CodeRabbit configuration file
always check the stack if there is one for the current PR. do not give localized reviews for the PR, always see all changes in the light of the whole stack of PRs (if there is a stack, if there is no stack you can continue to make localized suggestions/reviews)
Files:
core/providers/anthropic/utils.gotransports/changelog.mdcore/providers/gemini/utils.gocore/providers/gemini/chat.gocore/providers/gemini/responses.gocore/providers/gemini/gemini_test.gocore/schemas/chatcompletions.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.gotests/integrations/python/tests/test_google.py
🧠 Learnings (14)
📚 Learning: 2025-12-09T17:07:42.007Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/schemas/account.go:9-18
Timestamp: 2025-12-09T17:07:42.007Z
Learning: In core/schemas/account.go, the HuggingFaceKeyConfig field within the Key struct is currently unused and reserved for future Hugging Face inference endpoint deployments. Do not flag this field as missing from OpenAPI documentation or require its presence in the API spec until the feature is actively implemented and used. When the feature is added, update the OpenAPI docs accordingly; otherwise, treat this field as non-breaking and not part of the current API surface.
Applied to files:
core/providers/anthropic/utils.gocore/providers/gemini/utils.gocore/providers/gemini/chat.gocore/providers/gemini/responses.gocore/providers/gemini/gemini_test.gocore/schemas/chatcompletions.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.go
📚 Learning: 2025-12-29T11:54:55.836Z
Learnt from: akshaydeo
Repo: maximhq/bifrost PR: 1153
File: framework/configstore/rdb.go:2221-2246
Timestamp: 2025-12-29T11:54:55.836Z
Learning: In Go reviews, do not flag range-over-int patterns like for i := range n as compile-time errors, assuming Go 1.22+ semantics. Only flag actual range-capable values (slices, arrays, maps, channels, strings) and other compile-time issues. This applies to all Go files across the repository.
Applied to files:
core/providers/anthropic/utils.gocore/providers/gemini/utils.gocore/providers/gemini/chat.gocore/providers/gemini/responses.gocore/providers/gemini/gemini_test.gocore/schemas/chatcompletions.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.go
📚 Learning: 2026-01-14T04:40:11.480Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1312
File: framework/modelcatalog/pricing.go:276-426
Timestamp: 2026-01-14T04:40:11.480Z
Learning: In the Bifrost codebase, ImageUsage and other usage types guarantee that TotalTokens is populated (computed as InputTokens + OutputTokens if providers don’t supply TotalTokens). Reviewers can rely on this invariant and should not assume TotalTokens may be missing when input/output tokens exist. When implementing tiering logic or token-based decisions, you can safely use TotalTokens without extra null/zero guards, provided you’re in a context where InputTokens and OutputTokens are present. If a branch might discard tokens, ensure the invariant is preserved or add explicit checks only where the inputs are confirmed to be valid.
Applied to files:
core/providers/anthropic/utils.gocore/providers/gemini/utils.gocore/providers/gemini/chat.gocore/providers/gemini/responses.gocore/providers/gemini/gemini_test.gocore/schemas/chatcompletions.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.go
📚 Learning: 2026-01-14T13:30:28.760Z
Learnt from: Radheshg04
Repo: maximhq/bifrost PR: 1326
File: plugins/semanticcache/test_utils.go:545-559
Timestamp: 2026-01-14T13:30:28.760Z
Learning: In the maximhq/bifrost repository, prefer using bifrost.Ptr() to create pointers instead of the address operator (&) even when & would be valid syntactically. Apply this consistently across all code paths, including test utilities, to improve consistency and readability. Replace occurrences of &value where a *T is expected with bifrost.Ptr(value) (or an equivalent call) and ensure the function is in scope and used correctly for the target pointer type.
Applied to files:
core/providers/anthropic/utils.gocore/providers/gemini/utils.gocore/providers/gemini/chat.gocore/providers/gemini/responses.gocore/providers/gemini/gemini_test.gocore/schemas/chatcompletions.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.go
📚 Learning: 2025-12-19T09:26:54.961Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/providers/utils/utils.go:1050-1051
Timestamp: 2025-12-19T09:26:54.961Z
Learning: Update streaming end-marker handling so HuggingFace is treated as a non-[DONE] provider for backends that do not emit a DONE marker (e.g., meta llama on novita). In core/providers/utils/utils.go, adjust ProviderSendsDoneMarker() (or related logic) to detect providers that may not emit DONE and avoid relying on DONE as the sole end signal. Add tests to cover both DONE-emitting and non-DONE backends, with clear documentation in code comments explaining the rationale and any fallback behavior.
Applied to files:
core/providers/anthropic/utils.gocore/providers/gemini/utils.gocore/providers/gemini/chat.gocore/providers/gemini/responses.gocore/providers/gemini/gemini_test.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.go
📚 Learning: 2026-01-10T11:27:47.535Z
Learnt from: Radheshg04
Repo: maximhq/bifrost PR: 1256
File: core/providers/openai/openai.go:2276-2385
Timestamp: 2026-01-10T11:27:47.535Z
Learning: Validate image generation requests for nil and missing prompts before dispatch. Follow the same pattern used here: core/bifrost.go validates nil/empty prompts, providerUtils.CheckContextAndGetRequestBody returns a structured error when the request converter yields nil, and apply this across all providers (including OpenAI) to avoid sending null bodies.
Applied to files:
core/providers/anthropic/utils.gocore/providers/gemini/utils.gocore/providers/gemini/chat.gocore/providers/gemini/responses.gocore/providers/gemini/gemini_test.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.go
📚 Learning: 2026-01-11T14:08:10.341Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1298
File: core/providers/anthropic/anthropic.go:682-699
Timestamp: 2026-01-11T14:08:10.341Z
Learning: In Anthroplic streaming implementations (and analogous providers), ensure that the final 'summary' chunk, which carries usage information and metadata, is emitted after all delta chunks and uses a chunk index of last_delta_index + 1. This differentiates the summary chunk from content delta chunks. Apply this convention consistently in the anthropic provider code and in similar streaming providers, and consider adding a targeted test to assert the ordering and chunk index logic.
Applied to files:
core/providers/anthropic/utils.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.go
📚 Learning: 2026-01-13T13:36:35.221Z
Learnt from: TejasGhatte
Repo: maximhq/bifrost PR: 1319
File: core/providers/anthropic/responses.go:937-937
Timestamp: 2026-01-13T13:36:35.221Z
Learning: In core/providers/anthropic/responses.go, when handling Anthropic API streaming responses, ensure that content_block_start events include a signature field set to an empty string (e.g., contentBlock.Signature = ""). The actual signature is delivered later via signature_delta events. This behavior is per Anthropic's specification and should not be treated as an error. This guideline should apply to all Anthropic response handling files under core/providers/anthropic/ and similar go files that process streaming blocks.
Applied to files:
core/providers/anthropic/utils.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.go
📚 Learning: 2026-01-14T06:57:42.750Z
Learnt from: TejasGhatte
Repo: maximhq/bifrost PR: 1319
File: core/providers/anthropic/types.go:248-336
Timestamp: 2026-01-14T06:57:42.750Z
Learning: For Anthropic citation types (page_location, char_location, content_block_location), ensure there is an optional string field file_id to reference uploaded files. Update the Go structs modeling these citations to include FileID *string (or string with omitempty) and document its optionality in comments, so code consuming these types can handle absence of file_id gracefully.
Applied to files:
core/providers/anthropic/utils.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.go
📚 Learning: 2026-01-14T06:57:42.750Z
Learnt from: TejasGhatte
Repo: maximhq/bifrost PR: 1319
File: core/providers/anthropic/types.go:248-336
Timestamp: 2026-01-14T06:57:42.750Z
Learning: In core/providers/anthropic/types.go, ensure the web_search_result_location citation type includes a string field named 'url' alongside the existing fields 'encrypted_index', 'title', and 'cited_text'. If the field is missing, add it with type string and appropriate struct tags (e.g., json and/or db tags) and update any related serialization or usage accordingly.
Applied to files:
core/providers/anthropic/utils.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.go
📚 Learning: 2026-01-14T10:53:44.658Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1326
File: core/providers/gemini/gemini.go:1679-1754
Timestamp: 2026-01-14T10:53:44.658Z
Learning: Validate image generation inputs in core/bifrost.go before invoking any provider handler. Ensure in all provider implementations (e.g., core/providers/gemini/gemini.go) that the request and request.Input are non-nil before use, to prevent nil dereferences and provide clear error handling. Apply this invariant broadly to all providers and add tests for nil input scenarios.
Applied to files:
core/providers/anthropic/utils.gocore/providers/gemini/utils.gocore/providers/gemini/chat.gocore/providers/gemini/responses.gocore/providers/gemini/gemini_test.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.go
📚 Learning: 2025-12-15T10:16:21.909Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/providers/huggingface/huggingface_test.go:12-63
Timestamp: 2025-12-15T10:16:21.909Z
Learning: In provider tests under core/providers/<provider>/*_test.go, do not require or flag the use of defer for Shutdown(); instead call client.Shutdown() at the end of each test function. This pattern appears consistent across all provider tests. Apply this rule only within this path; for other tests or resources, defer may still be appropriate.
Applied to files:
core/providers/gemini/gemini_test.go
📚 Learning: 2025-12-17T08:44:08.788Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1114
File: core/schemas/chatcompletions.go:224-228
Timestamp: 2025-12-17T08:44:08.788Z
Learning: In core/schemas/chatcompletions.go, ensure the schema structures mirror OpenAI's API specifications exactly. Use the valid values for fields like ChatAudioParameters.Format and ChatAudioParameters.Voice as defined by OpenAI's documentation, and avoid adding additional inline documentation or constants to maintain direct compatibility with OpenAI's API.
Applied to files:
core/schemas/chatcompletions.go
📚 Learning: 2026-01-13T17:10:07.064Z
Learnt from: Radheshg04
Repo: maximhq/bifrost PR: 1312
File: tests/integrations/python/tests/test_openai.py:1166-1258
Timestamp: 2026-01-13T17:10:07.064Z
Learning: In tests under tests/integrations/python, prefer using the OpenAI image generation model 'gpt-image-1' via the config key providers.openai.image_generation for image-generation scenarios. This avoids DALLE-3 parameter limitations (e.g., n>1, quality/size combos). Ensure tests reference this provider in mocks/fixtures and document why this choice is used for test determinism.
Applied to files:
tests/integrations/python/tests/test_google.py
🧬 Code graph analysis (8)
core/providers/anthropic/utils.go (2)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(324-327)core/schemas/utils.go (1)
SafeExtractOrderedMap(549-572)
core/providers/gemini/utils.go (2)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(324-327)core/schemas/utils.go (1)
SafeExtractOrderedMap(549-572)
core/providers/gemini/chat.go (2)
core/schemas/chatcompletions.go (3)
ChatReasoningDetails(869-877)BifrostReasoningDetailsTypeText(865-865)BifrostReasoningDetailsTypeEncrypted(864-864)core/schemas/utils.go (1)
Ptr(14-16)
core/providers/gemini/gemini_test.go (2)
core/schemas/chatcompletions.go (1)
AdditionalPropertiesStruct(324-327)core/schemas/utils.go (1)
Ptr(14-16)
core/schemas/chatcompletions.go (2)
core/schemas/json_native.go (2)
Marshal(8-10)Unmarshal(18-20)core/schemas/json_wasm.go (2)
Marshal(8-10)Unmarshal(22-24)
core/providers/anthropic/responses.go (4)
core/schemas/utils.go (1)
Ptr(14-16)core/providers/anthropic/types.go (2)
AnthropicStreamEvent(498-507)AnthropicStreamDelta(519-527)core/schemas/responses.go (2)
ResponsesToolMessage(475-495)ResponsesMessageTypeFunctionCall(300-300)core/providers/anthropic/utils.go (1)
ConvertBifrostFinishReasonToAnthropic(88-93)
core/providers/anthropic/types.go (4)
core/schemas/chatcompletions.go (1)
CacheControl(719-722)core/schemas/responses.go (1)
Citations(421-423)plugins/governance/main.go (1)
Config(33-35)plugins/maxim/main.go (1)
Config(30-33)
tests/integrations/python/tests/test_google.py (1)
core/providers/gemini/types.go (4)
Tool(647-674)Content(1016-1024)Part(1030-1054)FunctionResponse(1249-1271)
🪛 Ruff (0.14.11)
tests/integrations/python/tests/test_google.py
1682-1682: Unused method argument: test_config
(ARG002)
1772-1772: Unused method argument: test_config
(ARG002)
1906-1906: Unused method argument: test_config
(ARG002)
1961-1961: Unused method argument: test_config
(ARG002)
2016-2016: Unused method argument: test_config
(ARG002)
2099-2099: Do not catch blind exception: Exception
(BLE001)
2136-2136: Do not catch blind exception: Exception
(BLE001)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
- GitHub Check: Graphite / mergeability_check
🔇 Additional comments (15)
core/providers/anthropic/responses.go (2)
902-928: OutputIndex-first indexing is consistently applied (good).The OutputIndex→ContentIndex fallback pattern is now used across start/delta/stop conversions, which should prevent index mismatches for downstream clients.
Also applies to: 1096-1104, 1121-1127, 1141-1148, 1236-1241, 1262-1269, 1303-1311, 1325-1333
941-949: Keep emptysignatureon thinkingcontent_block_start(expected Anthropic streaming behavior).Initializing
contentBlock.Signature = schemas.Ptr("")for thinking blocks is intentional here; the actual signature arrives viasignature_deltalater. This matches the repo’s established Anthropic streaming behavior. Based on learnings, this should not be removed.Also applies to: 955-962, 983-1002
core/providers/gemini/gemini_test.go (1)
558-562: Test updated correctly forAdditionalPropertiesStruct.This matches the new schema shape (
AdditionalPropertiesBool) and keeps the intent ofadditionalProperties: false.core/providers/gemini/responses.go (8)
246-286: LGTM - Thought signature extraction with proper encoding.The extraction logic correctly:
- Uses
RawURLEncodingfor decoding signatures embedded in tool IDs (line 256)- Falls back to
StdEncodingfor JSON field values from reasoning messages (line 278)- Tracks consumed reasoning messages to avoid duplicate processing
338-345: LGTM - Standalone reasoning message handling.Correctly decodes encrypted content using
StdEncoding(appropriate for JSON field values) and creates a Part with the thought signature.
457-477: LGTM - Streaming path signature extraction is consistent.The streaming path correctly mirrors the non-streaming extraction logic:
- Extracts signature portion from CallID
- Preserves full CallID in
functionCall.ID- Uses
RawURLEncodingfor decoding (matching the encoding used at line 1059)
1057-1099: LGTM - Function call signature embedding with proper guards.The implementation correctly:
- Guards against double-appending with
!strings.Contains(toolUseID, thoughtSignatureSeparator)- Uses
RawURLEncodingto produce Gemini-compliant tool IDs (onlya-zA-Z0-9_-)- Propagates the augmented ID consistently to both
addedEventanddoneEvent
1866-1871: LGTM - Non-streaming path matches streaming implementation.Consistent with
processGeminiFunctionCallPart: uses the same guard against double-appending andRawURLEncodingfor Gemini tool ID compliance.
1903-1915: LGTM - Function response name extraction handles embedded signatures.Correctly extracts the base tool name when the ID contains an embedded thought signature, falling back to the full ID when no signature is present.
2450-2457: LGTM - FunctionResponse ID preserves full CallID.Correctly sets the
FunctionResponse.IDto the fullCallID, maintaining consistency for function call/response correlation.
887-890: Verify: StdEncoding is appropriate for JSON Signature fields.Lines 888, 992, 1516, 1601, 1846, and 2026 all use
base64.StdEncodingfor encodingThoughtSignatureinto JSON fields (not tool IDs). This is correct since JSON field values can contain any characters, unlike tool IDs which have character constraints.transports/changelog.md (1)
1-6: Changelog entries look consistent with the PR/stack scope.No issues here—this reads like a concise rollup of the stack’s user-facing changes.
core/providers/gemini/utils.go (1)
1145-1155: LGTM: AdditionalProperties now supports bool-or-object consistently.core/schemas/chatcompletions.go (1)
276-320: Schema API change + “null → omitted” normalization is solid.tests/integrations/python/tests/test_google.py (1)
661-661: No concerns.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
363249d to
e443fbf
Compare
69a68b6 to
37fc678
Compare
e443fbf to
ee628de
Compare
37fc678 to
03fac97
Compare
Merge activity
|

Summary
Improves Anthropic and Gemini provider compatibility by fixing index handling and enhancing thought signature support.
Changes
Type of change
Affected areas
How to test
Test Anthropic API compatibility with Claude 3 models:
Test with real API calls to verify proper index handling and thought signature preservation.
Breaking changes
Related issues
Fixes issues with Anthropic API compatibility and Gemini thought signature handling.
Security considerations
No security implications. Changes focus on API compatibility and data structure handling.
Checklist