Skip to content

refactor: standardize request body handling and error messages across providers - #730

Closed
Pratham-Mishra04 wants to merge 1 commit into
10-31-feat-http-response-decoding-and-ctx-level-req-control-addedfrom
11-02-feat_core_providers_standardization_refactors
Closed

refactor: standardize request body handling and error messages across providers#730
Pratham-Mishra04 wants to merge 1 commit into
10-31-feat-http-response-decoding-and-ctx-level-req-control-addedfrom
11-02-feat_core_providers_standardization_refactors

Conversation

@Pratham-Mishra04

Copy link
Copy Markdown
Collaborator

Summary

Refactored provider request handling to improve error handling, support gzip responses, and standardize unsupported operation errors across providers.

Changes

  • Added checkContextAndGetRequestBody utility to centralize request body preparation and error handling
  • Implemented checkAndDecodeBody to properly handle gzipped responses
  • Standardized unsupported operation error messages using request types instead of hardcoded strings
  • Improved empty API key handling to make it consistent across providers
  • Enhanced error handling in streaming operations
  • Fixed response body handling to avoid potential use-after-free issues
  • Added support for user ID in Anthropic requests via metadata field
  • Added support for reasoning content in Anthropic responses

Type of change

  • Bug fix
  • Feature
  • Refactor
  • Documentation
  • Chore/CI

Affected areas

  • Core (Go)
  • Transports (HTTP)
  • Providers/Integrations
  • Plugins
  • UI (Next.js)
  • Docs

How to test

Test various provider API calls with and without API keys:

# Core/Transports
go test ./core/providers/...
go test ./transports/bifrost-http/...

# Test with gzipped responses
curl -H "Accept-Encoding: gzip" http://localhost:8000/v1/models

Breaking changes

  • Yes
  • No

Related issues

Improves error handling and response processing across all providers.

Security considerations

Improved handling of API keys and authentication headers.

Checklist

  • I added/updated tests where appropriate
  • I verified builds succeed (Go and UI)
  • I verified the CI pipeline passes locally if applicable

Pratham-Mishra04 commented Nov 2, 2025

Copy link
Copy Markdown
Collaborator Author

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@coderabbitai

coderabbitai Bot commented Nov 2, 2025

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added support for user metadata tracking in Anthropic responses
    • Added support for reasoning/thinking content in Anthropic responses
    • Added raw response passthrough for provider-specific response formats
  • Bug Fixes

    • Improved error handling and response decoding across all providers
    • Added nil-safety checks for request body processing
    • Fixed potential issues with missing API keys/credentials
  • Refactor

    • Standardized request and response processing across all AI providers for improved consistency and reliability

Walkthrough

This PR refactors request and response handling across multiple AI provider implementations. Key changes include: migrating from generic interface{} to pre-serialized JSON []byte for request bodies, conditionally setting authentication headers only when credentials are present, implementing centralized error handling using schema-based constants, adding context-based request body caching and decoding utilities, and introducing raw response passthrough for specific providers.

Changes

Cohort / File(s) Summary
Core utility functions and request/response handling
core/providers/utils.go, core/utils.go
Introduced checkContextAndGetRequestBody, getRequestBodyFromContext, and checkAndDecodeBody helpers for centralized request body marshaling and response decoding with gzip support. Updated newUnsupportedOperationError signature to accept typed RequestType and ModelProvider instead of strings. Added isKeySkippingAllowed conditional for provider-specific key handling.
Anthropic provider refactoring
core/providers/anthropic.go, core/schemas/providers/anthropic/types.go, core/schemas/providers/anthropic/responses.go
Changed completeRequest to accept pre-serialized JSON []byte. Conditionally set x-api-key header. Updated error construction to use schema constants. Enhanced metadata propagation (User field) and thinking/reasoning content handling with signature support.
Azure provider refactoring
core/providers/azure.go
Updated completeRequest signature to accept jsonData []byte and path string. Reworked URL construction via getPathFromContext(). Introduced checkAndDecodeBody for response handling. Replaced inline error strings with schema-based constants.
Bedrock provider refactoring
core/providers/bedrock.go
Modified completeRequest and makeStreamingRequest to accept pre-serialized jsonData []byte. Replaced bedrock.ToBedrockXRequest calls with checkContextAndGetRequestBody. Updated unsupported operation errors to use schema constants.
Cerebras, Cohere, OpenRouter, Groq, Mistral, Ollama, Parasail, SGL providers
core/providers/cerebras.go, core/providers/cohere.go, core/providers/openrouter.go, core/providers/groq.go, core/providers/mistral.go, core/providers/ollama.go, core/providers/parasail.go, core/providers/sgl.go
Conditional auth header construction (only set when key.Value non-empty). Updated unsupported operation errors to use schema-based constants and provider.GetProviderKey(). Minor URL construction changes via getPathFromContext() for relevant providers.
Gemini provider refactoring
core/providers/gemini.go, core/schemas/providers/gemini/speech.go
Introduced completeRequest helper method for speech endpoints. Added checkContextAndGetRequestBody for centralized request body handling. Conditional auth header setup. Updated ToGeminiSpeechRequest signature to remove responseModalities parameter. Enhanced error handling with checkAndDecodeBody.
OpenAI provider refactoring
core/providers/openai.go
Migrated to checkContextAndGetRequestBody for request body preparation. Updated three streaming handlers' signatures to accept authHeader map[string]string. Dynamic auth header construction per-request. Enhanced response body decoding via checkAndDecodeBody. Adjusted streaming workflows to dynamically set Stream/StreamOptions flags.
Vertex provider refactoring
core/providers/vertex.go, core/schemas/providers/vertex/types.go
Reworked ChatCompletion with context-aware request body construction supporting both Claude and OpenAI converters. Updated Embedding path to use checkContextAndGetRequestBody. Added DefaultVertexAnthropicVersion constant. Enhanced error parsing for Vertex/OpenAI formats. Updated unsupported operation errors to use schema constants.
Core schema updates
core/schemas/responses.go
Added optional User field (pointer to string) to ResponsesParameters struct for user metadata propagation.
Framework and transport layer
framework/modelcatalog/main.go, transports/bifrost-http/handlers/server.go, transports/bifrost-http/integrations/anthropic.go, transports/bifrost-http/integrations/genai.go, transports/bifrost-http/integrations/openai.go
Added nil-check guard in AddModelDataToPool. Changed model bootstrap to log errors instead of returning. Introduced conditional raw response passthrough for Anthropic, Gemini, and OpenAI providers when RawResponse is present in ExtraFields.

Sequence Diagrams

sequenceDiagram
    participant Caller
    participant Provider
    participant checkContextAndGetRequestBody
    participant Converter
    participant JSON_Marshal
    participant Handler

    Caller->>Provider: Call TextCompletion(ctx, request)
    Provider->>checkContextAndGetRequestBody: checkContextAndGetRequestBody(ctx, converter)
    checkContextAndGetRequestBody->>checkContextAndGetRequestBody: Check cached body in context
    alt Body cached
        checkContextAndGetRequestBody-->>Provider: Return cached jsonData
    else Body not cached
        checkContextAndGetRequestBody->>Converter: Call converter function
        Converter-->>checkContextAndGetRequestBody: Return (converted body, error)
        alt Conversion error
            checkContextAndGetRequestBody-->>Provider: Return BifrostError
        else Conversion OK
            checkContextAndGetRequestBody->>JSON_Marshal: Marshal to JSON
            alt Marshal error
                checkContextAndGetRequestBody-->>Provider: Return BifrostError
            else Marshal OK
                checkContextAndGetRequestBody-->>Provider: Return jsonData []byte
            end
        end
    end
    Provider->>Handler: Call completeRequest(ctx, jsonData, url, key)
    Handler->>Handler: Set conditional auth header (if key non-empty)
    Handler->>Handler: Execute HTTP request with jsonData as body
    Handler->>Handler: Call checkAndDecodeBody(response)
    Handler->>Handler: Handle gzip decompression if needed
    Handler-->>Provider: Return decoded body + latency + error
    Provider-->>Caller: Return result
Loading
sequenceDiagram
    participant Handler
    participant HTTP_Response
    participant checkAndDecodeBody
    participant Decompressor
    participant JSON_Unmarshal

    Handler->>HTTP_Response: Receive response
    Handler->>checkAndDecodeBody: checkAndDecodeBody(response)
    checkAndDecodeBody->>checkAndDecodeBody: Check Content-Encoding header
    alt Encoding == gzip
        checkAndDecodeBody->>Decompressor: Create gzip.NewReader
        Decompressor-->>checkAndDecodeBody: Return reader
        checkAndDecodeBody->>checkAndDecodeBody: Read and decompress
        alt Decompression error
            checkAndDecodeBody-->>Handler: Return error
        else Decompression OK
            checkAndDecodeBody-->>Handler: Return decompressed body
        end
    else No gzip encoding
        checkAndDecodeBody->>HTTP_Response: Read body directly
        checkAndDecodeBody-->>Handler: Return raw body
    end
    Handler->>JSON_Unmarshal: Unmarshal decoded body to schema
    alt Unmarshal error
        Handler->>Handler: Create BifrostError with provider-specific context
        Handler-->>Caller: Return error
    else Unmarshal OK
        Handler-->>Caller: Return parsed response
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Areas requiring extra attention:

  • Signature changes across streaming handlers: handleOpenAITextCompletionStreaming, handleOpenAIChatCompletionStreaming, and handleOpenAIResponsesStreaming now accept authHeader map[string]string parameter; verify all call sites are properly updated.
  • Request body caching logic: checkContextAndGetRequestBody and getRequestBodyFromContext introduce new context-based caching patterns; ensure proper context propagation and no stale data issues.
  • Provider-specific converters in Vertex: The dual-path logic for Claude vs. OpenAI converters in ChatCompletion requires careful validation that both branches properly handle metadata and error cases.
  • Raw response passthrough: New conditional logic in transport layer integrations (anthropic.go, genai.go, openai.go) to return RawResponse; verify ExtraFields population is correct for all providers.
  • Anthropic metadata and reasoning propagation: New Metadata and Signature fields in Anthropic types require verification that user IDs and thinking content are correctly round-tripped.
  • Error handling path fragmentation: Multiple providers updated error construction; audit that all newUnsupportedOperationError calls use correct schema constants and provider keys.
  • Auth header conditionals: 15+ provider files now conditionally set auth headers; spot-check that logic is consistent and no auth headers are erroneously dropped.

Poem

🐰 A hop through refactored flows,
Where JSON streams and context glows!
Auth headers dance when keys align,
And schemas make the errors shine—
Raw responses whisper through the gate,
While requests find their pre-serialized fate!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 70.83% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The PR title "refactor: standardize request body handling and error messages across providers" is directly and fully related to the main changes in the changeset. The raw summary demonstrates that the core refactoring involves standardizing request body handling by introducing checkContextAndGetRequestBody utility and checkAndDecodeBody for gzipped responses, as well as standardizing unsupported operation error messages across multiple providers using schema-based request types. The title is concise, uses a conventional commit format, and clearly communicates the primary purpose of the changes without being vague or generic.
Description Check ✅ Passed The PR description follows the repository template structure closely and includes all major required sections: a clear summary explaining the refactoring purpose, a detailed changes section with bullet points describing modifications, type of change selections (Bug fix, Feature, Refactor), affected areas (Core, Transports, Providers/Integrations), testing instructions with specific commands including gzipped response testing, breaking changes status, related issues statement, security considerations, and a completed checklist. The description is substantive, well-organized, and provides sufficient detail for reviewers to understand the scope and intent of the changes.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 11-02-feat_core_providers_standardization_refactors

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
core/schemas/providers/gemini/speech.go (1)

20-20: Consider whether hardcoding ModalityAudio is always correct.

The response modality is now hardcoded to ModalityAudio. If speech requests might need other modalities (e.g., text + audio), this removes that flexibility.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 376d868 and c6ffb0a.

📒 Files selected for processing (26)
  • core/providers/anthropic.go (13 hunks)
  • core/providers/azure.go (13 hunks)
  • core/providers/bedrock.go (11 hunks)
  • core/providers/cerebras.go (6 hunks)
  • core/providers/cohere.go (8 hunks)
  • core/providers/gemini.go (14 hunks)
  • core/providers/groq.go (2 hunks)
  • core/providers/mistral.go (4 hunks)
  • core/providers/ollama.go (1 hunks)
  • core/providers/openai.go (23 hunks)
  • core/providers/openrouter.go (5 hunks)
  • core/providers/parasail.go (3 hunks)
  • core/providers/sgl.go (1 hunks)
  • core/providers/utils.go (3 hunks)
  • core/providers/vertex.go (8 hunks)
  • core/schemas/providers/anthropic/responses.go (5 hunks)
  • core/schemas/providers/anthropic/types.go (3 hunks)
  • core/schemas/providers/gemini/speech.go (2 hunks)
  • core/schemas/providers/vertex/types.go (1 hunks)
  • core/schemas/responses.go (1 hunks)
  • core/utils.go (1 hunks)
  • framework/modelcatalog/main.go (1 hunks)
  • transports/bifrost-http/handlers/server.go (1 hunks)
  • transports/bifrost-http/integrations/anthropic.go (1 hunks)
  • transports/bifrost-http/integrations/genai.go (1 hunks)
  • transports/bifrost-http/integrations/openai.go (5 hunks)
🧰 Additional context used
🧬 Code graph analysis (22)
transports/bifrost-http/handlers/server.go (1)
transports/bifrost-http/lib/config.go (1)
  • Config (135-165)
core/providers/sgl.go (3)
core/schemas/bifrost.go (4)
  • SpeechRequest (90-90)
  • SpeechStreamRequest (91-91)
  • TranscriptionRequest (92-92)
  • TranscriptionStreamRequest (93-93)
core/schemas/speech.go (1)
  • BifrostSpeechRequest (9-15)
core/schemas/transcriptions.go (2)
  • BifrostTranscriptionRequest (3-9)
  • BifrostTranscriptionResponse (11-21)
core/providers/groq.go (2)
core/schemas/bifrost.go (5)
  • EmbeddingRequest (89-89)
  • SpeechRequest (90-90)
  • SpeechStreamRequest (91-91)
  • TranscriptionRequest (92-92)
  • TranscriptionStreamRequest (93-93)
core/schemas/account.go (1)
  • Key (8-17)
core/utils.go (1)
core/schemas/bifrost.go (4)
  • ModelProvider (32-32)
  • Azure (36-36)
  • Bedrock (38-38)
  • Vertex (40-40)
core/providers/cohere.go (3)
core/schemas/provider.go (1)
  • ErrProviderResponseUnmarshal (25-25)
core/schemas/bifrost.go (7)
  • TextCompletionRequest (83-83)
  • BifrostError (326-335)
  • TextCompletionStreamRequest (84-84)
  • SpeechRequest (90-90)
  • SpeechStreamRequest (91-91)
  • TranscriptionRequest (92-92)
  • TranscriptionStreamRequest (93-93)
core/schemas/account.go (1)
  • Key (8-17)
core/providers/openrouter.go (2)
core/schemas/bifrost.go (5)
  • EmbeddingRequest (89-89)
  • SpeechRequest (90-90)
  • SpeechStreamRequest (91-91)
  • TranscriptionRequest (92-92)
  • TranscriptionStreamRequest (93-93)
core/schemas/account.go (1)
  • Key (8-17)
core/schemas/providers/anthropic/responses.go (3)
core/schemas/providers/anthropic/types.go (2)
  • AnthropicMetaData (52-54)
  • AnthropicContentBlockTypeThinking (130-130)
core/schemas/responses.go (3)
  • ResponsesReasoning (704-707)
  • ResponsesReasoningContent (718-721)
  • ResponsesReasoningContentBlockTypeSummaryText (714-714)
ui/lib/types/logs.ts (2)
  • ResponsesReasoning (401-404)
  • ResponsesReasoningContent (396-399)
core/providers/vertex.go (7)
core/schemas/bifrost.go (7)
  • TextCompletionRequest (83-83)
  • Vertex (40-40)
  • TextCompletionStreamRequest (84-84)
  • SpeechRequest (90-90)
  • SpeechStreamRequest (91-91)
  • TranscriptionRequest (92-92)
  • TranscriptionStreamRequest (93-93)
core/schemas/account.go (2)
  • Key (8-17)
  • VertexKeyConfig (29-33)
core/schemas/providers/anthropic/chat.go (1)
  • ToAnthropicChatCompletionRequest (374-605)
core/schemas/providers/openai/chat.go (1)
  • ToOpenAIChatRequest (20-61)
core/schemas/providers/vertex/types.go (1)
  • DefaultVertexAnthropicVersion (8-8)
core/utils.go (1)
  • Ptr (46-48)
core/schemas/providers/vertex/embedding.go (1)
  • ToVertexEmbeddingRequest (8-67)
core/providers/anthropic.go (5)
core/schemas/bifrost.go (9)
  • BifrostError (326-335)
  • TextCompletionStreamRequest (84-84)
  • ModelProvider (32-32)
  • BifrostStream (291-298)
  • EmbeddingRequest (89-89)
  • SpeechRequest (90-90)
  • SpeechStreamRequest (91-91)
  • TranscriptionRequest (92-92)
  • TranscriptionStreamRequest (93-93)
core/schemas/provider.go (2)
  • ErrProviderResponseUnmarshal (25-25)
  • PostHookRunner (194-194)
core/schemas/providers/anthropic/text.go (1)
  • ToAnthropicTextCompletionRequest (11-46)
core/schemas/providers/anthropic/chat.go (1)
  • ToAnthropicChatCompletionRequest (374-605)
core/schemas/providers/anthropic/responses.go (1)
  • ToAnthropicResponsesRequest (117-199)
transports/bifrost-http/integrations/openai.go (1)
core/schemas/bifrost.go (1)
  • OpenAI (35-35)
core/providers/cerebras.go (3)
core/schemas/bifrost.go (7)
  • EmbeddingRequest (89-89)
  • BifrostError (326-335)
  • SpeechRequest (90-90)
  • BifrostStream (291-298)
  • SpeechStreamRequest (91-91)
  • TranscriptionRequest (92-92)
  • TranscriptionStreamRequest (93-93)
core/schemas/account.go (1)
  • Key (8-17)
core/schemas/provider.go (1)
  • PostHookRunner (194-194)
core/providers/gemini.go (6)
core/schemas/account.go (1)
  • Key (8-17)
core/schemas/providers/gemini/types.go (1)
  • GenerateContentResponse (1173-1189)
core/schemas/provider.go (4)
  • ErrProviderResponseUnmarshal (25-25)
  • Provider (197-224)
  • ErrProviderRequestTimedOut (22-22)
  • ErrProviderRequest (24-24)
core/schemas/providers/openai/chat.go (1)
  • ToOpenAIChatRequest (20-61)
core/schemas/providers/gemini/speech.go (1)
  • ToGeminiSpeechRequest (9-41)
core/schemas/providers/gemini/transcription.go (1)
  • ToGeminiTranscriptionRequest (5-73)
transports/bifrost-http/integrations/genai.go (4)
core/schemas/bifrost.go (1)
  • Gemini (47-47)
core/schemas/providers/gemini/embedding.go (1)
  • ToGeminiEmbeddingResponse (57-100)
transports/bifrost-http/integrations/router.go (1)
  • ChatResponseConverter (92-92)
core/schemas/chatcompletions.go (1)
  • BifrostChatResponse (20-30)
core/providers/azure.go (7)
core/schemas/account.go (2)
  • Key (8-17)
  • AzureKeyConfig (21-25)
core/schemas/bifrost.go (9)
  • RequestType (79-79)
  • BifrostError (326-335)
  • TextCompletionRequest (83-83)
  • ChatCompletionRequest (85-85)
  • EmbeddingRequest (89-89)
  • SpeechRequest (90-90)
  • SpeechStreamRequest (91-91)
  • TranscriptionRequest (92-92)
  • TranscriptionStreamRequest (93-93)
core/schemas/provider.go (1)
  • ErrProviderResponseUnmarshal (25-25)
core/schemas/providers/openai/text.go (1)
  • ToOpenAITextCompletionRequest (8-25)
core/schemas/providers/openai/chat.go (1)
  • ToOpenAIChatRequest (20-61)
core/schemas/providers/openai/responses.go (1)
  • ToOpenAIResponsesRequest (32-53)
core/schemas/providers/openai/embedding.go (1)
  • ToOpenAIEmbeddingRequest (22-40)
core/providers/ollama.go (3)
core/schemas/bifrost.go (4)
  • SpeechRequest (90-90)
  • SpeechStreamRequest (91-91)
  • TranscriptionRequest (92-92)
  • TranscriptionStreamRequest (93-93)
core/schemas/speech.go (1)
  • BifrostSpeechRequest (9-15)
core/schemas/transcriptions.go (1)
  • BifrostTranscriptionRequest (3-9)
core/providers/utils.go (2)
core/schemas/bifrost.go (6)
  • BifrostContextKeyRequestBody (110-110)
  • ModelProvider (32-32)
  • BifrostError (326-335)
  • RequestType (79-79)
  • ErrorField (344-351)
  • BifrostErrorExtraFields (393-397)
core/schemas/provider.go (2)
  • ErrProviderJSONMarshaling (26-26)
  • Provider (197-224)
transports/bifrost-http/integrations/anthropic.go (2)
core/schemas/provider.go (1)
  • Provider (197-224)
core/schemas/bifrost.go (1)
  • Anthropic (37-37)
core/providers/mistral.go (3)
core/schemas/bifrost.go (7)
  • TextCompletionRequest (83-83)
  • BifrostError (326-335)
  • TextCompletionStreamRequest (84-84)
  • SpeechRequest (90-90)
  • SpeechStreamRequest (91-91)
  • TranscriptionRequest (92-92)
  • TranscriptionStreamRequest (93-93)
core/schemas/provider.go (1)
  • PostHookRunner (194-194)
core/schemas/account.go (1)
  • Key (8-17)
core/providers/bedrock.go (7)
core/schemas/account.go (2)
  • Key (8-17)
  • BedrockKeyConfig (39-46)
core/schemas/bifrost.go (7)
  • BifrostError (326-335)
  • TextCompletionStreamRequest (84-84)
  • Bedrock (38-38)
  • SpeechRequest (90-90)
  • SpeechStreamRequest (91-91)
  • TranscriptionRequest (92-92)
  • TranscriptionStreamRequest (93-93)
core/schemas/providers/bedrock/types.go (1)
  • DefaultBedrockRegion (4-4)
core/schemas/providers/bedrock/text.go (1)
  • ToBedrockTextCompletionRequest (11-58)
core/schemas/providers/bedrock/chat.go (1)
  • ToBedrockChatCompletionRequest (12-42)
core/schemas/providers/bedrock/responses.go (1)
  • ToBedrockResponsesRequest (12-118)
core/schemas/providers/bedrock/embedding.go (1)
  • ToBedrockTitanEmbeddingRequest (12-41)
core/providers/parasail.go (2)
core/schemas/bifrost.go (7)
  • TextCompletionRequest (83-83)
  • TextCompletionStreamRequest (84-84)
  • EmbeddingRequest (89-89)
  • SpeechRequest (90-90)
  • SpeechStreamRequest (91-91)
  • TranscriptionRequest (92-92)
  • TranscriptionStreamRequest (93-93)
core/schemas/account.go (1)
  • Key (8-17)
core/schemas/providers/gemini/speech.go (2)
core/schemas/speech.go (1)
  • BifrostSpeechRequest (9-15)
core/schemas/providers/gemini/types.go (4)
  • GeminiGenerationRequest (51-64)
  • GenerationConfig (622-688)
  • Modality (697-697)
  • ModalityAudio (707-707)
core/providers/openai.go (8)
core/schemas/providers/openai/text.go (1)
  • ToOpenAITextCompletionRequest (8-25)
core/schemas/provider.go (2)
  • ErrProviderResponseUnmarshal (25-25)
  • ErrProviderDecodeRaw (28-28)
core/utils.go (1)
  • Ptr (46-48)
core/schemas/chatcompletions.go (1)
  • ChatStreamOptions (176-179)
core/schemas/providers/openai/chat.go (1)
  • ToOpenAIChatRequest (20-61)
core/schemas/providers/openai/responses.go (1)
  • ToOpenAIResponsesRequest (32-53)
core/schemas/providers/openai/embedding.go (1)
  • ToOpenAIEmbeddingRequest (22-40)
core/schemas/providers/openai/speech.go (1)
  • ToOpenAISpeechRequest (20-38)
⏰ 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 (9)
framework/modelcatalog/main.go (1)

221-236: LGTM: Good defensive programming.

The nil check prevents potential panic when dereferencing modelData.Data. This aligns with the PR's goal of improving error handling.

transports/bifrost-http/integrations/genai.go (2)

48-55: LGTM: Consistent raw response passthrough pattern.

The conditional RawResponse passthrough for Gemini provider aligns with the PR's standardization objectives and maintains backward compatibility.


56-63: LGTM: Mirrors embedding converter logic.

Consistent implementation of raw response passthrough for chat completions.

core/schemas/providers/vertex/types.go (1)

7-9: LGTM: Clean constant addition.

The exported constant provides a sensible default version for Vertex Anthropic API requests.

transports/bifrost-http/integrations/anthropic.go (1)

70-77: LGTM: Consistent with GenAI integration pattern.

The RawResponse passthrough for Anthropic follows the same pattern implemented for Gemini, providing consistency across providers.

core/schemas/responses.go (1)

97-97: LGTM: Clean field addition for user context.

The optional User field extends ResponsesParameters to support user identification in requests, aligning with provider-specific metadata requirements.

core/schemas/providers/gemini/speech.go (1)

9-9: No breaking changes detected.

The grep results show both callers in core/providers/gemini.go (lines 378 and 416) are already calling ToGeminiSpeechRequest(request) with a single argument, matching the current signature. The responseModalities parameter removal does not break any callers in the codebase.

core/utils.go (1)

62-64: Disregard this review comment—the function name is already clear and appropriate.

The function name isKeySkippingAllowed accurately describes its purpose in context. The logic checks whether a given provider allows skipping key selection (returning true for all providers except Azure, Bedrock, and Vertex). The usage at core/bifrost.go:2338 shows it's called with a variable named skipKeySelection, which reinforces the semantics. The boolean naming pattern is...Allowed is idiomatic Go and follows standard conventions for predicate functions.

The suggested alternatives (canSkipKeyValidation, allowsEmptyKey) are less precise—canSkipKeyValidation conflates validation with key selection, and allowsEmptyKey obscures the specific intent. Since the function has only one call site and the name aligns with its context, no refactoring is necessary.

Likely an incorrect or invalid review comment.

transports/bifrost-http/handlers/server.go (1)

578-589: Graceful degradation is intentional and properly implemented.

Verification confirms that all downstream operations handle missing model catalog data gracefully:

  • OTEL plugin checks if pricingManager != nil before cost calculation (lines 458, 503)
  • Telemetry plugin checks if p.pricingManager != nil before cost calculation (lines 317-319)
  • Logging plugin checks if p.pricingManager != nil before cost calculation (lines 460-462)
  • Governance plugin checks if p.modelCatalog != nil before cost calculation (lines 483-485)

When ListAllModels fails, AddModelDataToPool is skipped, leaving PricingManager empty but functional. Cost calculations safely degrade to 0.0, and model lookups return empty slices. Server startup succeeds without catalog data, and request handling proceeds normally.

Comment on lines +181 to 191
respBody, err := checkAndDecodeBody(resp)
if err != nil {
return nil, latency, newBifrostOperationError(schemas.ErrProviderResponseUnmarshal, err, provider.GetProviderKey())
}

// Read the response body and copy it before releasing the response
// to avoid use-after-free since resp.Body() references fasthttp's internal buffer
bodyCopy := append([]byte(nil), resp.Body()...)
// bodyCopy := append([]byte(nil), resp.Body()...)

return bodyCopy, latency, nil
return respBody, latency, nil
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Restore the defensive body copy before releasing the fasthttp response.

checkAndDecodeBody returns the slice from fasthttp.Response.Body() for non-gzipped payloads. Once the deferred fasthttp.ReleaseResponse(resp) runs, that buffer is recycled, so propagating respBody upward causes use-after-free corruption in every caller (TextCompletion, ChatCompletion, Responses, etc.). Bring back the copy that existed here previously.

 	respBody, err := checkAndDecodeBody(resp)
 	if err != nil {
 		return nil, latency, newBifrostOperationError(schemas.ErrProviderResponseUnmarshal, err, provider.GetProviderKey())
 	}
 
-	// Read the response body and copy it before releasing the response
-	// to avoid use-after-free since resp.Body() references fasthttp's internal buffer
-
-	return respBody, latency, nil
+	// Read the response body and copy it before releasing the response
+	// to avoid use-after-free since resp.Body() references fasthttp's internal buffer
+	bodyCopy := append([]byte(nil), respBody...)
+	return bodyCopy, latency, nil
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
respBody, err := checkAndDecodeBody(resp)
if err != nil {
return nil, latency, newBifrostOperationError(schemas.ErrProviderResponseUnmarshal, err, provider.GetProviderKey())
}
// Read the response body and copy it before releasing the response
// to avoid use-after-free since resp.Body() references fasthttp's internal buffer
bodyCopy := append([]byte(nil), resp.Body()...)
// bodyCopy := append([]byte(nil), resp.Body()...)
return bodyCopy, latency, nil
return respBody, latency, nil
}
respBody, err := checkAndDecodeBody(resp)
if err != nil {
return nil, latency, newBifrostOperationError(schemas.ErrProviderResponseUnmarshal, err, provider.GetProviderKey())
}
// Read the response body and copy it before releasing the response
// to avoid use-after-free since resp.Body() references fasthttp's internal buffer
bodyCopy := append([]byte(nil), respBody...)
return bodyCopy, latency, nil
}
🤖 Prompt for AI Agents
In core/providers/anthropic.go around lines 181 to 191, restore a defensive copy
of the response body returned by checkAndDecodeBody before releasing the
fasthttp response to avoid use-after-free: after getting respBody and before
calling fasthttp.ReleaseResponse(resp) (or allowing its deferred call to run),
create a copy (e.g. append([]byte(nil), respBody...)) and return that copy (and
not the slice that points into resp's internal buffer); ensure this is done for
the non-gzipped path so callers receive a safe, independently allocated byte
slice.

Comment thread core/providers/openai.go
Comment on lines +299 to +308
func() (any, error) {
reqBody := openai.ToOpenAITextCompletionRequest(request)
if reqBody != nil {
reqBody.Stream = schemas.Ptr(true)
reqBody.StreamOptions = &schemas.ChatStreamOptions{
IncludeUsage: schemas.Ptr(true),
}
}
return reqBody, nil
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Preserve existing stream options when enabling usage data

Both helpers overwrite StreamOptions with a fresh struct, so any caller-supplied flags (e.g., IncludeObfuscation) disappear the moment we enable usage streaming. That’s a regression for providers relying on additional stream metadata. Instead, reuse the existing struct (creating it only when nil) and set IncludeUsage in place.

-			if reqBody != nil {
-				reqBody.Stream = schemas.Ptr(true)
-				reqBody.StreamOptions = &schemas.ChatStreamOptions{
-					IncludeUsage: schemas.Ptr(true),
-				}
-			}
+			if reqBody != nil {
+				reqBody.Stream = schemas.Ptr(true)
+				if reqBody.StreamOptions == nil {
+					reqBody.StreamOptions = &schemas.ChatStreamOptions{}
+				}
+				reqBody.StreamOptions.IncludeUsage = schemas.Ptr(true)
+			}
@@
-			if reqBody != nil {
-				reqBody.Stream = schemas.Ptr(true)
-				reqBody.StreamOptions = &schemas.ChatStreamOptions{
-					IncludeUsage: schemas.Ptr(true),
-				}
-			}
+			if reqBody != nil {
+				reqBody.Stream = schemas.Ptr(true)
+				if reqBody.StreamOptions == nil {
+					reqBody.StreamOptions = &schemas.ChatStreamOptions{}
+				}
+				reqBody.StreamOptions.IncludeUsage = schemas.Ptr(true)
+			}

Also applies to: 651-659

🤖 Prompt for AI Agents
In core/providers/openai.go around lines 299 to 308 (and likewise at 651-659),
the current code unconditionally assigns a new StreamOptions struct which
discards any caller-supplied stream flags; instead, if reqBody.StreamOptions is
nil allocate a new schemas.ChatStreamOptions and assign it back, otherwise reuse
the existing struct and set reqBody.StreamOptions.IncludeUsage =
schemas.Ptr(true) in place (do not replace the StreamOptions pointer), and
ensure reqBody.Stream remains true — repeat the same in the other location to
preserve other flags like IncludeObfuscation.

Comment thread core/providers/vertex.go
Comment on lines +552 to +572
func() (any, error) {
reqBody := anthropic.ToAnthropicChatCompletionRequest(request)
if reqBody == nil {
return nil, fmt.Errorf("chat completion input is not provided")
}

reqBody.Stream = schemas.Ptr(true)
reqBody.Stream = schemas.Ptr(true)

// Convert struct to map for Vertex API
reqBytes, _ := sonic.Marshal(reqBody)
var requestBody map[string]interface{}
sonic.Unmarshal(reqBytes, &requestBody)
// Convert struct to map for Vertex API
reqBytes, _ := sonic.Marshal(reqBody)
var requestBody map[string]interface{}
sonic.Unmarshal(reqBytes, &requestBody)

if _, exists := requestBody["anthropic_version"]; !exists {
requestBody["anthropic_version"] = "vertex-2023-10-16"
}
if _, exists := requestBody["anthropic_version"]; !exists {
requestBody["anthropic_version"] = vertex.DefaultVertexAnthropicVersion
}

delete(requestBody, "model")
delete(requestBody, "region")
delete(requestBody, "model")
delete(requestBody, "region")
return reqBody, nil
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Restore Vertex Claude streaming payload mutations

When we return reqBody here, the anthropic_version default we just injected—and the removals of model/region—are dropped. As a result, Claude streaming calls go out without the required anthropic_version and with fields Vertex rejects, so the endpoint fails. Return the mutated requestBody map instead so those adjustments persist.

-				delete(requestBody, "model")
-				delete(requestBody, "region")
-				return reqBody, nil
+				delete(requestBody, "model")
+				delete(requestBody, "region")
+				return requestBody, nil
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func() (any, error) {
reqBody := anthropic.ToAnthropicChatCompletionRequest(request)
if reqBody == nil {
return nil, fmt.Errorf("chat completion input is not provided")
}
reqBody.Stream = schemas.Ptr(true)
reqBody.Stream = schemas.Ptr(true)
// Convert struct to map for Vertex API
reqBytes, _ := sonic.Marshal(reqBody)
var requestBody map[string]interface{}
sonic.Unmarshal(reqBytes, &requestBody)
// Convert struct to map for Vertex API
reqBytes, _ := sonic.Marshal(reqBody)
var requestBody map[string]interface{}
sonic.Unmarshal(reqBytes, &requestBody)
if _, exists := requestBody["anthropic_version"]; !exists {
requestBody["anthropic_version"] = "vertex-2023-10-16"
}
if _, exists := requestBody["anthropic_version"]; !exists {
requestBody["anthropic_version"] = vertex.DefaultVertexAnthropicVersion
}
delete(requestBody, "model")
delete(requestBody, "region")
delete(requestBody, "model")
delete(requestBody, "region")
return reqBody, nil
},
func() (any, error) {
reqBody := anthropic.ToAnthropicChatCompletionRequest(request)
if reqBody == nil {
return nil, fmt.Errorf("chat completion input is not provided")
}
reqBody.Stream = schemas.Ptr(true)
// Convert struct to map for Vertex API
reqBytes, _ := sonic.Marshal(reqBody)
var requestBody map[string]interface{}
sonic.Unmarshal(reqBytes, &requestBody)
if _, exists := requestBody["anthropic_version"]; !exists {
requestBody["anthropic_version"] = vertex.DefaultVertexAnthropicVersion
}
delete(requestBody, "model")
delete(requestBody, "region")
return requestBody, nil
},
🤖 Prompt for AI Agents
In core/providers/vertex.go around lines 552 to 572, the function builds a
requestBody map and mutates it (injects anthropic_version and deletes
model/region) but then returns the original reqBody struct so those changes are
lost; change the return value to return the mutated requestBody map (not
reqBody) so the anthropic_version default and removals persist when sending the
Vertex Claude streaming payload.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant