Skip to content

Conversation

@akshaydeo
Copy link
Contributor

@akshaydeo akshaydeo commented Sep 24, 2025

Summary

Bump transports version from 1.2.20 to 1.3.0-prerelease1

Changes

  • Responses support. Across all models + providers <3 - all . Response streaming is coming in prerelease2. @pratham_maxim_91868 and @TejasGhatte
  • Otel support (along with traces that follows GenAI extension spec)
  • You can now set-up observability from UI (otel and Maxim, more to follow soon)
  • Provider level fallbacks. You can set fallbacks for OpenAI models between OpenAI, Azure and OpenRouter in a virtual key
  • More robust logging support for streaming flows (text, chat, audtio, transcription)

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

Verify the version number is correctly updated:

cat transports/version
# Should output: 1.3.0-prerelease1

Breaking changes

  • Yes
  • No

Related issues

N/A

Security considerations

No security implications.

Checklist

  • I read docs/contributing/README.md and followed the guidelines
  • I added/updated tests where appropriate
  • I updated documentation where needed
  • I verified builds succeed (Go and UI)
  • I verified the CI pipeline passes locally if applicable

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 24, 2025

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Added Responses API with cross-provider support and conversions.
    • Dynamic plugin reloads (no restart needed).
    • Toggle OpenAI Responses vs Chat via key config.
    • Enhanced streaming and tool integration; richer media/content handling.
  • Breaking Changes
    • Unified request-based SDK across all operations; legacy operation enums removed.
    • Response schema updated (choices, usage, extra fields).
    • Some providers don’t support Responses streaming.
  • Refactor
    • Standardized provider implementations; improved error metadata and proxy naming.
  • Chores
    • Bumped version to 1.3.0-prerelease1; added enterprise cleanup target; updated ignore rules.
  • Documentation
    • Changelog updated.

Walkthrough

Refactors core and provider APIs to use typed Bifrost request/response structs, adds Responses API and many provider-specific converters, restructures pooling/plugin storage, updates error metadata, bumps version to 1.3.0-prerelease1, and adds enterprise cleanup/ignore tooling.

Changes

Cohort / File(s) Summary of Changes
Versioning
transports/version
Bumped semantic version from 1.2.221.3.0-prerelease1.
Repo tooling
Makefile, .gitignore
Added cleanup-enterprise Make target and PHONY; install-ui depends on it; added enterprise UI ignore patterns to .gitignore; minor APP_DIR whitespace change.
Core engine
core/bifrost.go, core/changelog.md
Reworked Bifrost internals: plugin storage → atomic.Pointer, introduced request/pipeline pooling and reset helpers, removed ChannelMessage.Type, updated shutdown/plugin cleanup, updated changelog entries.
MCP / tool handling
core/mcp.go
Switched MCP tool maps and APIs to chat-specific schemas.ChatTool, updated tool registration/execution signatures and conversions to ChatMessage/ChatTool.
Provider interface & utils
core/providers/*, core/providers/utils.go
Unified provider method signatures to accept *schemas.Bifrost*Request structs, generalized completeRequest bodies to interface{}, centralized OpenAI-like handlers, removed many in-file pools, and extended error ExtraFields and request-type gating.
Provider implementations
core/providers/*.go (Anthropic, Azure, Bedrock, Cohere, Gemini, OpenAI, OpenRouter, Cerebras, Groq, Mistral, Ollama, Parasail, SGL, Vertex, etc.)
Rewrote provider adapters to use typed converters and shared handlers; added Responses/ResponsesStream surfaces (many as unsupported), updated streaming/event handling, swapped provider-specific response types to centralized schemas/converters.
Schemas — core
core/schemas/bifrost.go, core/schemas/account.go, core/schemas/provider.go
Added BifrostRequest and per-operation request types (Text/Chat/Responses/Embedding/Speech/Transcription), new RequestType values (ResponsesRequest/ResponsesStreamRequest), added OpenAIKeyConfig on Key, updated Provider interface to request-object methods and removed legacy Operation type.
Schemas — chat & mux
core/schemas/chatcompletions.go, core/schemas/mux.go
Introduced rich chat completions schema (tools, content blocks, annotations) and bidirectional chat↔responses conversion helpers and request/response translation.
Schemas — provider adapters
core/schemas/providers/*/*
Added extensive provider-specific typed schemas and converters (anthropic, bedrock, cohere v2, gemini, etc.) for requests, responses, streaming, image/audio conversions, tool-call mapping, and Responses API support.
Misc docs/cleanup
core/changelog.md, Makefile
Changelog edits to reflect Responses API and breaking changes; added build cleanup target and ignore rules.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Client
  participant Core as Bifrost Core
  participant Pipeline as Plugin Pipeline
  participant Adapter as Provider Adapter
  participant Provider as External Provider

  Client->>Core: Send *BifrostRequest (Text/Chat/Responses/Embedding/Speech/Transcription)
  Core->>Pipeline: Run pre-hooks / select provider
  Pipeline->>Core: Return (possibly mutated) BifrostRequest
  Core->>Adapter: Convert BifrostRequest → provider-specific request
  Adapter->>Provider: Call provider API (typed request)
  Provider-->>Adapter: Return typed response or stream events
  Adapter-->>Core: Convert provider response → BifrostResponse / BifrostStream
  Core->>Pipeline: Run post-hooks (RunPostHooks)
  Pipeline->>Client: Return final response or stream
  Note right of Core: atomic plugin pointer, request pooling, ExtraFields enrichment
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120+ minutes

Possibly related PRs

Suggested reviewers

  • danpiths

Poem

I hop through structs and typed request trees,
Swapping loose params for neat, shaped keys.
Plugins pooled, converters stitch provider song,
Responses hum and streams roll along.
— a rabbit, nibbling bytes and hopping strong 🐇

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title Check ❓ Inconclusive The title “v1.3.0” merely states the new version number without indicating what is being updated or why, making it too generic to understand the scope or intent of this PR. Consider revising the title to briefly describe the core change, for example “Bump transports version to 1.3.0-prerelease1” to clearly convey the purpose of this update.
✅ Passed checks (2 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 90.82% which is sufficient. The required threshold is 80.00%.
Description Check ✅ Passed The pull request description largely adheres to the repository’s template by including a concise summary, detailed change list, type of change, affected areas, testing instructions, breaking change indicator, related issues, security considerations, and a checklist; the only template section absent is Screenshots/Recordings, which is not applicable for a non-UI bump.
✨ 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 v1.3.0

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

@akshaydeo akshaydeo marked this pull request as ready for review September 24, 2025 21:56
Copy link
Contributor Author

akshaydeo commented Sep 24, 2025

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

Copy link
Collaborator

@danpiths danpiths left a comment

Choose a reason for hiding this comment

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

@danpiths approved this PR from Slack with Graphite

Copy link
Contributor Author

@CodeRabbit-ai full review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 6, 2025

@akshaydeo: I'll perform a full review of this PR. Let me analyze all the changes in detail.

✅ Actions performed

Full review triggered.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 17

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (6)
core/providers/utils.go (1)

124-170: Match skipped headers using canonical casing

We canonically normalize header names via textproto.CanonicalMIMEHeaderKey, yet the skip list check still compares against the raw key. If the caller supplies "User-Agent" in skipHeaders (per the docstring) and the map contains "user-agent", the comparison misses and we’ll still inject the header. Please compare using the canonical form (same applies to the HTTP variant below).

Proposed fix:

-		if key == "Authorization" {
+		if canonicalKey == "Authorization" {
 			continue
 		}
-		if skipHeaders != nil {
-			if slices.Contains(skipHeaders, key) {
+		if skipHeaders != nil {
+			if slices.Contains(skipHeaders, canonicalKey) {
 				continue
 			}
 		}

Apply the same adjustment in setExtraHeadersHTTP.

core/schemas/provider.go (1)

88-125: Add gating for the new Responses operations

checkOperationAllowed consults AllowedRequests. With the new Responses methods, any custom provider config that already sets AllowedRequests now rejects these operations because they fall into the default branch. There is currently no way to opt in. Please extend the struct and switch so integrators can explicitly allow Responses / ResponsesStream.

 type AllowedRequests struct {
 	TextCompletion       bool `json:"text_completion"`
 	ChatCompletion       bool `json:"chat_completion"`
 	ChatCompletionStream bool `json:"chat_completion_stream"`
 	Embedding            bool `json:"embedding"`
 	Speech               bool `json:"speech"`
 	SpeechStream         bool `json:"speech_stream"`
 	Transcription        bool `json:"transcription"`
 	TranscriptionStream  bool `json:"transcription_stream"`
+	Responses            bool `json:"responses"`
+	ResponsesStream      bool `json:"responses_stream"`
 }
@@
 	switch operation {
 	case TextCompletionRequest:
 		return ar.TextCompletion
 	case ChatCompletionRequest:
 		return ar.ChatCompletion
 	case ChatCompletionStreamRequest:
 		return ar.ChatCompletionStream
 	case EmbeddingRequest:
 		return ar.Embedding
 	case SpeechRequest:
 		return ar.Speech
 	case SpeechStreamRequest:
 		return ar.SpeechStream
 	case TranscriptionRequest:
 		return ar.Transcription
 	case TranscriptionStreamRequest:
 		return ar.TranscriptionStream
+	case ResponsesRequest:
+		return ar.Responses
+	case ResponsesStreamRequest:
+		return ar.ResponsesStream
 	default:
 		return false // Default to not allowed for unknown operations
 	}
core/schemas/bifrost.go (1)

296-317: Reset sibling embedding representations when decoding

When the same BifrostEmbeddingResponse instance is reused (responses are pooled per the core/bifrost.go changes), decoding a new payload never clears the other representations. For example, if a previous request populated EmbeddingArray and the next response is a string, EmbeddingArray keeps the old data while EmbeddingStr is now set. Any call that checks len(EmbeddingArray) (or the 2‑D variant) will incorrectly think numeric embeddings are present, returning corrupt data to downstream consumers.

Please nil out the other fields in each branch so that exactly one representation stays live.

 	if err := sonic.Unmarshal(data, &stringContent); err == nil {
 		be.EmbeddingStr = &stringContent
+		be.EmbeddingArray = nil
+		be.Embedding2DArray = nil
 		return nil
 	}
@@
 	if err := sonic.Unmarshal(data, &arrayContent); err == nil {
 		be.EmbeddingArray = arrayContent
+		be.EmbeddingStr = nil
+		be.Embedding2DArray = nil
 		return nil
 	}
@@
 	if err := sonic.Unmarshal(data, &arrayContent2D); err == nil {
 		be.Embedding2DArray = arrayContent2D
+		be.EmbeddingStr = nil
+		be.EmbeddingArray = nil
 		return nil
 	}
core/providers/gemini.go (1)

140-158: Populate ExtraFields metadata for chat responses

After handleProviderResponse we only fill Provider, leaving RequestType and ModelRequested empty. Hooks, logging, and downstream post-processing rely on these fields (every other provider sets them). Please set them alongside the provider entry.

 	response.ExtraFields.Provider = providerName
+	response.ExtraFields.ModelRequested = request.Model
+	response.ExtraFields.RequestType = schemas.ChatCompletionRequest
core/bifrost.go (2)

1684-1752: Critical: pooled BifrostRequest aliases ChannelMessage memory

releaseChannelMessage puts &msg.BifrostRequest back into bifrostRequestPool. That pointer owns the storage that lives inside the pooled ChannelMessage. The moment another goroutine grabs the same ChannelMessage from channelMessagePool, it mutates that storage while the first goroutine still holds the pooled BifrostRequest, leading to data races and corrupted requests under load. We need the request pool to manage standalone objects, and only the top-level caller should return them.

Please stop returning the embedded field to the pool and instead release the request obtained from getBifrostRequest exactly once per public call. For example:

 func (bifrost *Bifrost) handleRequest(ctx context.Context, req *schemas.BifrostRequest) (*schemas.BifrostResponse, *schemas.BifrostError) {
+	defer bifrost.releaseBifrostRequest(req)
@@
 func (bifrost *Bifrost) handleStreamRequest(ctx context.Context, req *schemas.BifrostRequest) (chan *schemas.BifrostStream, *schemas.BifrostError) {
+	defer bifrost.releaseBifrostRequest(req)
@@
 func (bifrost *Bifrost) releaseChannelMessage(msg *ChannelMessage) {
@@
-	bifrost.releaseBifrostRequest(&msg.BifrostRequest)
+	resetBifrostRequest(&msg.BifrostRequest)

This keeps the pools independent and eliminates the aliasing race.


1246-1263: Critical: RunPostHooks invoked with wrong plugin count

RunPostHooks needs the number of PreHooks that actually ran (preCount) so it only executes PostHooks for that executed prefix. Passing len(*bifrost.plugins.Load()) replays PostHooks for plugins whose PreHook never ran (e.g., after a short-circuit or when plugins were added/removed), breaking plugin invariants and letting PostHooks operate on uninitialized state.

Use the preCount returned by RunPreHooks instead:

-	pluginCount := len(*bifrost.plugins.Load())
 	select {
 	case result = <-msg.Response:
@@
-		resp, bifrostErr := pipeline.RunPostHooks(&ctx, result, nil, pluginCount)
+		resp, bifrostErr := pipeline.RunPostHooks(&ctx, result, nil, preCount)
@@
-		resp, bifrostErrPtr = pipeline.RunPostHooks(&ctx, nil, bifrostErrPtr, pluginCount)
+		resp, bifrostErrPtr = pipeline.RunPostHooks(&ctx, nil, bifrostErrPtr, preCount)
@@
-		recoveredResp, recoveredErr := pipeline.RunPostHooks(&ctx, nil, &bifrostErrVal, len(*bifrost.plugins.Load()))
+		recoveredResp, recoveredErr := pipeline.RunPostHooks(&ctx, nil, &bifrostErrVal, preCount)

This keeps post-hook execution aligned with what actually ran during the request.

Also applies to: 1373-1377

🧹 Nitpick comments (1)
Makefile (1)

37-42: Handle all enterprise directories in cleanup.

The target only removes ui/app/enterprise, leaving ui/components/enterprise (which we now ignore in git) behind. Please delete both so the cleanup fully resets the enterprise UI footprint.

Apply this diff to cover both directories:

 cleanup-enterprise: ## Clean up enterprise directories if present
 	@echo "$(GREEN)Cleaning up enterprise...$(NC)"
 	@if [ -d "ui/app/enterprise" ]; then rm -rf ui/app/enterprise; fi
+	@if [ -d "ui/components/enterprise" ]; then rm -rf ui/components/enterprise; fi
 	@echo "$(GREEN)Enterprise cleaned up$(NC)"
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 423d0ba and 4dfdd55.

⛔ Files ignored due to path filters (8)
  • framework/go.sum is excluded by !**/*.sum
  • plugins/governance/go.sum is excluded by !**/*.sum
  • plugins/logging/go.sum is excluded by !**/*.sum
  • plugins/maxim/go.sum is excluded by !**/*.sum
  • plugins/otel/go.sum is excluded by !**/*.sum
  • plugins/semanticcache/go.sum is excluded by !**/*.sum
  • plugins/telemetry/go.sum is excluded by !**/*.sum
  • transports/go.sum is excluded by !**/*.sum
📒 Files selected for processing (47)
  • .gitignore (1 hunks)
  • Makefile (3 hunks)
  • core/bifrost.go (43 hunks)
  • core/changelog.md (1 hunks)
  • core/mcp.go (16 hunks)
  • core/providers/anthropic.go (25 hunks)
  • core/providers/azure.go (10 hunks)
  • core/providers/bedrock.go (16 hunks)
  • core/providers/cerebras.go (1 hunks)
  • core/providers/cohere.go (8 hunks)
  • core/providers/gemini.go (18 hunks)
  • core/providers/groq.go (1 hunks)
  • core/providers/mistral.go (1 hunks)
  • core/providers/ollama.go (1 hunks)
  • core/providers/openai.go (20 hunks)
  • core/providers/openrouter.go (1 hunks)
  • core/providers/parasail.go (1 hunks)
  • core/providers/sgl.go (1 hunks)
  • core/providers/utils.go (13 hunks)
  • core/providers/vertex.go (14 hunks)
  • core/schemas/account.go (1 hunks)
  • core/schemas/bifrost.go (12 hunks)
  • core/schemas/chatcompletions.go (1 hunks)
  • core/schemas/embedding.go (1 hunks)
  • core/schemas/mux.go (1 hunks)
  • core/schemas/provider.go (4 hunks)
  • core/schemas/providers/anthropic/chat.go (1 hunks)
  • core/schemas/providers/anthropic/responses.go (1 hunks)
  • core/schemas/providers/anthropic/text.go (1 hunks)
  • core/schemas/providers/anthropic/types.go (1 hunks)
  • core/schemas/providers/anthropic/utils.go (1 hunks)
  • core/schemas/providers/bedrock/chat.go (1 hunks)
  • core/schemas/providers/bedrock/embedding.go (1 hunks)
  • core/schemas/providers/bedrock/responses.go (1 hunks)
  • core/schemas/providers/bedrock/text.go (1 hunks)
  • core/schemas/providers/bedrock/types.go (1 hunks)
  • core/schemas/providers/bedrock/utils.go (1 hunks)
  • core/schemas/providers/cohere/chat.go (1 hunks)
  • core/schemas/providers/cohere/embedding.go (1 hunks)
  • core/schemas/providers/cohere/responses.go (1 hunks)
  • core/schemas/providers/cohere/types.go (1 hunks)
  • core/schemas/providers/gemini/chat.go (1 hunks)
  • core/schemas/providers/gemini/embedding.go (1 hunks)
  • core/schemas/providers/gemini/errors.go (1 hunks)
  • core/schemas/providers/gemini/responses.go (1 hunks)
  • core/schemas/providers/gemini/speech.go (1 hunks)
  • core/schemas/providers/gemini/transcription.go (1 hunks)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
core/bifrost.go (1)

1197-1268: Run PostHooks for exactly the plugins that ran.

RunPreHooks tells us how many plugins actually executed (preCount), but we later call RunPostHooks with len(*bifrost.plugins.Load()). If the plugin list changes mid-flight, we’ll skip PostHooks for plugins that already ran (or try to run hooks that never ran PreHooks), breaking plugin invariants. Please pass preCount through to every RunPostHooks call in both tryRequest and tryStreamRequest.

-	pluginCount := len(*bifrost.plugins.Load())
 	select {
 	case result = <-msg.Response:
@@
-		resp, bifrostErr := pipeline.RunPostHooks(&ctx, result, nil, pluginCount)
+		resp, bifrostErr := pipeline.RunPostHooks(&ctx, result, nil, preCount)
@@
-		resp, bifrostErrPtr = pipeline.RunPostHooks(&ctx, nil, bifrostErrPtr, pluginCount)
+		resp, bifrostErrPtr = pipeline.RunPostHooks(&ctx, nil, bifrostErrPtr, preCount)

And similarly inside tryStreamRequest:

-		recoveredResp, recoveredErr := pipeline.RunPostHooks(&ctx, nil, &bifrostErrVal, len(*bifrost.plugins.Load()))
+		recoveredResp, recoveredErr := pipeline.RunPostHooks(&ctx, nil, &bifrostErrVal, preCount)

Also applies to: 1287-1382

core/providers/utils.go (2)

128-142: Honor header skips using canonical casing

We canonicalize keys but still compare the raw map key. A caller providing "authorization" (lowercase) or canonicalized skip headers never matches, so the supposed guard against overriding Authorization (and the skip list) can be bypassed. Please compare against the canonical form instead.

-		// Skip Authorization header for security reasons
-		if key == "Authorization" {
+		// Skip Authorization header for security reasons
+		if canonicalKey == "Authorization" {
 			continue
 		}
-		if skipHeaders != nil {
-			if slices.Contains(skipHeaders, key) {
-				continue
-			}
-		}
+		if skipHeaders != nil {
+			for _, skip := range skipHeaders {
+				if textproto.CanonicalMIMEHeaderKey(skip) == canonicalKey {
+					continue
+				}
+			}
+		}

156-171: Also fix the HTTP helper’s skip logic

Same bug here: we canonicalize but still compare the original key, so lowercase "authorization" or canonical skip headers slip through. Please match on the canonical form.

-		// Skip Authorization header for security reasons
-		if key == "Authorization" {
+		// Skip Authorization header for security reasons
+		if canonicalKey == "Authorization" {
 			continue
 		}
-		if skipHeaders != nil {
-			if slices.Contains(skipHeaders, key) {
-				continue
-			}
-		}
+		if skipHeaders != nil {
+			for _, skip := range skipHeaders {
+				if textproto.CanonicalMIMEHeaderKey(skip) == canonicalKey {
+					continue
+				}
+			}
+		}
core/providers/anthropic.go (1)

588-617: Copy streaming deltas before taking addresses.

event is reused for every SSE payload, so storing &event.Delta.Text / &event.Delta.Thinking causes previously emitted chunks to mutate when the next payload arrives. Clients then see incorrect content/finish data.

Apply this diff so each chunk holds its own copy:

@@
-        if event.Delta.Text != "" {
+        if event.Delta.Text != "" {
+          text := event.Delta.Text
           streamResponse := &schemas.BifrostResponse{
@@
-                Delta: schemas.BifrostStreamDelta{
-                  Content: &event.Delta.Text,
+                Delta: schemas.BifrostStreamDelta{
+                  Content: &text,
@@
-        if event.Delta.Thinking != "" {
+        if event.Delta.Thinking != "" {
+          thought := event.Delta.Thinking
           streamResponse := &schemas.BifrostResponse{
@@
-                Delta: schemas.BifrostStreamDelta{
-                  Thought: &event.Delta.Thinking,
+                Delta: schemas.BifrostStreamDelta{
+                  Thought: &thought,

Also applies to: 654-683

core/providers/gemini.go (1)

140-158: Set chat completion metadata before returning.

This path never stamps RequestType or ModelRequested, so downstream telemetry and cache keys see empty metadata for Gemini chats.

Add the missing assignments right after we set the provider:

   response.ExtraFields.Provider = providerName
+  response.ExtraFields.RequestType = schemas.ChatCompletionRequest
+  response.ExtraFields.ModelRequested = request.Model
+  response.Model = request.Model
♻️ Duplicate comments (5)
core/schemas/providers/bedrock/text.go (1)

12-58: Prevent nil dereference when the caller forgets Input.

The initial guard touches bifrostReq.Input before checking whether it’s non-nil, so requests with Input == nil panic. Update the guard to short-circuit when bifrostReq or bifrostReq.Input is nil before inspecting nested fields.

-func ToBedrockTextCompletionRequest(bifrostReq *schemas.BifrostTextCompletionRequest) *BedrockTextCompletionRequest {
-	if bifrostReq == nil || (bifrostReq.Input.PromptStr == nil && len(bifrostReq.Input.PromptArray) == 0) {
+func ToBedrockTextCompletionRequest(bifrostReq *schemas.BifrostTextCompletionRequest) *BedrockTextCompletionRequest {
+	if bifrostReq == nil || bifrostReq.Input == nil || (bifrostReq.Input.PromptStr == nil && len(bifrostReq.Input.PromptArray) == 0) {
 		return nil
 	}
core/schemas/providers/bedrock/chat.go (1)

53-91: Guard against nil bedrockResp.Output before dereferencing.

Line 53 accesses bedrockResp.Output.Message without verifying that bedrockResp.Output is non-nil. When Bedrock omits Output in error or streaming cases, this will panic.

Apply this diff to add a nil guard:

-	if bedrockResp.Output.Message != nil {
-		for _, contentBlock := range bedrockResp.Output.Message.Content {
+	if bedrockResp.Output != nil && bedrockResp.Output.Message != nil {
+		for _, contentBlock := range bedrockResp.Output.Message.Content {
core/schemas/providers/cohere/chat.go (1)

152-174: Preserve Cohere's "auto" tool-choice semantics.

Lines 161-163 map the default case (which includes ChatToolChoiceTypeAuto) to ToolChoiceRequired, forcing the model to pick a tool even when callers requested "auto" behavior. Similarly, lines 170-172 map non-function types to ToolChoiceAuto in the second switch, but the first switch already coerced auto to required.

Apply this diff to preserve auto semantics:

 if toolChoice.ChatToolChoiceStr != nil {
     switch schemas.ChatToolChoiceType(*toolChoice.ChatToolChoiceStr) {
     case schemas.ChatToolChoiceTypeNone:
         toolChoice := ToolChoiceNone
         cohereReq.ToolChoice = &toolChoice
+    case schemas.ChatToolChoiceTypeAuto:
+        toolChoice := ToolChoiceAuto
+        cohereReq.ToolChoice = &toolChoice
+    case schemas.ChatToolChoiceTypeRequired:
+        toolChoice := ToolChoiceRequired
+        cohereReq.ToolChoice = &toolChoice
     default:
-        toolChoice := ToolChoiceRequired
+        toolChoice := ToolChoiceAuto
         cohereReq.ToolChoice = &toolChoice
     }
 } else if toolChoice.ChatToolChoiceStruct != nil {
     switch toolChoice.ChatToolChoiceStruct.Type {
     case schemas.ChatToolChoiceTypeFunction:
         toolChoice := ToolChoiceRequired
         cohereReq.ToolChoice = &toolChoice
     default:
         toolChoice := ToolChoiceAuto
         cohereReq.ToolChoice = &toolChoice					
     }
 }
core/providers/vertex.go (1)

544-556: Keep Vertex OAuth header intact.

We still pass Authorization: Bearer even when key.Value is empty, clobbering the OAuth token that the pooled client injects. Please reuse the guarded header map you just built.

Switch the call to:

-    return handleOpenAIStreaming(
+    return handleOpenAIStreaming(
       ctx,
       client,
       url,
       request,
-      map[string]string{"Authorization": "Bearer " + key.Value},
+      authHeader,
       provider.networkConfig.ExtraHeaders,
       schemas.Vertex,
       postHookRunner,
       provider.logger,
     )
core/schemas/providers/anthropic/responses.go (1)

784-801: String-form tool_choice now preserved — good fix

The function correctly translates string-form tool choices (auto/any/none/required) instead of dropping them. This resolves the earlier regression.

🧹 Nitpick comments (5)
core/schemas/providers/bedrock/utils.go (3)

355-374: Return nil inference config when no fields set

convertInferenceConfig always returns a non-nil empty struct, causing empty inferenceConfig in requests.

Apply this diff:

 func convertInferenceConfig(params *schemas.ChatParameters) *BedrockInferenceConfig {
-  var config BedrockInferenceConfig
+  var (
+    config  BedrockInferenceConfig
+    hasAny  bool
+  )
   if params.MaxCompletionTokens != nil {
     config.MaxTokens = params.MaxCompletionTokens
+    hasAny = true
   }
   if params.Temperature != nil {
     config.Temperature = params.Temperature
+    hasAny = true
   }
   if params.TopP != nil {
     config.TopP = params.TopP
+    hasAny = true
   }
   if params.Stop != nil {
     config.StopSequences = params.Stop
+    hasAny = true
   }
-  return &config
+  if !hasAny {
+    return nil
+  }
+  return &config
 }

123-141: Handle developer role gracefully

Bedrock doesn’t have a “developer” role; erroring breaks requests containing such messages. Map it to system or user.

Apply this diff to treat developer as system:

   for _, msg := range bifrostMessages {
     switch msg.Role {
     case schemas.ChatMessageRoleSystem:
@@
-    case schemas.ChatMessageRoleUser, schemas.ChatMessageRoleAssistant:
+    case schemas.ChatMessageRoleUser, schemas.ChatMessageRoleAssistant:
@@
-    case schemas.ChatMessageRoleTool:
+    case schemas.ChatMessageRoleTool:
@@
-    default:
-      return nil, nil, fmt.Errorf("unsupported message role: %s", msg.Role)
+    case schemas.ChatMessageRoleDeveloper:
+      systemMsg, err := convertSystemMessage(msg)
+      if err != nil {
+        return nil, nil, fmt.Errorf("failed to convert developer message: %w", err)
+      }
+      systemMessages = append(systemMessages, systemMsg)
+    default:
+      return nil, nil, fmt.Errorf("unsupported message role: %s", msg.Role)

511-518: Ineffective tool content detection on Chat content blocks

Checking ChatMessage.Content.ContentBlocks for "tool_use"/"tool_result" never matches (those types aren’t part of ChatContentBlockType). Tool presence is already detected via ToolCalls and ChatToolMessage.

Consider removing this block to avoid dead code:

-  // Check content blocks
-  if msg.Content.ContentBlocks != nil {
-    for _, block := range msg.Content.ContentBlocks {
-      if block.Type == "tool_use" || block.Type == "tool_result" {
-        hasContent = true
-      }
-    }
-  }
core/schemas/chatcompletions.go (2)

126-147: Docstring references wrong type; clarify and handle null

Comments mention ChatMessageContent; this is ChatToolChoice. Also Unmarshal should accept null.

Apply this diff:

-// MarshalJSON implements custom JSON marshalling for ChatMessageContent.
-// It marshals either ContentStr or ContentBlocks directly without wrapping.
+// MarshalJSON implements custom JSON marshalling for ChatToolChoice.
+// It marshals either the string or struct representation directly.
@@
-// UnmarshalJSON implements custom JSON unmarshalling for ChatMessageContent.
-// It determines whether "content" is a string or array and assigns to the appropriate field.
-// It also handles direct string/array content without a wrapper object.
+// UnmarshalJSON implements custom JSON unmarshalling for ChatToolChoice.
+// It accepts either a string or an object; null is treated as unset.
 func (ctc *ChatToolChoice) UnmarshalJSON(data []byte) error {
+  trimmed := bytes.TrimSpace(data)
+  if len(trimmed) == 0 || bytes.Equal(trimmed, []byte("null")) {
+    ctc.ChatToolChoiceStr = nil
+    ctc.ChatToolChoiceStruct = nil
+    return nil
+  }

347-349: Minor typo in comment

“retured” → “returned”.

Apply this diff:

-  Arguments string  `json:"arguments"` // stringified json as retured by OpenAI, might not be a valid JSON always
+  Arguments string  `json:"arguments"` // stringified JSON as returned by OpenAI; might not always be valid JSON
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4dfdd55 and a7f5bf5.

📒 Files selected for processing (28)
  • core/bifrost.go (38 hunks)
  • core/changelog.md (1 hunks)
  • core/providers/anthropic.go (26 hunks)
  • core/providers/bedrock.go (19 hunks)
  • core/providers/cohere.go (9 hunks)
  • core/providers/gemini.go (18 hunks)
  • core/providers/openai.go (23 hunks)
  • core/providers/utils.go (20 hunks)
  • core/providers/vertex.go (14 hunks)
  • core/schemas/bifrost.go (12 hunks)
  • core/schemas/chatcompletions.go (1 hunks)
  • core/schemas/embedding.go (1 hunks)
  • core/schemas/mux.go (1 hunks)
  • core/schemas/providers/anthropic/chat.go (1 hunks)
  • core/schemas/providers/anthropic/responses.go (1 hunks)
  • core/schemas/providers/anthropic/text.go (1 hunks)
  • core/schemas/providers/anthropic/types.go (1 hunks)
  • core/schemas/providers/bedrock/chat.go (1 hunks)
  • core/schemas/providers/bedrock/responses.go (1 hunks)
  • core/schemas/providers/bedrock/text.go (1 hunks)
  • core/schemas/providers/bedrock/types.go (1 hunks)
  • core/schemas/providers/bedrock/utils.go (1 hunks)
  • core/schemas/providers/cohere/chat.go (1 hunks)
  • core/schemas/providers/cohere/embedding.go (1 hunks)
  • core/schemas/providers/cohere/responses.go (1 hunks)
  • core/schemas/providers/cohere/types.go (1 hunks)
  • core/schemas/providers/gemini/embedding.go (1 hunks)
  • core/schemas/providers/gemini/errors.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • core/schemas/providers/anthropic/text.go
  • core/schemas/providers/bedrock/responses.go
  • core/schemas/providers/gemini/embedding.go
🧰 Additional context used
🧬 Code graph analysis (22)
core/schemas/providers/cohere/responses.go (4)
core/schemas/bifrost.go (3)
  • BifrostResponsesRequest (136-142)
  • BifrostResponse (177-192)
  • LLMUsage (195-201)
core/schemas/providers/cohere/types.go (15)
  • CohereChatRequest (11-28)
  • CohereChatRequestTool (30-33)
  • CohereChatRequestFunction (35-39)
  • CohereChatResponse (277-283)
  • CohereToolChoice (176-176)
  • CohereMessage (42-48)
  • NewStringContent (90-94)
  • NewBlocksContent (97-101)
  • CohereToolCall (185-189)
  • CohereFunction (192-195)
  • CohereContentBlock (134-148)
  • CohereContentBlockTypeText (126-126)
  • CohereContentBlockTypeImage (127-127)
  • CohereImageURL (151-153)
  • CohereContentBlockTypeThinking (128-128)
core/schemas/utils.go (4)
  • SafeExtractIntPointer (481-489)
  • SafeExtractStringSlice (432-460)
  • SafeExtractFloat64Pointer (492-500)
  • Ptr (14-16)
core/schemas/responses.go (15)
  • ResponsesToolFunction (1231-1234)
  • ResponsesResponse (85-98)
  • ResponsesExtendedResponseUsage (125-130)
  • ResponsesToolChoice (1158-1161)
  • ResponsesMessage (176-188)
  • ResponsesMessageTypeMessage (152-152)
  • ResponsesToolMessage (588-611)
  • ResponsesFunctionToolCallOutput (860-863)
  • ResponsesMessageContent (469-472)
  • ResponsesMessageContentBlock (527-538)
  • ResponsesInputMessageContentBlockTypeText (516-516)
  • ResponsesInputMessageContentBlockTypeImage (517-517)
  • ResponsesInputMessageContentBlockImage (540-543)
  • ResponsesOutputMessageContentTypeReasoning (522-522)
  • ResponsesMessageTypeFunctionCall (157-157)
core/schemas/providers/cohere/chat.go (4)
core/schemas/bifrost.go (10)
  • BifrostChatRequest (128-134)
  • LogProbs (263-268)
  • BifrostResponse (177-192)
  • BifrostChatResponseChoice (324-333)
  • BifrostNonStreamResponseChoice (340-343)
  • BifrostResponseExtraFields (398-407)
  • RequestType (89-89)
  • Cohere (49-49)
  • LLMUsage (195-201)
  • BilledLLMUsage (232-237)
core/schemas/providers/cohere/types.go (16)
  • CohereChatRequest (11-28)
  • CohereMessage (42-48)
  • NewStringContent (90-94)
  • CohereContentBlock (134-148)
  • CohereImageURL (151-153)
  • NewBlocksContent (97-101)
  • CohereToolCall (185-189)
  • CohereFunction (192-195)
  • CohereThinking (162-165)
  • CohereThinkingType (168-168)
  • CohereChatRequestTool (30-33)
  • CohereChatRequestFunction (35-39)
  • ToolChoiceNone (180-180)
  • ToolChoiceRequired (179-179)
  • ToolChoiceAuto (181-181)
  • CohereChatResponse (277-283)
core/schemas/chatcompletions.go (8)
  • ChatAssistantMessage (316-320)
  • ChatToolMessage (311-313)
  • ChatMessage (202-211)
  • ChatMessageContent (214-217)
  • ChatContentBlock (280-287)
  • ChatInputImage (290-293)
  • ChatAssistantMessageToolCall (339-343)
  • ChatAssistantMessageToolCallFunction (346-349)
core/schemas/utils.go (6)
  • Ptr (14-16)
  • SafeExtractFromMap (514-520)
  • SafeExtractString (275-292)
  • SafeExtractIntPointer (481-489)
  • SafeExtractStringPointer (463-478)
  • SafeExtractBoolPointer (503-511)
core/schemas/providers/anthropic/chat.go (5)
core/schemas/utils.go (5)
  • Ptr (14-16)
  • ParseModelString (21-37)
  • JsonifyInput (261-270)
  • SafeExtractIntPointer (481-489)
  • MapFinishReasonToProvider (40-48)
core/schemas/chatcompletions.go (27)
  • ChatToolChoiceTypeFunction (106-106)
  • ChatMessage (202-211)
  • ChatMessageRoleSystem (196-196)
  • ChatMessageContent (214-217)
  • ChatContentBlock (280-287)
  • ChatContentBlockTypeText (272-272)
  • ChatMessageRole (190-190)
  • ChatAssistantMessageToolCall (339-343)
  • ChatAssistantMessageToolCallFunction (346-349)
  • ChatToolMessage (311-313)
  • ChatMessageRoleTool (197-197)
  • ChatAssistantMessage (316-320)
  • ChatParameters (11-40)
  • ChatTool (58-62)
  • ToolFunctionParameters (73-79)
  • ChatToolTypeFunction (53-53)
  • ChatToolFunction (65-70)
  • ChatToolChoice (121-124)
  • ChatToolChoiceStruct (114-119)
  • ChatToolChoiceType (98-98)
  • ChatToolChoiceFunction (168-170)
  • ChatMessageRoleAssistant (194-194)
  • ChatToolChoiceTypeAny (103-103)
  • ChatToolChoiceTypeRequired (104-104)
  • ChatToolChoiceTypeNone (102-102)
  • ChatToolChoiceTypeAllowedTools (108-108)
  • ChatToolChoiceTypeCustom (110-110)
core/schemas/providers/anthropic/types.go (20)
  • AnthropicMessageRequest (29-41)
  • AnthropicContentBlockTypeText (108-108)
  • AnthropicContentBlockTypeImage (109-109)
  • AnthropicContentBlockTypeToolUse (110-110)
  • AnthropicContentBlockTypeToolResult (111-111)
  • AnthropicMessageRoleAssistant (52-52)
  • AnthropicMessageResponse (181-190)
  • AnthropicDefaultMaxTokens (12-12)
  • AnthropicTool (155-160)
  • AnthropicToolChoice (163-167)
  • AnthropicMessage (56-59)
  • AnthropicContent (62-65)
  • AnthropicContentBlock (116-126)
  • AnthropicMessageRole (48-48)
  • AnthropicUsage (205-210)
  • AnthropicStreamResponse (215-224)
  • AnthropicStreamDelta (250-257)
  • AnthropicStreamMessage (238-247)
  • AnthropicMessageError (273-276)
  • AnthropicMessageErrorStruct (279-282)
core/schemas/bifrost.go (11)
  • BifrostChatRequest (128-134)
  • Anthropic (47-47)
  • BifrostResponse (177-192)
  • BifrostResponseExtraFields (398-407)
  • RequestType (89-89)
  • ChatCompletionRequest (93-93)
  • BifrostChatResponseChoice (324-333)
  • BifrostNonStreamResponseChoice (340-343)
  • LLMUsage (195-201)
  • BifrostStreamResponseChoice (346-348)
  • BifrostError (443-452)
core/schemas/providers/anthropic/utils.go (2)
  • MapAnthropicFinishReasonToBifrost (31-36)
  • ConvertToAnthropicImageBlock (48-100)
core/schemas/providers/bedrock/utils.go (4)
core/schemas/bifrost.go (1)
  • BifrostChatRequest (128-134)
core/schemas/providers/bedrock/types.go (20)
  • BedrockConverseRequest (26-38)
  • BedrockGuardrailConfig (191-195)
  • BedrockPerformanceConfig (198-200)
  • BedrockPromptVariable (203-205)
  • BedrockToolConfig (149-152)
  • BedrockMessage (48-51)
  • BedrockSystemMessage (54-57)
  • BedrockMessageRole (40-40)
  • BedrockContentBlock (60-81)
  • BedrockToolResult (114-118)
  • BedrockImageSource (84-87)
  • BedrockImageSourceData (90-92)
  • BedrockInferenceConfig (141-146)
  • BedrockTool (155-157)
  • BedrockToolSpec (160-164)
  • BedrockToolInputSchema (167-169)
  • BedrockToolChoice (172-177)
  • BedrockToolChoiceAuto (180-180)
  • BedrockToolChoiceAny (183-183)
  • BedrockToolUse (107-111)
core/schemas/chatcompletions.go (11)
  • ChatMessage (202-211)
  • ChatContentBlockTypeText (272-272)
  • ChatAssistantMessage (316-320)
  • ChatToolMessage (311-313)
  • ChatContentBlockTypeImage (273-273)
  • ChatMessageContent (214-217)
  • ChatContentBlock (280-287)
  • ChatContentBlockTypeInputAudio (274-274)
  • ChatParameters (11-40)
  • ChatToolChoice (121-124)
  • ChatAssistantMessageToolCall (339-343)
core/schemas/utils.go (3)
  • Ptr (14-16)
  • SanitizeImageURL (105-151)
  • ExtractURLTypeInfo (156-161)
core/schemas/providers/bedrock/text.go (4)
core/schemas/bifrost.go (8)
  • BifrostTextCompletionRequest (120-126)
  • BifrostResponse (177-192)
  • BifrostChatResponseChoice (324-333)
  • BifrostTextCompletionResponseChoice (335-337)
  • BifrostResponseExtraFields (398-407)
  • RequestType (89-89)
  • TextCompletionRequest (92-92)
  • Bedrock (48-48)
core/schemas/providers/bedrock/types.go (3)
  • BedrockTextCompletionRequest (7-23)
  • BedrockAnthropicTextResponse (211-215)
  • BedrockMistralTextResponse (219-224)
core/schemas/utils.go (1)
  • SafeExtractIntPointer (481-489)
core/schemas/providers/anthropic/text.go (1)
  • ToAnthropicTextCompletionRequest (11-46)
core/schemas/providers/anthropic/responses.go (5)
core/schemas/providers/anthropic/types.go (18)
  • AnthropicMessageRequest (29-41)
  • AnthropicDefaultMaxTokens (12-12)
  • AnthropicTool (155-160)
  • AnthropicMessageResponse (181-190)
  • AnthropicUsage (205-210)
  • AnthropicContentBlock (116-126)
  • AnthropicContentBlockTypeToolUse (110-110)
  • AnthropicMessage (56-59)
  • AnthropicContentBlockTypeText (108-108)
  • AnthropicContentBlockTypeImage (109-109)
  • AnthropicContentBlockTypeToolResult (111-111)
  • AnthropicToolChoice (163-167)
  • AnthropicContent (62-65)
  • AnthropicMessageRoleUser (51-51)
  • AnthropicMessageRoleAssistant (52-52)
  • AnthropicContentBlockTypeThinking (112-112)
  • AnthropicToolTypeCustom (146-146)
  • AnthropicImageSource (129-134)
core/schemas/bifrost.go (4)
  • BifrostResponsesRequest (136-142)
  • Anthropic (47-47)
  • BifrostResponse (177-192)
  • LLMUsage (195-201)
core/schemas/utils.go (5)
  • ParseModelString (21-37)
  • Ptr (14-16)
  • SafeExtractIntPointer (481-489)
  • SafeExtractStringSlice (432-460)
  • JsonifyInput (261-270)
core/schemas/responses.go (14)
  • ResponsesParameters (33-60)
  • ResponsesMessage (176-188)
  • ResponsesMessageContent (469-472)
  • ResponsesTool (1214-1229)
  • ResponsesResponse (85-98)
  • ResponsesExtendedResponseUsage (125-130)
  • ResponsesResponseInputTokens (137-139)
  • ResponsesMessageRoleType (459-459)
  • ResponsesMessageContentBlock (527-538)
  • ResponsesToolMessage (588-611)
  • ResponsesFunctionToolCallOutput (860-863)
  • ResponsesToolChoice (1158-1161)
  • ResponsesToolChoiceStruct (1150-1156)
  • ResponsesReasoning (928-931)
core/schemas/providers/anthropic/utils.go (1)
  • ConvertToAnthropicImageBlock (48-100)
core/schemas/mux.go (5)
core/schemas/chatcompletions.go (43)
  • ChatTool (58-62)
  • ChatToolTypeFunction (53-53)
  • ChatToolTypeCustom (54-54)
  • ChatToolType (49-49)
  • ChatToolFunction (65-70)
  • ChatToolCustom (81-83)
  • ChatToolCustomFormat (85-88)
  • ChatToolCustomGrammarFormat (91-94)
  • ChatToolChoice (121-124)
  • ChatToolChoiceStruct (114-119)
  • ChatToolChoiceTypeNone (102-102)
  • ChatToolChoiceTypeAny (103-103)
  • ChatToolChoiceTypeRequired (104-104)
  • ChatToolChoiceTypeFunction (106-106)
  • ChatToolChoiceTypeAllowedTools (108-108)
  • ChatToolChoiceTypeCustom (110-110)
  • ChatToolChoiceType (98-98)
  • ChatToolChoiceFunction (168-170)
  • ChatToolChoiceCustom (173-175)
  • ChatToolChoiceAllowedToolsTool (184-187)
  • ChatToolChoiceAllowedTools (178-181)
  • ChatMessage (202-211)
  • ChatAssistantMessage (316-320)
  • ChatMessageRoleAssistant (194-194)
  • ChatMessageRoleUser (195-195)
  • ChatMessageRoleSystem (196-196)
  • ChatMessageRoleTool (197-197)
  • ChatMessageRoleDeveloper (198-198)
  • ChatToolMessage (311-313)
  • ChatAssistantMessageToolCall (339-343)
  • ChatAssistantMessageToolCallFunction (346-349)
  • ChatMessageContent (214-217)
  • ChatContentBlock (280-287)
  • ChatContentBlockType (268-268)
  • ChatContentBlockTypeText (272-272)
  • ChatContentBlockTypeImage (273-273)
  • ChatContentBlockTypeFile (275-275)
  • ChatContentBlockTypeInputAudio (274-274)
  • ChatInputImage (290-293)
  • ChatInputFile (304-308)
  • ChatInputAudio (298-301)
  • ChatParameters (11-40)
  • ChatStreamOptions (43-46)
core/schemas/responses.go (40)
  • ResponsesTool (1214-1229)
  • ResponsesToolFunction (1231-1234)
  • ResponsesToolCustom (1471-1473)
  • ResponsesToolCustomFormat (1476-1482)
  • ResponsesToolChoice (1158-1161)
  • ResponsesToolChoiceStruct (1150-1156)
  • ResponsesToolChoiceType (1118-1118)
  • ResponsesToolChoiceAllowedToolDef (1203-1207)
  • ResponsesToolChoiceTypeFunction (1130-1130)
  • ResponsesToolChoiceTypeCustom (1146-1146)
  • ResponsesMessage (176-188)
  • ResponsesMessageTypeFunctionCall (157-157)
  • ResponsesInputMessageRoleAssistant (462-462)
  • ResponsesToolMessage (588-611)
  • ResponsesMessageTypeMessage (152-152)
  • ResponsesInputMessageRoleUser (463-463)
  • ResponsesMessageTypeRefusal (171-171)
  • ResponsesInputMessageRoleSystem (464-464)
  • ResponsesMessageTypeFunctionCallOutput (158-158)
  • ResponsesInputMessageRoleDeveloper (465-465)
  • ResponsesMessageContentBlock (527-538)
  • ResponsesOutputMessageContentTypeRefusal (521-521)
  • ResponsesOutputMessageContentRefusal (584-586)
  • ResponsesMessageContent (469-472)
  • ResponsesMessageContentBlockType (513-513)
  • ResponsesInputMessageContentBlockImage (540-543)
  • ResponsesInputMessageContentBlockFile (545-549)
  • ResponsesInputMessageContentBlockAudio (551-554)
  • ResponsesFunctionToolCallOutput (860-863)
  • ResponsesMessageTypeReasoning (169-169)
  • ResponsesInputMessageContentBlockTypeText (516-516)
  • ResponsesInputMessageContentBlockTypeImage (517-517)
  • ResponsesInputMessageContentBlockTypeFile (518-518)
  • ResponsesInputMessageContentBlockTypeAudio (519-519)
  • ResponsesParameters (33-60)
  • ResponsesStreamOptions (62-64)
  • ResponsesParametersReasoning (106-110)
  • ResponsesTextConfig (66-69)
  • ResponsesResponse (85-98)
  • ResponsesExtendedResponseUsage (125-130)
core/utils.go (1)
  • Ptr (12-14)
core/schemas/utils.go (1)
  • Ptr (14-16)
core/schemas/bifrost.go (6)
  • BifrostChatRequest (128-134)
  • BifrostResponsesRequest (136-142)
  • BifrostResponse (177-192)
  • BifrostNonStreamResponseChoice (340-343)
  • ResponsesRequest (95-95)
  • BifrostChatResponseChoice (324-333)
core/schemas/providers/gemini/errors.go (2)
core/schemas/bifrost.go (1)
  • BifrostError (443-452)
core/schemas/providers/gemini/types.go (3)
  • GeminiChatRequestError (1334-1336)
  • Type (778-778)
  • GeminiChatRequestErrorStruct (1339-1343)
core/schemas/providers/bedrock/chat.go (4)
core/schemas/bifrost.go (9)
  • BifrostChatRequest (128-134)
  • BifrostResponse (177-192)
  • BifrostChatResponseChoice (324-333)
  • BifrostNonStreamResponseChoice (340-343)
  • LLMUsage (195-201)
  • BifrostResponseExtraFields (398-407)
  • RequestType (89-89)
  • ChatCompletionRequest (93-93)
  • Bedrock (48-48)
core/schemas/providers/bedrock/types.go (2)
  • BedrockConverseRequest (26-38)
  • BedrockConverseResponse (227-235)
core/schemas/chatcompletions.go (8)
  • ChatContentBlock (280-287)
  • ChatAssistantMessageToolCall (339-343)
  • ChatContentBlockTypeText (272-272)
  • ChatAssistantMessageToolCallFunction (346-349)
  • ChatAssistantMessage (316-320)
  • ChatMessageContent (214-217)
  • ChatMessage (202-211)
  • ChatMessageRoleAssistant (194-194)
core/utils.go (1)
  • Ptr (12-14)
core/providers/bedrock.go (10)
core/schemas/providers/bedrock/types.go (6)
  • BedrockConverseResponse (227-235)
  • BedrockError (346-350)
  • BedrockAnthropicTextResponse (211-215)
  • BedrockMistralTextResponse (219-224)
  • BedrockTitanEmbeddingResponse (429-432)
  • BedrockStreamEvent (360-377)
core/schemas/account.go (2)
  • Key (8-17)
  • BedrockKeyConfig (45-52)
core/schemas/bifrost.go (23)
  • BifrostError (443-452)
  • ErrorField (460-467)
  • BifrostTextCompletionRequest (120-126)
  • BifrostResponse (177-192)
  • Bedrock (48-48)
  • TextCompletionRequest (92-92)
  • RequestType (89-89)
  • BifrostChatRequest (128-134)
  • ChatCompletionRequest (93-93)
  • BifrostResponsesRequest (136-142)
  • ResponsesRequest (95-95)
  • BifrostEmbeddingRequest (144-150)
  • EmbeddingRequest (97-97)
  • BifrostStream (432-435)
  • ChatCompletionStreamRequest (94-94)
  • LLMUsage (195-201)
  • ModelProvider (42-42)
  • BifrostChatResponseChoice (324-333)
  • BifrostStreamResponseChoice (346-348)
  • BifrostStreamDelta (351-357)
  • BifrostResponseExtraFields (398-407)
  • BifrostSpeechRequest (152-158)
  • BifrostTranscriptionRequest (160-166)
core/utils.go (1)
  • Ptr (12-14)
core/schemas/utils.go (1)
  • Ptr (14-16)
core/schemas/providers/bedrock/text.go (1)
  • ToBedrockTextCompletionRequest (11-58)
core/schemas/provider.go (2)
  • Provider (189-212)
  • PostHookRunner (186-186)
core/schemas/providers/bedrock/chat.go (1)
  • ToBedrockChatCompletionRequest (11-41)
core/schemas/providers/bedrock/responses.go (1)
  • ToBedrockResponsesRequest (12-118)
core/schemas/providers/bedrock/embedding.go (3)
  • DetermineEmbeddingModelType (87-96)
  • ToBedrockTitanEmbeddingRequest (12-41)
  • ToBedrockCohereEmbeddingRequest (72-84)
core/schemas/providers/cohere/embedding.go (3)
core/schemas/bifrost.go (6)
  • BifrostEmbeddingRequest (144-150)
  • BifrostResponse (177-192)
  • BifrostEmbedding (270-274)
  • BifrostEmbeddingResponse (276-280)
  • LLMUsage (195-201)
  • BilledLLMUsage (232-237)
core/schemas/providers/cohere/types.go (2)
  • CohereEmbeddingRequest (214-224)
  • CohereEmbeddingResponse (232-239)
core/schemas/utils.go (4)
  • SafeExtractIntPointer (481-489)
  • SafeExtractString (275-292)
  • SafeExtractStringSlice (432-460)
  • SafeExtractStringPointer (463-478)
core/providers/anthropic.go (8)
core/schemas/providers/anthropic/types.go (4)
  • AnthropicMessageResponse (181-190)
  • AnthropicTextResponse (193-202)
  • AnthropicError (285-291)
  • AnthropicStreamEvent (227-235)
core/schemas/bifrost.go (16)
  • BifrostError (443-452)
  • BifrostTextCompletionRequest (120-126)
  • BifrostResponse (177-192)
  • Anthropic (47-47)
  • TextCompletionRequest (92-92)
  • RequestType (89-89)
  • BifrostChatRequest (128-134)
  • ChatCompletionRequest (93-93)
  • BifrostResponsesRequest (136-142)
  • ResponsesRequest (95-95)
  • BifrostEmbeddingRequest (144-150)
  • BifrostStream (432-435)
  • ChatCompletionStreamRequest (94-94)
  • BifrostChatResponseChoice (324-333)
  • BifrostSpeechRequest (152-158)
  • BifrostTranscriptionRequest (160-166)
core/schemas/providers/anthropic/text.go (1)
  • ToAnthropicTextCompletionRequest (11-46)
core/schemas/provider.go (2)
  • Provider (189-212)
  • PostHookRunner (186-186)
core/schemas/providers/anthropic/responses.go (1)
  • ToAnthropicResponsesRequest (111-174)
core/utils.go (1)
  • Ptr (12-14)
core/schemas/providers/anthropic/utils.go (1)
  • MapAnthropicFinishReasonToBifrost (31-36)
core/schemas/chatcompletions.go (2)
  • ChatAssistantMessageToolCall (339-343)
  • ChatAssistantMessageToolCallFunction (346-349)
core/providers/vertex.go (8)
core/schemas/providers/anthropic/types.go (1)
  • AnthropicMessageResponse (181-190)
core/schemas/account.go (2)
  • Key (8-17)
  • VertexKeyConfig (35-39)
core/schemas/bifrost.go (17)
  • BifrostTextCompletionRequest (120-126)
  • BifrostResponse (177-192)
  • BifrostError (443-452)
  • BifrostChatRequest (128-134)
  • Vertex (50-50)
  • ErrorField (460-467)
  • RequestCancelled (427-427)
  • BifrostResponseExtraFields (398-407)
  • RequestType (89-89)
  • ChatCompletionRequest (93-93)
  • BifrostResponsesRequest (136-142)
  • ResponsesRequest (95-95)
  • BifrostEmbeddingRequest (144-150)
  • EmbeddingRequest (97-97)
  • BifrostStream (432-435)
  • BifrostSpeechRequest (152-158)
  • BifrostTranscriptionRequest (160-166)
core/schemas/providers/anthropic/chat.go (1)
  • ToAnthropicChatCompletionRequest (329-537)
core/schemas/providers/openai/chat.go (1)
  • ToOpenAIChatRequest (20-35)
core/schemas/provider.go (2)
  • Provider (189-212)
  • PostHookRunner (186-186)
core/schemas/providers/vertex/embedding.go (1)
  • ToVertexEmbeddingRequest (8-67)
core/schemas/providers/vertex/types.go (2)
  • VertexEmbeddingRequest (19-22)
  • VertexEmbeddingResponse (42-44)
core/schemas/providers/anthropic/types.go (3)
core/schemas/utils.go (1)
  • ImageContentType (88-88)
core/schemas/chatcompletions.go (1)
  • ToolFunctionParameters (73-79)
core/schemas/bifrost.go (1)
  • LLMUsage (195-201)
core/schemas/chatcompletions.go (2)
core/schemas/providers/gemini/types.go (4)
  • Type (778-778)
  • Role (12-12)
  • Content (955-963)
  • FileData (1073-1081)
ui/lib/types/logs.ts (2)
  • Function (138-142)
  • Citation (227-234)
core/providers/cohere.go (6)
core/schemas/providers/cohere/types.go (14)
  • CohereChatResponse (277-283)
  • CohereChatRequest (11-28)
  • CohereError (415-419)
  • CohereEmbeddingResponse (232-239)
  • CohereStreamEvent (359-364)
  • StreamEventMessageStart (344-344)
  • StreamEventContentDelta (346-346)
  • StreamEventToolPlanDelta (348-348)
  • StreamEventContentStart (345-345)
  • StreamEventToolCallStart (349-349)
  • StreamEventToolCallDelta (350-350)
  • StreamEventMessageEnd (354-354)
  • StreamEventToolCallEnd (351-351)
  • StreamEventContentEnd (347-347)
core/schemas/bifrost.go (19)
  • BifrostTextCompletionRequest (120-126)
  • BifrostResponse (177-192)
  • BifrostError (443-452)
  • BifrostChatRequest (128-134)
  • Cohere (49-49)
  • ChatCompletionRequest (93-93)
  • RequestType (89-89)
  • BifrostResponsesRequest (136-142)
  • ResponsesRequest (95-95)
  • BifrostEmbeddingRequest (144-150)
  • EmbeddingRequest (97-97)
  • BifrostStream (432-435)
  • ChatCompletionStreamRequest (94-94)
  • BifrostChatResponseChoice (324-333)
  • BifrostResponseExtraFields (398-407)
  • LLMUsage (195-201)
  • BilledLLMUsage (232-237)
  • BifrostSpeechRequest (152-158)
  • BifrostTranscriptionRequest (160-166)
core/schemas/providers/cohere/chat.go (1)
  • ToCohereChatCompletionRequest (8-179)
core/schemas/provider.go (2)
  • Provider (189-212)
  • PostHookRunner (186-186)
core/schemas/providers/cohere/responses.go (1)
  • ToCohereResponsesRequest (11-80)
core/schemas/providers/cohere/embedding.go (1)
  • ToCohereEmbeddingRequest (6-62)
core/providers/openai.go (12)
core/schemas/bifrost.go (24)
  • BifrostTextCompletionRequest (120-126)
  • BifrostResponse (177-192)
  • BifrostError (443-452)
  • OpenAI (45-45)
  • TextCompletionRequest (92-92)
  • ModelProvider (42-42)
  • RequestType (89-89)
  • BifrostChatRequest (128-134)
  • ChatCompletionRequest (93-93)
  • BifrostResponsesRequest (136-142)
  • ResponsesRequest (95-95)
  • BifrostEmbeddingRequest (144-150)
  • EmbeddingRequest (97-97)
  • BifrostStream (432-435)
  • ChatCompletionStreamRequest (94-94)
  • BifrostSpeechRequest (152-158)
  • SpeechRequest (98-98)
  • BifrostSpeech (359-364)
  • BifrostResponseExtraFields (398-407)
  • SpeechStreamRequest (99-99)
  • BifrostTranscriptionRequest (160-166)
  • TranscriptionRequest (100-100)
  • TranscriptionStreamRequest (101-101)
  • ErrorField (460-467)
core/schemas/providers/openai/text.go (1)
  • ToOpenAITextCompletionRequest (8-25)
core/schemas/provider.go (2)
  • Provider (189-212)
  • PostHookRunner (186-186)
core/schemas/providers/openai/chat.go (1)
  • ToOpenAIChatRequest (20-35)
core/schemas/providers/openai/responses.go (1)
  • ToOpenAIResponsesRequest (18-36)
core/schemas/providers/openai/embedding.go (1)
  • ToOpenAIEmbeddingRequest (22-40)
core/schemas/utils.go (1)
  • Ptr (14-16)
core/schemas/chatcompletions.go (1)
  • ChatStreamOptions (43-46)
core/schemas/providers/openai/speech.go (1)
  • ToOpenAISpeechRequest (29-47)
core/schemas/providers/openai/transcription.go (1)
  • ToOpenAITranscriptionRequest (22-40)
core/schemas/providers/openai/types.go (1)
  • OpenAITranscriptionRequest (61-67)
core/schemas/transcriptions.go (1)
  • TranscriptionParameters (7-16)
core/providers/gemini.go (9)
core/schemas/account.go (1)
  • Key (8-17)
core/schemas/bifrost.go (21)
  • BifrostTextCompletionRequest (120-126)
  • BifrostResponse (177-192)
  • BifrostError (443-452)
  • BifrostChatRequest (128-134)
  • Gemini (57-57)
  • ChatCompletionRequest (93-93)
  • BifrostStream (432-435)
  • ChatCompletionStreamRequest (94-94)
  • BifrostResponsesRequest (136-142)
  • RequestType (89-89)
  • ResponsesRequest (95-95)
  • BifrostEmbeddingRequest (144-150)
  • EmbeddingRequest (97-97)
  • BifrostSpeechRequest (152-158)
  • SpeechRequest (98-98)
  • SpeechStreamRequest (99-99)
  • BifrostSpeech (359-364)
  • BifrostResponseExtraFields (398-407)
  • BifrostTranscriptionRequest (160-166)
  • TranscriptionRequest (100-100)
  • TranscriptionStreamRequest (101-101)
core/schemas/providers/openai/chat.go (1)
  • ToOpenAIChatRequest (20-35)
core/schemas/provider.go (2)
  • PostHookRunner (186-186)
  • Provider (189-212)
core/schemas/providers/gemini/speech.go (1)
  • ToGeminiSpeechRequest (5-48)
core/schemas/providers/gemini/types.go (2)
  • Type (778-778)
  • GenerateContentResponse (1279-1295)
core/utils.go (1)
  • Ptr (12-14)
core/schemas/utils.go (1)
  • Ptr (14-16)
core/schemas/providers/gemini/transcription.go (1)
  • ToGeminiTranscriptionRequest (5-73)
core/bifrost.go (7)
core/schemas/account.go (2)
  • Account (59-75)
  • Key (8-17)
core/schemas/plugin.go (1)
  • Plugin (43-63)
core/mcp.go (1)
  • MCPManager (48-55)
core/schemas/bifrost.go (23)
  • BifrostRequest (15-27)
  • TextCompletionRequest (92-92)
  • BifrostTextCompletionRequest (120-126)
  • BifrostResponse (177-192)
  • BifrostError (443-452)
  • ErrorField (460-467)
  • RequestType (89-89)
  • ChatCompletionRequest (93-93)
  • BifrostChatRequest (128-134)
  • ChatCompletionStreamRequest (94-94)
  • BifrostStream (432-435)
  • ResponsesRequest (95-95)
  • BifrostResponsesRequest (136-142)
  • ResponsesStreamRequest (96-96)
  • EmbeddingRequest (97-97)
  • BifrostEmbeddingRequest (144-150)
  • SpeechRequest (98-98)
  • BifrostSpeechRequest (152-158)
  • SpeechStreamRequest (99-99)
  • TranscriptionRequest (100-100)
  • BifrostTranscriptionRequest (160-166)
  • TranscriptionStreamRequest (101-101)
  • BifrostErrorExtraFields (469-473)
core/schemas/provider.go (2)
  • Provider (189-212)
  • PostHookRunner (186-186)
core/schemas/chatcompletions.go (3)
  • ChatTool (58-62)
  • ChatAssistantMessageToolCall (339-343)
  • ChatMessage (202-211)
core/utils.go (2)
  • Ptr (12-14)
  • IsStreamRequestType (123-125)
core/providers/utils.go (4)
core/schemas/bifrost.go (8)
  • ErrorField (460-467)
  • RequestCancelled (427-427)
  • BifrostErrorExtraFields (469-473)
  • ModelProvider (42-42)
  • RequestType (89-89)
  • BifrostError (443-452)
  • BifrostResponse (177-192)
  • BifrostChatResponseChoice (324-333)
core/utils.go (1)
  • Ptr (12-14)
core/schemas/utils.go (1)
  • Ptr (14-16)
core/schemas/provider.go (3)
  • HTTPProxy (69-69)
  • Provider (189-212)
  • CustomProviderConfig (126-130)
core/schemas/bifrost.go (8)
core/schemas/provider.go (1)
  • Provider (189-212)
transports/bifrost-http/handlers/completions.go (5)
  • ChatRequest (163-167)
  • ResponsesRequest (169-173)
  • EmbeddingRequest (175-179)
  • SpeechRequest (181-185)
  • TranscriptionRequest (187-191)
core/schemas/textcompletions.go (2)
  • TextCompletionInput (9-12)
  • TextCompletionParameters (50-69)
core/schemas/chatcompletions.go (3)
  • ChatMessage (202-211)
  • ChatParameters (11-40)
  • ChatAssistantMessageToolCall (339-343)
core/schemas/responses.go (4)
  • ResponsesMessage (176-188)
  • ResponsesParameters (33-60)
  • ResponsesResponse (85-98)
  • ResponsesExtendedResponseUsage (125-130)
core/schemas/embedding.go (2)
  • EmbeddingInput (10-15)
  • EmbeddingParameters (88-95)
core/schemas/speech.go (2)
  • SpeechInput (10-12)
  • SpeechParameters (14-23)
core/schemas/transcriptions.go (1)
  • TranscriptionParameters (7-16)
core/schemas/providers/cohere/types.go (3)
core/schemas/bifrost.go (1)
  • LogProbs (263-268)
core/schemas/providers/gemini/types.go (4)
  • Type (778-778)
  • Role (12-12)
  • Content (955-963)
  • FinishReason (21-21)
ui/lib/types/logs.ts (2)
  • Function (138-142)
  • ToolCall (155-159)
🔇 Additional comments (7)
core/schemas/mux.go (1)

1-865: LGTM! Comprehensive bidirectional conversion logic.

The file implements thorough bidirectional conversions between Chat Completions API and Responses API formats, including:

  • Tool and ToolChoice conversions in both directions
  • Message format translations with proper expansion/aggregation of tool calls
  • Request/response conversions preserving all relevant data
  • Proper handling of content blocks, refusals, and special message types

The implementation includes appropriate nil checks and defensive coding. Based on learnings, the cm.Content field access patterns are safe in this context.

core/schemas/providers/cohere/responses.go (1)

1-424: LGTM! Cohere Responses conversions look solid.

The file implements complete bidirectional conversions between Bifrost Responses structures and Cohere v2 chat structures. All critical nil checks are in place:

  • Lines 48 and 70 properly guard bifrostReq.Params access
  • Content and tool message conversions handle nil cases appropriately
  • System message aggregation and content block transformations are well-structured

The implementation safely handles optional fields and provides sensible defaults throughout.

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

1-432: LGTM! Comprehensive Bedrock type definitions.

The file provides thorough type coverage for the Bedrock API, including:

  • Request types (text completion, converse, embedding)
  • Response types (standard, streaming, errors)
  • Supporting types (messages, content blocks, tools, guardrails, usage, metrics)

The type definitions are well-documented and align with the Bedrock API contract. The use of optional pointers is appropriate for nullable fields.

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

1-297: LGTM! Well-structured Anthropic type definitions.

The file provides complete Anthropic API type coverage with several strengths:

  • Custom JSON marshalling/unmarshalling for AnthropicContent with validation (lines 69-103)
  • Comprehensive request/response types including streaming support
  • Proper use of optional pointers for nullable fields
  • Clear constant definitions for roles, content types, and tool types

The IsStreamingRequested() method implementation (lines 44-46) correctly implements the StreamingRequest interface.

core/schemas/providers/anthropic/chat.go (1)

1-787: Well-implemented Anthropic chat conversions.

The file provides comprehensive bidirectional conversions between Anthropic and Bifrost formats with several strengths:

  • Proper handling of system messages, content blocks, and tool interactions
  • Streaming response support with SSE formatting
  • Finish reason mapping via helper functions
  • Error conversion with nil-safe handling

The conversions handle complex scenarios like tool use/results and multi-block content appropriately.

core/schemas/bifrost.go (1)

282-318: Embedding union marshal/unmarshal looks solid

Covers string, 1D, and 2D float32 shapes with sonic; clear error on unknown shape.

core/schemas/chatcompletions.go (1)

241-265: Null-safe ChatMessageContent unmarshal — good fix

Now handles null/empty, clears stale fields, and prefers string before blocks.

@akshaydeo akshaydeo mentioned this pull request Oct 6, 2025
18 tasks
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
core/bifrost.go (1)

1300-1348: Don't recycle ChannelMessage-backed requests in the request pool

releaseChannelMessage currently pushes &msg.BifrostRequest into bifrostRequestPool, but that pointer aliases the struct embedded inside the pooled ChannelMessage. The next call to getBifrostRequest can hand out that alias while another goroutine concurrently reuses the same ChannelMessage, so both goroutines mutate the same struct, causing data races and corrupting requests.

Return the original pooled request immediately after copying it into the channel message (in both tryRequest/tryStreamRequest), and stop enqueueing the embedded copy. Reset the copy in place instead so the message pool stays clean without sharing pointers across goroutines.

@@ func (bifrost *Bifrost) tryRequest(...) (*schemas.BifrostResponse, *schemas.BifrostError) {
-	msg := bifrost.getChannelMessage(*preReq)
+	msg := bifrost.getChannelMessage(*preReq)
+	bifrost.releaseBifrostRequest(preReq)
@@ func (bifrost *Bifrost) tryStreamRequest(...) (chan *schemas.BifrostStream, *schemas.BifrostError) {
-	msg := bifrost.getChannelMessage(*preReq)
+	msg := bifrost.getChannelMessage(*preReq)
+	bifrost.releaseBifrostRequest(preReq)
@@ func (bifrost *Bifrost) releaseChannelMessage(msg *ChannelMessage) {
-	bifrost.releaseBifrostRequest(&msg.BifrostRequest)
+	resetBifrostRequest(&msg.BifrostRequest)

Also applies to: 1419-1462, 1811-1826

♻️ Duplicate comments (8)
core/schemas/providers/bedrock/chat.go (1)

53-91: Guard Bedrock output before dereferencing.

bedrockResp.Output is optional in error/guardrail cases; dereferencing bedrockResp.Output.Message without checking the parent pointer will panic. Please gate on bedrockResp.Output != nil before touching nested fields.

-	if bedrockResp.Output.Message != nil {
+	if bedrockResp.Output != nil && bedrockResp.Output.Message != nil {
 		for _, contentBlock := range bedrockResp.Output.Message.Content {
core/schemas/providers/bedrock/text.go (1)

11-24: Include an Input == nil guard.

The early condition dereferences bifrostReq.Input before confirming it’s set, so requests without Input panic instead of returning nil.

-func ToBedrockTextCompletionRequest(bifrostReq *schemas.BifrostTextCompletionRequest) *BedrockTextCompletionRequest {
-	if bifrostReq == nil || (bifrostReq.Input.PromptStr == nil && len(bifrostReq.Input.PromptArray) == 0) {
+func ToBedrockTextCompletionRequest(bifrostReq *schemas.BifrostTextCompletionRequest) *BedrockTextCompletionRequest {
+	if bifrostReq == nil || bifrostReq.Input == nil || (bifrostReq.Input.PromptStr == nil && len(bifrostReq.Input.PromptArray) == 0) {
 		return nil
 	}
core/schemas/providers/anthropic/responses.go (2)

937-961: Subject...


Need to mention line numbers. Example line in snippet for message type message is around 941 to 956.

Let's craft comment.

Include diff.

Do so.

Next, continue review of same file for other potential issues.

Look at `convertChatParameters`: there is direct type assertion `requestFields.(map[string]interface{})` flagged previously in past review comment for bedrock utils. But this is anthropic file? No, it's bedrock. We'll handle later.

Focus on anthropic file for other issues.

Potential issue: In `ToResponsesBifrostRequest`, after building `bifrostReq.Params` pointer, they may leave ExtraParams empty (map). For zero extra, empt map but pointer ensures Additional fields? Fine.

But there might be concurrency.

Maybe more bug: In `convertResponsesMessagesToAnthropicMessages`, when `msg.Content.ContentBlocks` is nil, but they set `contentBlocks` to nil and not assign to `anthropicMsg.Content`. For user message with nil content (maybe because message type is builder), they'd append message with zero content (allowed? maybe). But unstoppable.

Maybe there is more important bug: In `convertBifrostToolToAnthropic`, Type pointer to `AnthropicToolTypeCustom`. For Bifrost function tool, they expect `type: "tool"`? need confirm. But to be safe, that's less obviously critical; may degrade but we need evidence. Without verifying, maybe not.

Focus on nil deref bug: we will comment.

Any other issues? maybe more.

Check `convertBifrostMessagesToAnthropicContent` `resultBlock := AnthropicContentBlock{ Type: "tool_result", }` but they don't set `Content` if only `ResponsesToolMessage` nil but `msg.Content` not nil etc. Should be fine.

Hence one comment.

Need to highlight exactly.

Let's craft comment.

Also mention "As per coding guidelines" if referencing? Not needed.

Ok craft comment.

---

Now review next file.

File: core/providers/bedrock.go.

Need to inspect for issues.

Focus.

Large modifications.

Look for bugs.

1. `for range config.ConcurrencyAndBufferSize.Concurrency` used to pre-warm pool. This loops over `config.ConcurrencyAndBufferSize.Concurrency`. What type? probably int. In Go, `for range someInt` invalid. Wait: earlier, pre-warm likely `for i := 0; i < config.ConcurrencyAndBufferSize.Concurrency; i++` but they wrote `for range config.ConcurrencyAndBufferSize.Concurrency`? This is invalid; but file compiles maybe because `Concurrency` type is `int`? Actually `for range <int>` is compile error: cannot range over config.Concurrency... (type int). Unless Concurrency field is slice? Need check `config.ConcurrencyAndBufferSize.Concurrency`. Likely `int`. But code existed before? need check: they changed from `for range config.ConcurrencyAndBufferSize.Concurrency`. That would not compile. Maybe field is simple numeric but they had same earlier? Without context can't confirm. But if this code compiled, then `Concurrency` might be slice or map. Hard to know. But not part of diff? yes lines 68-71 show loops uses `for range config.ConcurrencyAndBufferSize.Concurrency {` Should compile only if Concurrency is string or array. But we can't confirm, maybe customizing.

Focus on issues.

`completeRequest`: `jsonBody, err := sonic.Marshal(requestBody)` – requestBody interface may be pointer, fine.

In context cancel part: they return BifrostError with Type pointer `schemas.Ptr(schemas.RequestCancelled)`. Good.

`setExtraHeadersHTTP` we earlier changed to accept []string; they pass nil.

`if err := signAWSRequest(...); err != nil { return nil, err }` but sign returns *BifrostError. Works.

`body, err := io.ReadAll(resp.Body)`.

For non-OK status, they unmarshal into `bedrock.BedrockError`. Works.

`TextCompletion`: `requestBody := bedrock.ToBedrockTextCompletionRequest(request)`. But note `request` pointer may be nil (should check). They didn't check nil. Should they? Typically provider interface ensures request non-nil. ok.

`if requestBody == nil` returns `newBifrostOperationError...` good.

Switch for model.

`bifrostResponse.ExtraFields.Provider = providerName` etc. Works.

`ChatCompletion`: new logic.

`Responses`: new method.

Focus on `completeRequest`: For context cancellation, they check `errors.Is(err, context.Canceled)` but `sonic.Marshal` rarely returns context error; unstoppable.

Look for potential bug: `requestBody := bedrock.ToBedrockTextCompletionRequest(request)` may return nil when request invalid. Already handled.

`ChatCompletion`: uses `bedrock.ToBedrockChatCompletionRequest(request)` which returns `(*BedrockConverseRequest, error)`. They handle error.

`bedrockResponse := acquireBedrockChatResponse()` returns pointer from pool. They `defer release`. After Unmarshal, they call `ToBifrostResponse()`. Potential concurrency: `bedrockResponse.ToBifrostResponse()` uses pointer that will be returned to pool after function returns (since release in defer). But they pass pointer to conversion function before release; inside `ToBifrostResponse` they likely read data.

Potential bug: In ChatCompletion and Responses, after they call `bedrockResponse.ToBifrostResponse()` they release to pool (defer). That's fine.

Focus on `Responses` method: they convert request to `bedrock.ToBedrockResponsesRequest`. That function may return nil, err.

After `bifrostResponse, err := bedrockResponse.ToResponsesBifrostResponse()` they set Extra Fields.

However note: `bedrockResponse.ToResponsesBifrostResponse()` may return `bifrostResp` with `ResponsesResponse` pointer embedded maybe nil for created fields. But they set ExtraFields (embedding). Works.

Need bug.

Check `Embedding`: uses `modelType := bedrock.DetermineEmbeddingModelType(request.Model)`. But `request` may include provider prefix (like "bedrock/anthropic.claude"). For embeddings, walkway. Accept.

Switch `modelType`. For "cohere" case, they call `cohereResp.ToBifrostResponse()` and `bifrostResponse.Model = request.Model`. That function probably sets Model string but maybe standard.

Need find major bug.

Focus on streaming: `processAWSEventStreamData` aggregator.

Look at `createBifrostChatCompletionChunkResponse` call: they pass `chunkIndex` pointer. Works.

Look for concurrency or incorrect usage.

Potential bug: In `ChatCompletionStream`, when verifying status: they call `resp, respErr := provider.client.Do(req)`. If error occurs after request but before they return, they may not close resp body? they return error after retrieving `resp.Body.Close()`? They call `return nil, newBifrostOperationError` before ensuring `resp.Body` closed. But if `provider.client.Do` returns `resp` even with error? no.

Focus on `provider.processAWSEventStreamData` - returns remaining bytes. They call `handleStreamEndWithSuccess` even if messageID empty? `createBifrostChatCompletionChunkResponse` uses `id` maybe "", but they set messageID when first event with role or creation (makes sure set). However if stream ended before anything (e.g., usage message first), messageID remains "", but they still send chunk with empty ID. Accept.

Possible bug: In `processEventBuffer`, when event has `delta.Text != nil` but `text` maybe "", they skip send (only if text != ""), but should send even if empty? maybe fine.

Now highlight potential bug: handling of non-JSON event data. Meh.

Focus on new function `Responses`: they set `bifrostResponse.ExtraFields.RequestType = schemas.ResponsesRequest`. Good.

But they do not propagate `response.ID` from bedrock? `bedrockResponse` is pointer to struct with ID etc. `ToResponsesBifrostResponse` uses `response.ID` etc.

Need more.

Potential bug: In `ChatCompletionStream`, when `processAndSendError` is invoked, they pass `provider.logger` (the last arg). wait signature: `processAndSendError(ctx, postHookRunner, err, responseChan, requestType, providerName, model, logger)`. They pass `provider.logger` but function expects `schemas.Logger`. Works.

Focus on potential missing pointer: `region := "us-east-1"` but if config.Region nil uses default. Accept.

Large.

Need to examine for major bug.

Maybe `bedrock.ToBedrockChatCompletionRequest(request)` returns request containing `ModelID`? `BedrockConverseRequest` struct has `ModelID string 'json:"-"`. In converter they set `ModelID = bifrostReq.Model`. Later `provider.getModelPath` uses either request.Model (passed by Bifrost). For bedrock we might need to parse provider prefix to actual bedrock model, use deployments map etc. Works.

Need to find actual bug.

Maybe they forgot to set `requestBody.ModelID` in `ToBedrockResponsesRequest`? In bedrock/responses.go, they set `bedrockReq := &BedrockConverseRequest{ ModelID: bifrostReq.Model }`. `provider.getModelPath` uses `request.Model` (the Bifrost model). Works.

Focus on concurrency. Maybe this code obtains pointer from pool, but `ToBifrostResponse` might return pointer referencing `bedrockResponse` content; after return, they release to pool, but `bifrostResponse` might share internal slices referencing reused struct. But they release after `ToBifrostResponse` returns (which presumably copies). Hard to confirm.

Maybe there is issue: `for range config.ConcurrencyAndBufferSize.Concurrency` suspicion may be critical if Concurrency is int -> compile error (can't compile). But maybe Concurrency is bitset? Without verifying, uncertain. We need more info. Probably there was similar code before; maybe restructure.

Focus on `Responses`: they set `bifrostResponse.ExtraFields.RawResponse` only if raw parse success. They ignore parse errors by not gating.

Ok.

But need at least one comment for this file? Only if real issue.

Need to check for context when `checkOperationAllowed` with `requestType` expecting new semantics.

Maybe unify.

Maybe there is bug when `bedrock.ToBedrockResponsesRequest` returns error due to missing tool name. They propagate.

No obvious bug. maybe consider `Responses` support: they call `bedrockReq := ...` but they don't set `reqBody.ModelID` to actual path? yes.

Maybe major bug: `bedrockResponse.ToResponsesBifrostResponse()` likely returns `ResponsesResponse` with `CreatedAt` from `time.Now()` (makes sense). But `bifrostResponse.ExtraFields.RequestType` set to `schemas.ResponsesRequest`. Good.

Ok.

Next file: `core/schemas/providers/bedrock/utils.go`.

Need to inspect.

Important: earlier existing comment flagged type assertion. In new code they may still have issue.

At lines 47-49: `bedrockReq.AdditionalModelRequestFields = requestFields.(map[string]interface{})`. They still have unsafe type assertion! Should be safe cast with ok check. Past comment flagged as bug. Not fixed. Should we mark? yes: This is critical? If ExtraParams contains other type -> panic. Should mark major or critical? They assert type from interface to map without check. If user passes other type, will panic at runtime causing crash. That's critical.

Need comment.

Also we should mention referenced earlier (maybe there is previous comment). In instructions: If similar to past review comment, must tag ``. There was existing comment flagged as critical by coderabbit on earlier commit referencing same issue. That comment states this diff to fix but not applied. Now code still has issue. We must mark comment but tag `` because similar? Wait instructions: If you generate a new review comment similar to past review comment, you must tag with ``. The existing comment in `<past_review_comments>` for this file flagged same issue, with diff to fix. Since code still has issue, we need to re-raise but tag `` to indicate repeated. Also mention the past comment.

Hence produce review comment with `` and ``? both? instructions maybe allow both? Not sure. They say "If you generate a new review comment that is similar to a past review comment, you must tag it with ``." But we still need mark severity. So include both `` and ``.

Need to craft comment referencing `Line 47` etc.

Also there may be other issues.

Continue scanning file.

`convertChatParameters`: they check `if len(bifrostReq.Params.ExtraParams) > 0`, but `ExtraParams` map may be nil, `len` on nil map = 0, safe.

But inside they check `if bifrostReq.Params != nil && bifrostReq.Params.ExtraParams != nil {` inside. Already known.

Need to check for other nil guard issues.

`convertMessages`: they just convert.

`convertToolMessage`: there is `toolResultContent` slice. Works.

`convertContent`: no error.

`convertContentBlock`: unstoppable.

`convertImageToBedrockSource`: uses `schemas.SanitizeImageURL`. For remote http(s) returns sanitized URL with type `ImageContentTypeURL` maybe? `ExtractURLTypeInfo` returns type? In new `core/providers/utils.go` they removed those functions? Wait they removed from providers utils? maybe new location? `schemas.ExtractURLTypeInfo` may still exist.

`convertInferenceConfig`: returns pointer even if all zero, but `config` zero value; returning pointer ensures `MaxTokens` nil etc.

`convertToolConfig`: loops over tools. They don't check tool.Name exist; Bifrost `tool.Function.Name` is not pointer? `Name string`. They use as string.

`extractToolsFromConversationHistory`: they call `toolCall.Function.Name != nil`: but in Bifrost, `Name` type is pointer? `ChatAssistantMessageToolCallFunction` struct has `Name *string`. In call they check nil. Good.

`convertToolCallToContentBlock`: `sonic.Unmarshal([]byte(toolCall.Function.Arguments), &input)` but `Arguments` string may not be valid json; they fallback to empty object.

Large.

Hence comment on type assertion.

Maybe other bug: `convertChatParameters` uses `len(bifrostReq.Params.ExtraParams) > 0` but `ExtraParams` may be `nil`, safe.

But there is also `requestFields.(map[string]interface{})` bug.

Ok.

Next file: core/schemas/bifrost.go.

Need to examine for issues.

Large modifications.

Look for major bug.

Focus: `BifrostResponseExtraFields.ChunkIndex int 'json:"chunk_index"'` not omitempty; for non-streaming should be 0? They set zero. Accept.

Focus on `BifrostEmbeddingResponse.MarshalJSON`: when `EmbeddingArray` zero length but nil vs empty? They check `if be.EmbeddingArray != nil`. But in Go, zero-length slice not nil; after unmarshalling empty array -> [] -> non-nil. Works.

`BifrostResponse` struct embed `*ResponsesResponse`. For non responses, pointer nil.

Potential bug: In `BifrostResponseExtraFields`, `ChunkIndex` will default 0; for non streaming they want 0 maybe but enumerations. Accept.

But there might be nil pointer: `BifrostError` struct, `Error *ErrorField` field pointer.

`BifrostRequest` struct: contains `RequestType RequestType`. But they also have specific request pointers. Need to ensure there is a path verifying only one set? not.

Focus on `BifrostChatResponseChoice` struct: `*BifrostTextCompletionResponseChoice` pointer embedded? Wait: `BifrostTextCompletionResponseChoice` pointer? They embed pointer by value but note they don't add pointer star in struct? they embed pointer? In code:

type BifrostChatResponseChoice struct {
...
*BifrostTextCompletionResponseChoice

*BifrostNonStreamResponseChoice
*BifrostStreamResponseChoice

}


By default embedding pointer? In Go, embedding pointer type? They missing pointer for NonStream? They embed pointer to struct type but not pointer—they embed pointer due to `*BifrostTextCompletionResponseChoice`. But `*BifrostNonStreamResponseChoice`? They embed pointer but restful.

Maybe unstoppable.

Look for major bug: `BifrostRequest` type: fields `Provider`, `Model`, `Fallbacks`, `RequestType`. But specific request types (e.g., `BifrostTextCompletionRequest`) also include Provider, Model, etc. Possibly duplication. But restful.

Maybe there is concurrency issue.

Focus on `BifrostEmbeddingResponse.UnmarshalJSON`: In `if err := sonic.Unmarshal(data, &stringContent); err == nil { ... }` People storing base64? Works.

Look for unstoppable.

No major bug.

Next file: core/schemas/chatcompletions.go (Large). Need to inspect for issues.

Focus on `ChatToolChoice.UnmarshalJSON`: On string variant, they attempt to unmarshal to string. However, if JSON is object with field `{"type":"auto"}` they parse into struct (makes sense). For string they store pointer to string. But they don't handle `null`: they go to string unmarshal, fail, then struct, fail, then error. For `null` they'd first try string -> err (can't unmarshal string? `sonic.Unmarshal(null, &toolChoiceStr)` returns error? It probably returns error because null not string; they then attempt struct, also fails, and return error. But they should treat null gracefully (should set both nil). Unless spec expects no null. Maybe not necessary but nice.

Focus on `ChatMessageContent.MarshalJSON`: they check if both `ContentStr` and `ContentBlocks` set; but `ContentBlocks` zero value is nil. Works.

`ChatMessageContent.UnmarshalJSON` now handles null.

Check `ChatContentBlockType`: they include "input_file" but restful.

`ChatAssistantMessageAnnotation` maybe.

Need to find bug.

Maybe `ChatAssistantMessageAnnotationCitation` tags.

Everything else.

Ok.

Now we must produce review comments for issues found: at least two so far: nil pointer in anthropic conversion and type assertion in bedrock utils.

Need to ensure no other major issues.

Also maybe mention in bedrock utils `convertChatParameters` type assertion for AdditionalModelRequestFields (the same as flagged). Another potential: They don't set AdditionalModelRequestFields for string pointer? unstoppable.

Need to ensure we include review comment for nil pointer.

Let's craft comment in anthropic file.

Switch to final.

Construct review comment:


942-961: Guard nil Response message content before dereferencing.
Line 943 ...


But we need actual line numbers referencing from final code. Provided snippet line numbers 937 etc? We'll craft note referencing `Line 939` etc.

Let's produce comment with diff to guard.

Also mention that same pattern occurs in default block.

The diff should include guard.

Maybe we can show diff:

  •   if msg.Content.ContentStr != nil {
    
  •   if msg.Content != nil && msg.Content.ContentStr != nil {
    

...

  •   } else if msg.Content.ContentBlocks != nil {
    
  •   } else if msg.Content != nil && msg.Content.ContentBlocks != nil {
    

...


Similarly in default block at bottom.

Need to mention both.

Let's craft diff covering both sections.

Add comment.

Next comment for bedrock utils.

Also ensure we respond for other files? For rest, no comments needed but still wrap file markers. Already for utils we had no comment but we must have start and end marker even w/out comment (done). For bedrock file we need markers plus comment if any. We'll include start marker with scratchpad.

Ok.

For bedrock utils we need comment.

Maybe also check additional issues there: In `convertChatParameters`, there is similar type assertion for AdditionalModelRequestFields (should safe). We will highlight.

Let's craft comment referencing lines 47-49 etc.

Also include diff for fix.

Now also mark ``.

Need to ensure we add scratchpad entries for each file.

Currently we only added scratchpad for first file (utils). Need to add for anthropic.

Let's update scratchpad.

Need to maintain instructions—they want scratchpad comment preceding each file markers as we work? Already done for first file but not for others. Need to produce for each file.

For anthropic file we need scratchpad entry with tasks etc. Already not done? We need to include in final response? We can add now before file block? The instructions require scratchpad being hidden comment inside each file block? Example in guidelines, they inserted scratchpad within file block. So for each file we must include scratchpad state.

For `core/providers/utils.go` we already inserted scratchpad with tasks etc albeit minimal.

Need to do the same for other files: before `review_comment` etc include scratchpad (per instructions). Need to ensure there is entry even if comment or not.

For `core/schemas/providers/anthropic/responses.go`, we need to add scratchpad entry plus comment.

Also for `core/providers/bedrock.go` even if no comment to raise? we may not have comment but need to include scratchpad bullet to state review done (maybe no issues). We'll include.

For `core/schemas/providers/bedrock/utils.go` we need scratchpad plus comment.

For `core/schemas/bifrost.go` and `core/schemas/chatcompletions.go`, also need scratchpad entries even if no issues.

Maybe there might be more issues? Should we consider verifying other nil pointer? For `convertBifrostMessagesToAnthropicContent` default case, we should also guard `msg.Content` before referencing. We'll include in diff.

Also `case schemas.ResponsesMessageTypeMessage:` we need to set `contentBlocks` to nil even if `msg.Content` nil? If message has nil content we should skip? maybe we should skip (maybe degrade). We'll handle by checking `if msg.Content != nil { ... } else continue`. But we should adjust diff accordingly.

Let's craft diff to add nil guard in both cases.

Now writing comment.

Open review comment.

Need to mention severity.

Let's craft.

Additionally, after diff we produce final response.

Let's craft actual comment.

First, we need `review_comment_start` with line range.

Line range approx 941-963? We'll set Spl.

But actual line numbers maybe inaccurate; we use start and end referencing provided numbering. Provide `line_ranges: 937-976` etc.

Let's craft comment.

Subject line bold.

E.g., `**Fix nil Content dereference before building Anthropic blocks.**`

Then short explanation.

Add diff.

Remember to include ``.

Ok.

Next comment for bedrock utils.

Line 47 etc.

Construct comment.

Maybe also mention "Based on previous feedback" not necessary but we can mention earlier comment.

Now we need to ensure final response includes all file_end markers etc.

Let's plan final output:

For each file:

1. `core/providers/utils.go`: Already handled (no comments). Already inserted file block with scratchpad earlier? yes we inserted earlier but with no review comments. That block had scratchpad and file_end. Good.

2. `core/schemas/providers/anthropic/responses.go`: Need to create block with scratchpad and comment.

3. `core/providers/bedrock.go`: Provide block with scratchpad no comment.

4. `core/schemas/providers/bedrock/utils.go`: Provide block with scratchpad plus comment.

5. `core/schemas/bifrost.go`: Provide block with scratchpad no comment.

6. `core/schemas/chatcompletions.go`: Provide block with scratchpad no comment maybe.

But we might need to check for issues there? Should we ensure there isn't bug? maybe restful.

But to be thorough maybe degrade.

Take quick look at chatcompletions: `ChatToolChoice.MarshalJSON` still returns string pointer; unstoppable.

Ok.

One more cross-check: In `convertImageToBedrockSource`, they call `schemas.SanitizeImageURL` which now expects restful (since functions removed from providers). Should still exist.

Now craft final output.

Need to incorporate scratchpad for each file.

Let's craft for anthropic file:

...

core/providers/vertex.go (1)

543-556: Preserve OAuth token when no Vertex API key is provided

You correctly build authHeader only when key.Value is non-empty, but the call still hardcodes map[string]string{"Authorization": "Bearer " + key.Value}. When key.Value is empty (the normal service-account case) this overwrites the OAuth header with an empty bearer token and the stream 401s again.

Pass the guarded map you built:

-		return handleOpenAIStreaming(
+		return handleOpenAIStreaming(
 			ctx,
 			client,
 			url,
 			request,
-			map[string]string{"Authorization": "Bearer " + key.Value},
+			authHeader,
 			provider.networkConfig.ExtraHeaders,
 			schemas.Vertex,
 			postHookRunner,
 			provider.logger,
 		)
core/providers/gemini.go (2)

508-510: Add nil check for request to prevent panic.

The code dereferences request.Input without first verifying that request itself is non-nil. This creates the same critical issue as in the non-streaming Transcription method. Also, the check should catch empty slices, not just nil slices.

Apply this diff:

-  if request.Input == nil || request.Input.File == nil {
+  if request == nil || request.Input == nil || len(request.Input.File) == 0 {
     return nil, newBifrostOperationError("transcription input is not provided", fmt.Errorf("empty file input"), providerName)
   }

Based on past review comments.


459-461: Add nil check for request to prevent panic.

The code dereferences request.Input without first verifying that request itself is non-nil. If request is nil, this line panics instead of returning a proper error. Additionally, checking only request.Input.File == nil misses empty (zero-length) slices.

Apply this diff to fix both issues:

-  if request.Input == nil || request.Input.File == nil {
+  if request == nil || request.Input == nil || len(request.Input.File) == 0 {
     return nil, newBifrostOperationError("transcription input is not provided", fmt.Errorf("empty file input"), providerName)
   }

Based on past review comments.

core/schemas/providers/bedrock/utils.go (1)

47-49: Unsafe type assertion may panic; guard before assign.

Use an ok‑check when setting AdditionalModelRequestFields.

-			if requestFields, exists := bifrostReq.Params.ExtraParams["additionalModelRequestFieldPaths"]; exists {
-				bedrockReq.AdditionalModelRequestFields = requestFields.(map[string]interface{})
-			}
+			if requestFields, exists := bifrostReq.Params.ExtraParams["additionalModelRequestFieldPaths"]; exists {
+				if fields, ok := requestFields.(map[string]interface{}); ok {
+					bedrockReq.AdditionalModelRequestFields = fields
+				}
+			}
🧹 Nitpick comments (10)
core/providers/gemini.go (1)

513-518: Remove redundant nil check.

After the early return at lines 508-510 when request.Input.File is nil or empty, the check if request.Input.File != nil at line 513 is redundant—execution cannot reach this point with a nil or empty File.

Apply this diff to simplify:

-  // Check file size limit (Gemini has a 20MB limit for inline data)
-  if request.Input.File != nil {
-    const maxFileSize = 20 * 1024 * 1024 // 20MB
-    if len(request.Input.File) > maxFileSize {
-      return nil, newBifrostOperationError("audio file too large for inline transcription", fmt.Errorf("file size %d bytes exceeds 20MB limit", len(request.Input.File)), providerName)
-    }
+  // Check file size limit (Gemini has a 20MB limit for inline data)
+  const maxFileSize = 20 * 1024 * 1024 // 20MB
+  if len(request.Input.File) > maxFileSize {
+    return nil, newBifrostOperationError("audio file too large for inline transcription", fmt.Errorf("file size %d bytes exceeds 20MB limit", len(request.Input.File)), providerName)
   }
core/providers/bedrock.go (3)

183-188: Preserve Bedrock error type/code in BifrostError.

Return type/code to aid clients and observability.

-		return nil, &schemas.BifrostError{
-			StatusCode: &resp.StatusCode,
-			Error: &schemas.ErrorField{
-				Message: errorResp.Message,
-			},
-		}
+		return nil, &schemas.BifrostError{
+			StatusCode: &resp.StatusCode,
+			Error: &schemas.ErrorField{
+				Type:    schemas.Ptr(errorResp.Type),
+				Code:    errorResp.Code,
+				Message: errorResp.Message,
+			},
+		}

704-711: Tighten event filter: use “metrics”/“trace” instead of “metadata”.

Bedrock stream events expose usage, stopReason, metrics, trace. “metadata” here likely misses metrics events.

-						bytes.Contains(jsonBytes, []byte(`metadata`))
+						bytes.Contains(jsonBytes, []byte(`metrics`)) ||
+						bytes.Contains(jsonBytes, []byte(`trace`))

317-320: Fix provider name in comment (typo).

This method targets Bedrock, not Anthropic.

-// Responses performs a chat completion request to Anthropic's API.
-// It formats the request, sends it to Anthropic, and processes the response.
+// Responses performs a chat completion request to Bedrock's API.
+// It formats the request, sends it to Bedrock, and processes the response.
core/schemas/providers/bedrock/utils.go (5)

52-56: Broaden response field paths handling ([]string or []interface{}).

ExtraParams may arrive as []any from JSON; convert safely.

-			if responseFields, exists := bifrostReq.Params.ExtraParams["additionalModelResponseFieldPaths"]; exists {
-				if fields, ok := responseFields.([]string); ok {
-					bedrockReq.AdditionalModelResponseFieldPaths = fields
-				}
-			}
+			if responseFields, exists := bifrostReq.Params.ExtraParams["additionalModelResponseFieldPaths"]; exists {
+				switch v := responseFields.(type) {
+				case []string:
+					bedrockReq.AdditionalModelResponseFieldPaths = v
+				case []interface{}:
+					fields := make([]string, 0, len(v))
+					for _, it := range v {
+						if s, ok := it.(string); ok {
+							fields = append(fields, s)
+						}
+					}
+					if len(fields) > 0 {
+						bedrockReq.AdditionalModelResponseFieldPaths = fields
+					}
+				}
+			}

89-93: Make requestMetadata robust to map[string]any input.

Support both map[string]string and map[string]any.

-			if reqMetadata, exists := bifrostReq.Params.ExtraParams["requestMetadata"]; exists {
-				if metadata, ok := reqMetadata.(map[string]string); ok {
-					bedrockReq.RequestMetadata = metadata
-				}
-			}
+			if reqMetadata, exists := bifrostReq.Params.ExtraParams["requestMetadata"]; exists {
+				if metadata, ok := reqMetadata.(map[string]string); ok {
+					bedrockReq.RequestMetadata = metadata
+				} else if metadataAny, ok := reqMetadata.(map[string]interface{}); ok {
+					out := make(map[string]string, len(metadataAny))
+					for k, v := range metadataAny {
+						if s, ok := v.(string); ok {
+							out[k] = s
+						} else {
+							out[k] = fmt.Sprintf("%v", v)
+						}
+					}
+					bedrockReq.RequestMetadata = out
+				}
+			}

357-377: Avoid emitting empty inferenceConfig objects.

Return nil unless at least one field is set.

-func convertInferenceConfig(params *schemas.ChatParameters) *BedrockInferenceConfig {
-	var config BedrockInferenceConfig
-	if params.MaxCompletionTokens != nil {
-		config.MaxTokens = params.MaxCompletionTokens
-	}
-	if params.Temperature != nil {
-		config.Temperature = params.Temperature
-	}
-	if params.TopP != nil {
-		config.TopP = params.TopP
-	}
-	if params.Stop != nil {
-		config.StopSequences = params.Stop
-	}
-	return &config
-}
+func convertInferenceConfig(params *schemas.ChatParameters) *BedrockInferenceConfig {
+	var cfg BedrockInferenceConfig
+	has := false
+	if params.MaxCompletionTokens != nil {
+		cfg.MaxTokens = params.MaxCompletionTokens
+		has = true
+	}
+	if params.Temperature != nil {
+		cfg.Temperature = params.Temperature
+		has = true
+	}
+	if params.TopP != nil {
+		cfg.TopP = params.TopP
+		has = true
+	}
+	if params.Stop != nil {
+		cfg.StopSequences = params.Stop
+		has = true
+	}
+	if !has {
+		return nil
+	}
+	return &cfg
+}

210-226: Unify JSON library usage (prefer sonic).

For consistency and perf, use sonic.Unmarshal here too.

-		if err := json.Unmarshal([]byte(*msg.Content.ContentStr), &parsedOutput); err != nil {
+		if err := sonic.Unmarshal([]byte(*msg.Content.ContentStr), &parsedOutput); err != nil {

528-534: Remove unreachable tool content checks.

ChatContentBlockType has no "tool_use"/"tool_result" variants; this branch never fires.

-			if block.Type == "tool_use" || block.Type == "tool_result" {
-				hasContent = true
-			}
+			// No-op: tool_use/tool_result are not ChatContentBlock types here.
core/schemas/chatcompletions.go (1)

126-147: Fix docstrings for ChatToolChoice (copy/paste from ChatMessageContent).

Docs mention ChatMessageContent; update to describe ChatToolChoice marshal/unmarshal.

-// MarshalJSON implements custom JSON marshalling for ChatMessageContent.
-// It marshals either ContentStr or ContentBlocks directly without wrapping.
+// MarshalJSON implements custom JSON marshalling for ChatToolChoice.
+// It marshals either a string (choice) or a struct (detailed choice).
@@
-// UnmarshalJSON implements custom JSON unmarshalling for ChatMessageContent.
-// It determines whether "content" is a string or array and assigns to the appropriate field.
-// It also handles direct string/array content without a wrapper object.
+// UnmarshalJSON implements custom JSON unmarshalling for ChatToolChoice.
+// It accepts either a string (choice) or a struct (detailed choice).

@akshaydeo akshaydeo merged commit 249077d into main Oct 6, 2025
2 of 3 checks passed
@akshaydeo akshaydeo deleted the v1.3.0 branch October 6, 2025 10:00
akshaydeo added a commit that referenced this pull request Nov 17, 2025
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.

4 participants