Skip to content

Conversation

@Radheshg04
Copy link
Contributor

@Radheshg04 Radheshg04 commented Dec 2, 2025

Summary

This PR introduces image generation support to Bifrost and wires it into the existing gateway infrastructure. It adds core schemas, provider extensions, HTTP transport, streaming plumbing, cache integration, and initial UI support to address #950.

Changes

Implementation is in progress. Completed functionality is marked, and remaining work will be completed in follow-up commits before marking this PR ready for review.

Scoped Changes:

  • New /v1/images/generations endpoint (OpenAI-compatible)
  • Image generation via Chat Completion API (tool use pattern)
  • Image generation via Responses API (native support)
  • Streaming image delivery (base64 chunks)
  • Semantic caching for image generation
  • UI components for image rendering
  • Provider implementations: OpenAI DALL-E, Azure DALL-E

Rollout Plan:

  • Phase 1: Core schema and provider implementation (OpenAI + Azure)
  • Phase 2: HTTP transport and non-streaming endpoint
  • Phase 3: Streaming support and accumulator
  • Phase 4: Semantic cache integration (Base64 storage, 5min TTL)
  • Phase 5: UI components and documentation

Type of change

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

Affected areas

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

Testing Plan

Unit Tests

  • Schema serialization/deserialization
  • Request transformation (Bifrost → OpenAI format)
  • Response transformation (OpenAI → Bifrost format)
  • Stream chunk accumulation
  • Cache key generation

Integration Tests

  • End-to-end image generation (non-streaming)
  • End-to-end streaming image generation
  • Fallback to secondary provider
  • Cache hit/miss scenarios
  • Error handling (rate limits, invalid prompts)

Load Tests

  • Concurrent image generation requests
  • Stream memory usage under load
  • Cache performance at scale

How to test

End-to-end image generation

# Test image generation endpoint
curl -X POST http://localhost:8080/v1/images/generations \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $API_KEY" \
  -d '{
    "model": "openai/dall-e-3",
    "prompt": "A serene Japanese garden with cherry blossoms",
    "size": "1024x1024",
    "quality": "high",
    "response_format": "b64_json"
  }'

Expected Outcome:

{
"id": "abc123",
"created": 1699999999,
"model": "dall-e-3",
"data": [
  {
    "b64_json": "iVBORw0KGgo...",
    "revised_prompt": "A tranquil Japanese garden featuring blooming cherry blossom trees...",
    "index": 0
  }
],
"usage": {
  "prompt_tokens": 15,
  "total_tokens": 15
},
"extra_fields": {
  "provider": "openai",
  "latency_ms": 8500,
  "cache_debug": null
}
}

Unit Tests:

# Run tests from repository root
go test -v github.com/maximhq/bifrost/core/providers/openai -run TestImage
go test -v github.com/maximhq/bifrost/core -run TestImage

Expected outcome: PASS
Note: Might need to replace remote paths with local modules in go.mod (Once PR is merged, this directive is not needed)

replace github.com/maximhq/bifrost/core => ../../core

Screenshots/Recordings

N/A

Breaking changes

  • Yes
  • No

Related issues

Closes #950

Security considerations

N/A

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 Dec 2, 2025

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added image generation support (batch + streaming), HTTP endpoints, and UI image rendering with streaming progress.
  • Improvements

    • Streamed chunk assembly, progress reporting, latency/usage metadata, provider/model parameters, and retry/validation logic.
    • Image-aware semantic caching with deterministic keys.
  • Tests & Tools

    • Comprehensive image-generation tests: serialization, streaming, load, cache, fallback, and error scenarios; test utilities and validators.
  • Docs

    • Quickstart and provider matrix updated with image generation examples.

✏️ Tip: You can customize this high-level summary in your review settings.

Walkthrough

Adds end-to-end image generation: new image request/stream schemas and Bifrost APIs, provider image methods (OpenAI/Azure + stubs), streaming accumulator and chunk reassembly, semantic-cache image keys/metadata, HTTP image endpoints (streaming and non‑streaming), UI image streaming hook/component, and many image-focused tests/utilities.

Changes

Cohort / File(s) Summary
Core Schemas & API
core/schemas/images.go, core/schemas/bifrost.go, core/schemas/provider.go, core/schemas/responses.go
Add Bifrost image request/response/stream types, RequestType variants, extend BifrostRequest/BifrostResponse/BifrostStream unions, and add Provider image-generation method signatures.
Core Entry & Lifecycle
core/bifrost.go, core/utils.go, core/providers/utils/utils.go
New public Bifrost.ImageGenerationRequest / ImageGenerationStreamRequest entry points; lifecycle, fallback propagation, reset, and stream/non-stream handling extended to include image cases and marshal responses.
OpenAI Provider
core/providers/openai/*.go, core/providers/openai/types.go, core/providers/openai/openai_image_test.go
Implement OpenAI image generation and streaming: request/response mapping, streaming SSE handling, chunking, usage/latency mapping, orchestration, types, and unit tests.
Azure Provider
core/providers/azure/*.go
Add Azure image request/response mapping and OpenAI-compatible streaming delegation; conversion helpers and types added.
Other Providers (stubs & arity changes)
core/providers/*/*.go (Anthropic, Bedrock, Cerebras, Cohere, Elevenlabs, Gemini, Groq, Mistral, Ollama, OpenRouter, Parasail, Perplexity, SGL, Vertex, etc.)
Add ImageGeneration / ImageGenerationStream stubs returning unsupported-operation; update streaming call-sites to match expanded GetBifrostResponseForStreamResponse arity.
Streaming Framework
framework/streaming/types.go, framework/streaming/accumulator.go, framework/streaming/images.go
Introduce StreamTypeImage, ImageStreamChunk, pooling, accumulation, addImageStreamChunk, processImageStreamingResponse, chunk sorting/reassembly, and final response assembly.
Semantic Cache Plugin
plugins/semanticcache/*.go
Add image cache key generation (xxhash), vector properties extended (image_urls/image_b64/revised_prompts), request hashing short-circuit for image types, metadata extraction and storage helpers.
HTTP Transport & Router
transports/bifrost-http/handlers/inference.go, transports/bifrost-http/integrations/openai.go, transports/bifrost-http/integrations/router.go
Add POST /v1/images/generations route and ImageGenerationHTTPRequest type; streaming SSE handler and response converters; integration route configs and Azure prehook support for image requests.
UI: Components & Hooks
ui/components/chat/ImageMessage.tsx, ui/hooks/useImageStream.ts, ui/app/.../allowedRequestsFields.tsx, ui/lib/constants/logs.ts, ui/lib/types/config.ts
Add ImageMessage component and useImageStream hook for progressive rendering; UI enums/labels/colors and config types extended for image request types.
Tests & Test Utilities
core/images_test.go, core/internal/testutil/*.go, core/providers/openai/openai_test.go
Add comprehensive image tests and test utilities: serialization, stream chunk assembly, cache tests, error/fallback/load/stream tests, retry frameworks, validators, and scenario flags/config additions.
Misc / Docs & Changelogs
core/changelog.md, framework/changelog.md, plugins/semanticcache/changelog.md, transports/changelog.md, docs/**, ui/**
Changelog entries and docs updated to document image generation and streaming; provider support matrix and quickstarts updated.

Sequence Diagram(s)

sequenceDiagram
    actor Client
    participant HTTP as HTTP Handler
    participant Bifrost as Bifrost Core
    participant Cache as Semantic Cache
    participant Provider as Provider (OpenAI/Azure)
    participant Stream as Streaming Framework

    Client->>HTTP: POST /v1/images/generations (prompt, provider, model, params)
    HTTP->>Bifrost: ImageGenerationRequest(ctx, req)
    Bifrost->>Cache: lookup(getImageCacheKey)
    Cache-->>Bifrost: hit/miss

    alt Cache hit
        Bifrost-->>HTTP: return cached BifrostImageGenerationResponse
        HTTP-->>Client: 200 OK (images)
    else Cache miss
        Bifrost->>Provider: ImageGeneration(ctx, key, req)
        Provider->>Provider: vendor API call
        Provider-->>Bifrost: BifrostImageGenerationResponse
        Bifrost->>Cache: store response
        Bifrost-->>HTTP: return response
        HTTP-->>Client: 200 OK (images)
    end
Loading
sequenceDiagram
    actor Client
    participant HTTP as HTTP Handler
    participant Bifrost as Bifrost Core
    participant Provider as Provider (OpenAI/Azure)
    participant Stream as Streaming Framework
    participant Accum as Accumulator

    Client->>HTTP: POST /v1/images/generations?stream=true (prompt,...)
    HTTP->>Bifrost: ImageGenerationStreamRequest(ctx, req)
    Bifrost->>Provider: ImageGenerationStream(ctx, postHook, key, req)
    Provider->>Stream: SSE stream of image chunks
    loop per chunk
        Stream->>Accum: addImageStreamChunk(requestID, chunk)
        Accum-->>HTTP: partial BifrostImageGenerationStreamResponse
        HTTP-->>Client: SSE data: {partial chunk}
    end
    Provider->>Stream: completion event (+usage)
    Stream->>Accum: addImageStreamChunk(final=true)
    Accum->>Accum: assemble images (buildCompleteImageFromImageStreamChunks)
    Accum-->>HTTP: final BifrostImageGenerationStreamResponse (complete + usage)
    HTTP-->>Client: SSE data: {complete response}
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Areas needing focused review:

  • OpenAI / Azure provider image implementations and streaming SSE handling.
  • Streaming accumulator: chunk ordering, pooling, interleaved image chunk reassembly, and concurrency.
  • Semantic cache image-key determinism and metadata extraction (xxhash inputs).
  • Core dispatch changes and updated GetBifrostResponseForStreamResponse arity—ensure all call-sites updated.
  • Tests & retry framework — watch duplicated declarations and consistent expectation wiring.

Poem

"I stitched the prompt in moonlit hops, each chunk a tiny bead,

I nibble bits of base64 until a full bright picture's freed.
Cache keeps crumbs for quicker feasts, providers hum along,
Accumulators sort my bits and sing the final song.
A rabbit paints with streaming light — hooray, the images are strong! 🐇✨"

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.90% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: support for image generation' clearly and concisely describes the main feature added: image generation support across the Bifrost platform.
Description check ✅ Passed The PR description comprehensively covers the purpose, completed changes with checkmarks, type of change, affected areas, testing plan, and related issue. It follows the template structure and provides sufficient detail.
Linked Issues check ✅ Passed The code changes comprehensively fulfill all core requirements from issue #950: image generation schemas, provider interface extensions, OpenAI/Azure implementations, streaming support, semantic caching, HTTP endpoint, and UI components are all present.
Out of Scope Changes check ✅ Passed All changes are directly aligned with the image generation feature scope from issue #950. Modifications to schemas, providers, transports, streaming framework, caching, UI, and documentation are all within the defined objectives.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@Radheshg04 Radheshg04 marked this pull request as ready for review December 4, 2025 16:43
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: 15

Caution

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

⚠️ Outside diff range comments (2)
core/schemas/responses.go (1)

525-573: Fix UnmarshalJSON ordering to avoid misclassifying computer tool outputs as image-generation outputs

The new ResponsesImageGenerationCallOutput integration introduces a bug in the UnmarshalJSON method:

  • For JSON objects like a computer tool screenshot output
    {"type": "computer_screenshot", "file_id": "...", "image_url": "..."},
    sonic.Unmarshal succeeds (missing/unknown fields simply leave struct fields at their zero values) because the unmarshaling does not fail.
  • As a result, the image-generation branch always "wins" for any object-shaped payload, and the code never reaches the ResponsesComputerToolCallOutputData branch. Existing computer tool outputs will be parsed as image-generation outputs with an empty Result.

Fix this by only treating the payload as an image-generation output when it actually contains a result field, and otherwise fall through to the computer-tool path:

 func (output *ResponsesToolMessageOutputStruct) UnmarshalJSON(data []byte) error {
   var str string
   if err := sonic.Unmarshal(data, &str); err == nil {
     output.ResponsesToolCallOutputStr = &str
     return nil
   }
   var array []ResponsesMessageContentBlock
   if err := sonic.Unmarshal(data, &array); err == nil {
     output.ResponsesFunctionToolCallOutputBlocks = array
     return nil
   }
-  var imageGenerationCallOutput ResponsesImageGenerationCallOutput
-  if err := sonic.Unmarshal(data, &imageGenerationCallOutput); err == nil {
-    output.ResponsesImageGenerationCallOutput = &imageGenerationCallOutput
-    return nil
-  }
-  var computerToolCallOutput ResponsesComputerToolCallOutputData
-  if err := sonic.Unmarshal(data, &computerToolCallOutput); err == nil {
-    output.ResponsesComputerToolCallOutput = &computerToolCallOutput
-    return nil
-  }
+  // Peek at the object to distinguish image-generation vs computer tool outputs.
+  var raw map[string]any
+  if err := sonic.Unmarshal(data, &raw); err == nil {
+    if _, hasResult := raw["result"]; hasResult {
+      var imageGenerationCallOutput ResponsesImageGenerationCallOutput
+      if err := sonic.Unmarshal(data, &imageGenerationCallOutput); err == nil {
+        output.ResponsesImageGenerationCallOutput = &imageGenerationCallOutput
+        return nil
+      }
+    }
+  }
+
+  var computerToolCallOutput ResponsesComputerToolCallOutputData
+  if err := sonic.Unmarshal(data, &computerToolCallOutput); err == nil {
+    output.ResponsesComputerToolCallOutput = &computerToolCallOutput
+    return nil
+  }
   return fmt.Errorf("responses tool message output struct is neither a string nor an array of responses message content blocks nor a computer tool call output data nor an image generation call output")
 }

This preserves the intended priority for image-generation outputs while ensuring computer tool outputs continue to be parsed correctly.

core/bifrost.go (1)

1806-1863: Missing ImageGenerationRequest handling in prepareFallbackRequest.

The prepareFallbackRequest method handles fallbacks for all request types except ImageGenerationRequest. This will cause fallbacks to silently fail for image generation requests.

 	if req.TranscriptionRequest != nil {
 		tmp := *req.TranscriptionRequest
 		tmp.Provider = fallback.Provider
 		tmp.Model = fallback.Model
 		fallbackReq.TranscriptionRequest = &tmp
 	}
+
+	if req.ImageGenerationRequest != nil {
+		tmp := *req.ImageGenerationRequest
+		tmp.Provider = fallback.Provider
+		tmp.Model = fallback.Model
+		fallbackReq.ImageGenerationRequest = &tmp
+	}

 	return &fallbackReq
 }
🧹 Nitpick comments (27)
core/schemas/provider.go (1)

234-239: ImageGeneration methods correctly extend the Provider interface; consider aligning gating config later

The new ImageGeneration / ImageGenerationStream signatures are consistent with existing operations (e.g., Chat/Speech) and look good. As a follow-up, you may want to extend AllowedRequests / CustomProviderConfig.IsOperationAllowed and any request-type-based routing so image-generation and its stream can be selectively enabled/disabled like the other operations.

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

802-810: Use a stream-specific request type for ImageGenerationStream unsupported errors

The unsupported stubs are fine, but ImageGenerationStream currently reports schemas.ImageGenerationRequest as the request type in NewUnsupportedOperationError. For clearer observability and any future per-operation gating, consider using a dedicated ImageGenerationStreamRequest constant here, mirroring how TextCompletionStream / ChatCompletionStream use their own request types.

plugins/semanticcache/main.go (1)

14-14: getImageCacheKey implementation is fine; consider expanding inputs if you need stricter separation

getImageCacheKey’s use of xxhash over prompt + main image params is sound, and returning "" when req/Input is nil gives a clear “no cache key” signal to call-sites.

If you eventually need stricter cache separation (e.g., by provider/model or provider-specific ExtraParams), you may want to fold those into the hash input or compose this helper with the existing CacheByModel / CacheByProvider behavior at call-sites.

Also applies to: 748-781

core/internal/testutil/image_generation_stream.go (1)

15-136: Streaming test harness looks solid; double-check event type strings and consider a buffered done

The concurrency pattern (wg + mutex + done signaling) is reasonable, and the test does a good job asserting partial vs completion behavior. Two minor points:

  • The checks against imgResp.Type ("image_generation.completed" and strings.Contains(..., "partial_image")) are tightly coupled to whatever values BifrostImageGenerationStreamResponse.Type actually uses. It’s worth confirming those are in sync (or, if you later introduce typed constants for image stream events, using them here to avoid drift).

  • If you want to avoid any chance of the collector goroutine blocking when the test times out or aborts early, you could make done a buffered channel of size 1.

core/providers/openrouter/openrouter.go (1)

271-279: Unsupported image-generation stubs align with existing pattern; consider using a stream request type

The new ImageGeneration / ImageGenerationStream methods correctly advertise lack of support via NewUnsupportedOperationError and match the style of other unsupported operations. For ImageGenerationStream, you may want to use a dedicated stream RequestType to keep metrics/logging distinct from non-stream image generation, similar to other streamed operations.

core/image_generation_tools_test.go (1)

1-512: Comprehensive image-generation tool tests; consider guarding against nil Error in assertions

This suite does a nice job covering happy paths, argument validation (missing/empty prompt, invalid JSON, missing required fields), and the new ResponsesImageGeneration* output serialization behavior, including priority semantics.

One small robustness tweak: in the tests that expect failures you directly access bifrostErr.Error.Message after assert.NotNil(t, bifrostErr). If future changes ever construct a BifrostError with a nil Error field, those tests will panic instead of failing cleanly. Adding a require.NotNil(t, bifrostErr.Error) before inspecting .Message in those cases would make the tests more defensive without changing intent.

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

894-902: Vertex image-generation stubs are consistent; stream method could use a dedicated request type

The new ImageGeneration / ImageGenerationStream methods correctly signal that Vertex doesn’t support image generation yet and match existing unsupported-operation patterns. For ImageGenerationStream, consider swapping schemas.ImageGenerationRequest for a stream-specific RequestType so observability and any future per-operation controls distinguish stream vs non-stream image generation.

core/schemas/responses.go (1)

1367-1407: Image-generation streaming response types fit cleanly into the existing enum

The added ResponsesStreamResponseTypeImageGenerationCall* constants follow the existing naming scheme for streamed events and give you a clear, typed surface for image-generation-related responses. They look good as-is; when convenient, it would be worth using these constants (rather than raw strings) wherever you check image-generation stream event types to avoid drift.

core/internal/testutil/image_generation_fallback.go (1)

50-60: Consider stricter error handling for fallback validation.

The current error handling logs a warning but doesn't fail the test when fallbacks are configured but the request still fails. This could mask real fallback mechanism bugs. Consider adding an assertion that when fallbacks are configured and valid, the request should eventually succeed.

 		if bifrostErr != nil {
 			// Check if error indicates fallback was attempted
 			errorMsg := GetErrorMessage(bifrostErr)
 			if strings.Contains(strings.ToLower(errorMsg), "fallback") {
 				t.Logf("✅ Fallback mechanism triggered (expected behavior)")
 			} else {
-				// If we have fallbacks configured, the request should succeed via fallback
-				// If it still fails, log it but don't fail the test (provider-specific)
-				t.Logf("⚠️  Request failed even with fallbacks: %v", errorMsg)
+				// If fallbacks are configured and valid, the request should succeed
+				// This might indicate a fallback mechanism issue
+				t.Errorf("⚠️  Request failed even with fallbacks configured: %v", errorMsg)
 			}
 			return
 		}
ui/components/chat/ImageMessage.tsx (1)

23-24: Consider using the image's index property as the key.

Using array index (idx) as the React key is an anti-pattern when list items can be reordered. The images array items already have an index property that could provide a more stable key.

-      {images.map((img, idx) => (
-        <Card key={idx} className="overflow-hidden">
+      {images.map((img) => (
+        <Card key={img.index} className="overflow-hidden">
transports/bifrost-http/handlers/inference.go (2)

1191-1197: Move defer cancel() after the streaming check for consistency.

Other handlers (e.g., textCompletion, chatCompletion, speech) place defer cancel() after the streaming check, not before. While calling cancel twice is safe, this placement is inconsistent with the established pattern and could cause confusion.

 	// Convert context
 	bifrostCtx, cancel := lib.ConvertToBifrostContext(ctx, h.handlerStore.ShouldAllowDirectKeys())
 	if bifrostCtx == nil {
 		SendError(ctx, fasthttp.StatusInternalServerError, "Failed to convert context")
 		return
 	}
-	defer cancel()
 
 	// Handle streaming image generation
 	if req.BifrostParams.Stream != nil && *req.BifrostParams.Stream {
 		h.handleStreamingImageGeneration(ctx, bifrostReq, bifrostCtx, cancel)
 		return
 	}
+
+	defer cancel() // Ensure cleanup on function exit
 
 	// Execute request
 	resp, bifrostErr := h.client.ImageGenerationRequest(*bifrostCtx, bifrostReq)

1165-1165: Fix typo: "witout" → "without".

-		// Continue witout extra params
+		// Continue without extra params
core/internal/testutil/image_generation_cache.go (2)

28-38: Consider including fallbacks for comprehensive cache testing.

The request omits testConfig.ImageGenerationFallbacks, which is available in the config (per account.go lines 65-66). While this may be intentional to test cache behavior without fallback complexity, including fallbacks would ensure cache key generation accounts for them properly.

 request := &schemas.BifrostImageGenerationRequest{
 	Provider: testConfig.Provider,
 	Model:    testConfig.ImageGenerationModel,
 	Input: &schemas.ImageGenerationInput{
 		Prompt: cacheTestPrompt,
 	},
 	Params: &schemas.ImageGenerationParameters{
 		Size:           bifrost.Ptr("1024x1024"),
 		ResponseFormat: bifrost.Ptr("b64_json"),
 	},
+	Fallbacks: testConfig.ImageGenerationFallbacks,
 }

94-104: Single-image comparison may miss edge cases.

The validation only compares the first image's B64JSON. If n > 1 is ever used, consider iterating through all images to ensure complete cache integrity.

framework/streaming/types.go (1)

258-269: Consider using ImageGenerationRequest for consistency.

Other stream type handlers in ToBifrostResponse use the non-stream request type for the final accumulated response (e.g., StreamTypeAudio uses schemas.SpeechRequest, StreamTypeTranscription uses schemas.TranscriptionRequest). For consistency, this should likely use schemas.ImageGenerationRequest instead of schemas.ImageGenerationStreamRequest.

 	case StreamTypeImage:
 		imageResp := p.Data.ImageGenerationOutput
 		if imageResp == nil {
 			imageResp = &schemas.BifrostImageGenerationResponse{}
 		}
 		resp.ImageGenerationResponse = imageResp
 		resp.ImageGenerationResponse.ExtraFields = schemas.BifrostResponseExtraFields{
-			RequestType:    schemas.ImageGenerationStreamRequest,
+			RequestType:    schemas.ImageGenerationRequest,
 			Provider:       p.Provider,
 			ModelRequested: p.Model,
 			Latency:        p.Data.Latency,
 		}
core/providers/openai/openai_image_test.go (2)

13-14: Consider renaming test function for clarity.

The function name TestImageGenerationStreamingRequestConversion is misleading since it tests general request conversion (via ToOpenAIImageGenerationRequest), not streaming-specific behavior.

-// TestImageGenerationStreamingRequestConversion
-func TestImageGenerationStreamingRequestConversion(t *testing.T) {
+// TestImageGenerationRequestConversion tests Bifrost to OpenAI request conversion
+func TestImageGenerationRequestConversion(t *testing.T) {

245-252: Handle potential unmarshal errors in test verification.

The json.Unmarshal error is silently ignored. If unmarshaling fails, the subsequent tt.verify call would operate on an empty map, potentially hiding issues.

 		t.Run(tt.name, func(t *testing.T) {
 			t.Parallel()
 			jsonBytes, _ := sonic.Marshal(tt.chunk)
 			var data map[string]interface{}
-			json.Unmarshal(jsonBytes, &data)
+			if err := json.Unmarshal(jsonBytes, &data); err != nil {
+				t.Fatalf("Failed to unmarshal: %v", err)
+			}
 			tt.verify(t, data)
 		})
plugins/semanticcache/utils.go (2)

88-88: Fix typo in comment.

The comment has a typo: "styele" should be "style".

-	// Special handling for image generation (hash = prompt + size + quality + styele + n)
+	// Special handling for image generation (hash = prompt + size + quality + style + n)

516-517: Consider adding nil check for defensive coding.

The code accesses req.ImageGenerationRequest.Input without verifying that req.ImageGenerationRequest is not nil. While the request type check suggests it should be populated, a defensive nil check would prevent potential panics if the request is malformed.

 	case schemas.ImageGenerationRequest, schemas.ImageGenerationStreamRequest:
+		if req.ImageGenerationRequest == nil {
+			return nil
+		}
 		return req.ImageGenerationRequest.Input
framework/streaming/images.go (2)

137-163: Consolidate repeated last chunk access.

The code accesses acc.ImageStreamChunks[len(acc.ImageStreamChunks)-1] three times (lines 138, 150, 158). Consider extracting this to a single variable for clarity and efficiency.

 	// Update token usage from final chunk if available
 	if len(acc.ImageStreamChunks) > 0 {
 		lastChunk := acc.ImageStreamChunks[len(acc.ImageStreamChunks)-1]
 		if lastChunk.Delta != nil && lastChunk.Delta.Usage != nil {
 			data.TokenUsage = &schemas.BifrostLLMUsage{
 				PromptTokens:     lastChunk.Delta.Usage.PromptTokens,
 				CompletionTokens: 0, // Image generation doesn't have completion tokens
 				TotalTokens:      lastChunk.Delta.Usage.TotalTokens,
 			}
 		}
-	}
-
-	// Update cost from final chunk if available
-	if len(acc.ImageStreamChunks) > 0 {
-		lastChunk := acc.ImageStreamChunks[len(acc.ImageStreamChunks)-1]
 		if lastChunk.Cost != nil {
 			data.Cost = lastChunk.Cost
 		}
-	}
-
-	// Update semantic cache debug from final chunk if available
-	if len(acc.ImageStreamChunks) > 0 {
-		lastChunk := acc.ImageStreamChunks[len(acc.ImageStreamChunks)-1]
 		if lastChunk.SemanticCacheDebug != nil {
 			data.CacheDebug = lastChunk.SemanticCacheDebug
 		}
 		data.FinishReason = lastChunk.FinishReason
 	}

202-206: Remove or implement empty conditional block.

This block contains only a comment and no actual code. Either remove it entirely or implement the intended usage extraction logic.

-		// Extract usage if available
-		if result.ImageGenerationStreamResponse.Usage != nil {
-			// Note: ImageUsage doesn't directly map to BifrostLLMUsage, but we can store it
-			// The actual usage will be extracted in processAccumulatedImageStreamingChunks
-		}
ui/hooks/useImageStream.ts (3)

107-179: Stale closure risk with options in processChunk dependency.

The options object in the dependency array can cause issues if the consumer passes a new object reference on each render (common with inline callbacks). Consider using useRef to hold callbacks or using useCallback for the options callbacks at the call site.

+  const optionsRef = useRef(options);
+  optionsRef.current = options;
+
   // Process incoming chunk
   const processChunk = useCallback(
     (chunk: ImageStreamChunk) => {
       // ... existing logic ...
       if (type === "error" || error) {
         const errorMsg = error?.message || "Unknown streaming error";
         setState((prev) => ({ ...prev, error: errorMsg, isStreaming: false }));
-        options.onError?.(errorMsg);
+        optionsRef.current.onError?.(errorMsg);
         return;
       }
       // ... rest of function, similar changes for onComplete ...
     },
-    [buildImageFromChunks, options]
+    [buildImageFromChunks]
   );

138-141: Progress estimation is coarse; consider documenting the assumption.

The hardcoded assumption of ~10 chunks per image may not reflect actual streaming behavior. Consider making this configurable or adding a comment explaining the estimate.


245-250: Consider logging parse errors in development only.

The console.error for parse failures could be noisy in production. Consider gating this log or using a more structured error handling approach.

               try {
                 const chunk: ImageStreamChunk = JSON.parse(data);
                 processChunk(chunk);
               } catch (parseError) {
-                console.error("Failed to parse SSE chunk:", parseError);
+                if (process.env.NODE_ENV === "development") {
+                  console.error("Failed to parse SSE chunk:", parseError);
+                }
               }
core/bifrost.go (1)

1192-1238: Consider extracting shared parameter mapping logic.

The parameter extraction code for size, quality, style, response_format, and n is nearly identical between ExecuteImageGenerationTool and ExecuteResponsesImageGenerationTool. Consider extracting to a helper function.

// Helper to extract image generation parameters from tool call arguments
func extractImageGenerationParams(args map[string]interface{}) *schemas.ImageGenerationParameters {
    var params *schemas.ImageGenerationParameters
    
    if size, ok := args["size"].(string); ok && size != "" {
        if params == nil { params = &schemas.ImageGenerationParameters{} }
        params.Size = &size
    }
    if quality, ok := args["quality"].(string); ok && quality != "" {
        if params == nil { params = &schemas.ImageGenerationParameters{} }
        params.Quality = &quality
    }
    // ... similar for style, response_format, n
    return params
}

Also applies to: 1342-1388

core/providers/openai/images.go (2)

365-366: ChunkIndex initialized to -1 is unusual but intentional.

The chunkIndex starts at -1 and is incremented to 0 before first use at line 456. This works correctly but is non-obvious.

Consider initializing to 0 and restructuring the increment logic for clarity:

-		chunkIndex := -1
+		chunkIndex := 0
 		// ... later in the loop ...
-						chunkIndex++
-						chunk := b64Data[offset:end]
-						bifrostChunk.PartialB64 = chunk
-						bifrostChunk.ChunkIndex = chunkIndex
+						chunk := b64Data[offset:end]
+						bifrostChunk.PartialB64 = chunk
+						bifrostChunk.ChunkIndex = chunkIndex
+						chunkIndex++

422-427: TODO comment indicates incomplete usage tracking.

The comment suggests usage tracking is not fully implemented for streaming responses.

Would you like help implementing proper usage tracking for streaming image generation?

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 49c4beb and d50deda.

📒 Files selected for processing (48)
  • core/bifrost.go (6 hunks)
  • core/image_generation_tools_test.go (1 hunks)
  • core/images_test.go (1 hunks)
  • core/internal/testutil/account.go (2 hunks)
  • core/internal/testutil/image_generation.go (1 hunks)
  • core/internal/testutil/image_generation_cache.go (1 hunks)
  • core/internal/testutil/image_generation_errors.go (1 hunks)
  • core/internal/testutil/image_generation_fallback.go (1 hunks)
  • core/internal/testutil/image_generation_load.go (1 hunks)
  • core/internal/testutil/image_generation_stream.go (1 hunks)
  • core/internal/testutil/tests.go (1 hunks)
  • core/providers/anthropic/anthropic.go (4 hunks)
  • core/providers/azure/azure.go (1 hunks)
  • core/providers/azure/images.go (1 hunks)
  • core/providers/bedrock/bedrock.go (6 hunks)
  • core/providers/cerebras/cerebras.go (1 hunks)
  • core/providers/cohere/cohere.go (3 hunks)
  • core/providers/elevenlabs/elevenlabs.go (3 hunks)
  • core/providers/gemini/gemini.go (4 hunks)
  • core/providers/groq/groq.go (1 hunks)
  • core/providers/mistral/mistral.go (1 hunks)
  • core/providers/ollama/ollama.go (1 hunks)
  • core/providers/openai/images.go (1 hunks)
  • core/providers/openai/openai.go (8 hunks)
  • core/providers/openai/openai_image_test.go (1 hunks)
  • core/providers/openrouter/openrouter.go (1 hunks)
  • core/providers/parasail/parasail.go (1 hunks)
  • core/providers/perplexity/perplexity.go (1 hunks)
  • core/providers/sgl/sgl.go (1 hunks)
  • core/providers/utils/utils.go (2 hunks)
  • core/providers/vertex/vertex.go (1 hunks)
  • core/schemas/bifrost.go (10 hunks)
  • core/schemas/chatcompletions.go (1 hunks)
  • core/schemas/images.go (1 hunks)
  • core/schemas/mux.go (1 hunks)
  • core/schemas/provider.go (1 hunks)
  • core/schemas/responses.go (3 hunks)
  • core/utils.go (1 hunks)
  • framework/streaming/accumulator.go (10 hunks)
  • framework/streaming/images.go (1 hunks)
  • framework/streaming/types.go (5 hunks)
  • plugins/semanticcache/main.go (4 hunks)
  • plugins/semanticcache/search.go (2 hunks)
  • plugins/semanticcache/stream.go (1 hunks)
  • plugins/semanticcache/utils.go (7 hunks)
  • transports/bifrost-http/handlers/inference.go (6 hunks)
  • ui/components/chat/ImageMessage.tsx (1 hunks)
  • ui/hooks/useImageStream.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**

⚙️ CodeRabbit configuration file

always check the stack if there is one for the current PR. do not give localized reviews for the PR, always see all changes in the light of the whole stack of PRs (if there is a stack, if there is no stack you can continue to make localized suggestions/reviews)

Files:

  • plugins/semanticcache/stream.go
  • core/schemas/provider.go
  • core/internal/testutil/account.go
  • core/providers/groq/groq.go
  • core/internal/testutil/image_generation_errors.go
  • core/providers/ollama/ollama.go
  • core/providers/azure/azure.go
  • core/providers/cerebras/cerebras.go
  • core/schemas/images.go
  • ui/components/chat/ImageMessage.tsx
  • core/providers/mistral/mistral.go
  • core/schemas/chatcompletions.go
  • core/providers/sgl/sgl.go
  • core/schemas/responses.go
  • core/providers/cohere/cohere.go
  • core/providers/anthropic/anthropic.go
  • core/providers/openai/openai_image_test.go
  • core/providers/utils/utils.go
  • core/providers/perplexity/perplexity.go
  • core/internal/testutil/image_generation_cache.go
  • core/internal/testutil/image_generation_fallback.go
  • ui/hooks/useImageStream.ts
  • framework/streaming/images.go
  • core/image_generation_tools_test.go
  • framework/streaming/accumulator.go
  • core/providers/openai/openai.go
  • core/images_test.go
  • core/internal/testutil/image_generation.go
  • transports/bifrost-http/handlers/inference.go
  • core/internal/testutil/image_generation_load.go
  • plugins/semanticcache/search.go
  • core/providers/vertex/vertex.go
  • core/providers/openrouter/openrouter.go
  • core/providers/bedrock/bedrock.go
  • core/providers/gemini/gemini.go
  • core/schemas/mux.go
  • plugins/semanticcache/utils.go
  • core/internal/testutil/tests.go
  • core/schemas/bifrost.go
  • core/internal/testutil/image_generation_stream.go
  • core/utils.go
  • plugins/semanticcache/main.go
  • core/bifrost.go
  • core/providers/azure/images.go
  • core/providers/elevenlabs/elevenlabs.go
  • core/providers/parasail/parasail.go
  • core/providers/openai/images.go
  • framework/streaming/types.go
🧬 Code graph analysis (34)
core/schemas/provider.go (3)
core/schemas/account.go (1)
  • Key (8-17)
core/schemas/images.go (2)
  • BifrostImageGenerationRequest (4-11)
  • BifrostImageGenerationResponse (33-40)
core/schemas/bifrost.go (2)
  • BifrostError (378-387)
  • BifrostStream (340-348)
core/internal/testutil/account.go (1)
core/schemas/bifrost.go (1)
  • Fallback (132-135)
core/providers/groq/groq.go (4)
core/schemas/images.go (2)
  • BifrostImageGenerationRequest (4-11)
  • BifrostImageGenerationResponse (33-40)
core/providers/utils/utils.go (1)
  • NewUnsupportedOperationError (391-402)
core/schemas/bifrost.go (2)
  • ImageGenerationRequest (100-100)
  • BifrostStream (340-348)
core/schemas/provider.go (1)
  • PostHookRunner (204-204)
core/internal/testutil/image_generation_errors.go (2)
core/internal/testutil/account.go (1)
  • ComprehensiveTestConfig (49-68)
core/schemas/images.go (3)
  • BifrostImageGenerationRequest (4-11)
  • ImageGenerationInput (18-20)
  • ImageGenerationParameters (22-30)
core/schemas/images.go (2)
core/schemas/provider.go (1)
  • Provider (207-240)
core/schemas/bifrost.go (4)
  • ModelProvider (32-32)
  • Fallback (132-135)
  • BifrostResponseExtraFields (306-315)
  • BifrostError (378-387)
ui/components/chat/ImageMessage.tsx (2)
ui/components/ui/card.tsx (1)
  • Card (50-50)
ui/components/ui/skeleton.tsx (1)
  • Skeleton (7-7)
core/providers/mistral/mistral.go (3)
core/schemas/images.go (2)
  • BifrostImageGenerationRequest (4-11)
  • BifrostImageGenerationResponse (33-40)
core/schemas/bifrost.go (2)
  • BifrostError (378-387)
  • ImageGenerationRequest (100-100)
core/providers/utils/utils.go (1)
  • NewUnsupportedOperationError (391-402)
core/schemas/chatcompletions.go (2)
core/providers/gemini/types.go (1)
  • Type (778-778)
ui/lib/types/logs.ts (1)
  • Function (141-146)
core/providers/sgl/sgl.go (3)
core/schemas/images.go (2)
  • BifrostImageGenerationRequest (4-11)
  • BifrostImageGenerationResponse (33-40)
core/providers/utils/utils.go (1)
  • NewUnsupportedOperationError (391-402)
core/schemas/bifrost.go (2)
  • ImageGenerationRequest (100-100)
  • BifrostStream (340-348)
core/providers/cohere/cohere.go (4)
core/providers/utils/utils.go (3)
  • ProcessAndSendResponse (535-565)
  • GetBifrostResponseForStreamResponse (787-819)
  • NewUnsupportedOperationError (391-402)
core/schemas/images.go (2)
  • BifrostImageGenerationRequest (4-11)
  • BifrostImageGenerationResponse (33-40)
core/schemas/bifrost.go (3)
  • BifrostError (378-387)
  • ImageGenerationRequest (100-100)
  • BifrostStream (340-348)
core/schemas/provider.go (1)
  • PostHookRunner (204-204)
core/providers/anthropic/anthropic.go (3)
core/providers/utils/utils.go (3)
  • ProcessAndSendResponse (535-565)
  • GetBifrostResponseForStreamResponse (787-819)
  • NewUnsupportedOperationError (391-402)
core/schemas/bifrost.go (2)
  • BifrostError (378-387)
  • BifrostStream (340-348)
core/schemas/provider.go (1)
  • PostHookRunner (204-204)
core/providers/openai/openai_image_test.go (2)
core/schemas/images.go (4)
  • BifrostImageGenerationRequest (4-11)
  • ImageGenerationInput (18-20)
  • ImageGenerationParameters (22-30)
  • BifrostImageGenerationResponse (33-40)
core/providers/openai/images.go (4)
  • OpenAIImageGenerationRequest (27-37)
  • ToOpenAIImageGenerationRequest (102-114)
  • OpenAIImageGenerationResponse (40-52)
  • OpenAIImageGenerationUsage (54-63)
core/providers/utils/utils.go (1)
core/schemas/images.go (1)
  • BifrostImageGenerationStreamResponse (55-65)
core/internal/testutil/image_generation_cache.go (3)
core/internal/testutil/account.go (1)
  • ComprehensiveTestConfig (49-68)
core/schemas/images.go (3)
  • BifrostImageGenerationRequest (4-11)
  • ImageGenerationInput (18-20)
  • ImageGenerationParameters (22-30)
ui/lib/types/logs.ts (1)
  • CacheDebug (202-211)
core/internal/testutil/image_generation_fallback.go (3)
core/internal/testutil/account.go (1)
  • ComprehensiveTestConfig (49-68)
core/schemas/images.go (3)
  • BifrostImageGenerationRequest (4-11)
  • ImageGenerationInput (18-20)
  • ImageGenerationParameters (22-30)
core/schemas/bifrost.go (1)
  • ImageGenerationRequest (100-100)
ui/hooks/useImageStream.ts (2)
ui/lib/store/apis/baseApi.ts (1)
  • getTokenFromStorage (10-25)
ui/lib/utils/port.ts (1)
  • getEndpointUrl (117-127)
framework/streaming/accumulator.go (2)
framework/streaming/types.go (4)
  • ImageStreamChunk (99-109)
  • StreamAccumulator (112-124)
  • ChatStreamChunk (75-84)
  • ResponsesStreamChunk (87-96)
core/schemas/bifrost.go (1)
  • ImageGenerationStreamRequest (101-101)
core/providers/openai/openai.go (1)
core/providers/utils/utils.go (2)
  • ProcessAndSendResponse (535-565)
  • GetBifrostResponseForStreamResponse (787-819)
core/images_test.go (2)
core/schemas/images.go (7)
  • BifrostImageGenerationRequest (4-11)
  • ImageGenerationInput (18-20)
  • ImageGenerationParameters (22-30)
  • BifrostImageGenerationResponse (33-40)
  • ImageData (42-47)
  • ImageUsage (49-52)
  • BifrostImageGenerationStreamResponse (55-65)
core/schemas/bifrost.go (1)
  • OpenAI (35-35)
core/internal/testutil/image_generation.go (3)
core/internal/testutil/account.go (1)
  • ComprehensiveTestConfig (49-68)
core/schemas/images.go (3)
  • BifrostImageGenerationRequest (4-11)
  • ImageGenerationInput (18-20)
  • ImageGenerationParameters (22-30)
core/schemas/bifrost.go (1)
  • ImageGenerationRequest (100-100)
transports/bifrost-http/handlers/inference.go (4)
core/schemas/images.go (3)
  • ImageGenerationInput (18-20)
  • ImageGenerationParameters (22-30)
  • BifrostImageGenerationRequest (4-11)
core/schemas/utils.go (1)
  • ParseModelString (23-36)
core/schemas/bifrost.go (4)
  • ModelProvider (32-32)
  • ImageGenerationRequest (100-100)
  • BifrostStream (340-348)
  • BifrostError (378-387)
transports/bifrost-http/lib/ctx.go (1)
  • ConvertToBifrostContext (72-283)
core/providers/vertex/vertex.go (4)
core/schemas/images.go (2)
  • BifrostImageGenerationRequest (4-11)
  • BifrostImageGenerationResponse (33-40)
core/schemas/bifrost.go (2)
  • BifrostError (378-387)
  • ImageGenerationRequest (100-100)
core/providers/utils/utils.go (1)
  • NewUnsupportedOperationError (391-402)
core/schemas/provider.go (1)
  • PostHookRunner (204-204)
core/providers/openrouter/openrouter.go (4)
core/schemas/images.go (2)
  • BifrostImageGenerationRequest (4-11)
  • BifrostImageGenerationResponse (33-40)
core/providers/utils/utils.go (1)
  • NewUnsupportedOperationError (391-402)
core/schemas/bifrost.go (2)
  • ImageGenerationRequest (100-100)
  • BifrostStream (340-348)
core/schemas/provider.go (1)
  • PostHookRunner (204-204)
core/providers/bedrock/bedrock.go (3)
core/providers/utils/utils.go (3)
  • ProcessAndSendResponse (535-565)
  • GetBifrostResponseForStreamResponse (787-819)
  • NewUnsupportedOperationError (391-402)
core/schemas/bifrost.go (2)
  • BifrostError (378-387)
  • BifrostStream (340-348)
core/schemas/provider.go (1)
  • PostHookRunner (204-204)
core/providers/gemini/gemini.go (4)
core/providers/utils/utils.go (3)
  • ProcessAndSendResponse (535-565)
  • GetBifrostResponseForStreamResponse (787-819)
  • NewUnsupportedOperationError (391-402)
core/schemas/images.go (2)
  • BifrostImageGenerationRequest (4-11)
  • BifrostImageGenerationResponse (33-40)
core/schemas/bifrost.go (3)
  • BifrostError (378-387)
  • ImageGenerationRequest (100-100)
  • BifrostStream (340-348)
core/schemas/provider.go (1)
  • PostHookRunner (204-204)
core/schemas/mux.go (2)
core/schemas/chatcompletions.go (1)
  • ChatToolTypeImageGeneration (199-199)
core/schemas/responses.go (1)
  • ResponsesToolImageGeneration (1318-1329)
plugins/semanticcache/utils.go (2)
core/schemas/bifrost.go (3)
  • RequestType (83-83)
  • ImageGenerationRequest (100-100)
  • ImageGenerationStreamRequest (101-101)
core/schemas/images.go (2)
  • ImageGenerationInput (18-20)
  • ImageGenerationParameters (22-30)
core/internal/testutil/tests.go (6)
core/internal/testutil/image_generation.go (1)
  • RunImageGenerationTest (13-86)
core/internal/testutil/image_generation_stream.go (1)
  • RunImageGenerationStreamTest (16-137)
core/internal/testutil/image_generation_fallback.go (1)
  • RunImageGenerationFallbackTest (14-80)
core/internal/testutil/image_generation_cache.go (1)
  • RunImageGenerationCacheTest (14-140)
core/internal/testutil/image_generation_errors.go (1)
  • RunImageGenerationErrorTest (14-154)
core/internal/testutil/image_generation_load.go (3)
  • RunImageGenerationLoadTest (16-101)
  • RunImageGenerationStreamLoadTest (104-211)
  • RunImageGenerationCacheLoadTest (214-307)
core/internal/testutil/image_generation_stream.go (2)
core/schemas/images.go (4)
  • BifrostImageGenerationRequest (4-11)
  • ImageGenerationInput (18-20)
  • ImageGenerationParameters (22-30)
  • BifrostImageGenerationStreamResponse (55-65)
core/schemas/bifrost.go (2)
  • ImageGenerationStreamRequest (101-101)
  • BifrostStream (340-348)
core/utils.go (1)
core/schemas/bifrost.go (6)
  • TextCompletionStreamRequest (90-90)
  • ChatCompletionStreamRequest (92-92)
  • ResponsesStreamRequest (94-94)
  • SpeechStreamRequest (97-97)
  • TranscriptionStreamRequest (99-99)
  • ImageGenerationStreamRequest (101-101)
core/bifrost.go (6)
core/schemas/bifrost.go (6)
  • BifrostError (378-387)
  • OpenAI (35-35)
  • ModelProvider (32-32)
  • ImageGenerationRequest (100-100)
  • RequestType (83-83)
  • BifrostStream (340-348)
core/schemas/utils.go (2)
  • ParseModelString (23-36)
  • Ptr (16-18)
core/schemas/images.go (4)
  • BifrostImageGenerationRequest (4-11)
  • ImageGenerationInput (18-20)
  • ImageGenerationParameters (22-30)
  • BifrostImageGenerationResponse (33-40)
core/schemas/provider.go (1)
  • Provider (207-240)
core/utils.go (1)
  • Ptr (51-53)
core/schemas/responses.go (3)
  • ResponsesMessage (310-322)
  • ResponsesImageGenerationCall (762-764)
  • ResponsesMessageTypeImageGenerationCall (299-299)
core/providers/parasail/parasail.go (5)
core/schemas/account.go (1)
  • Key (8-17)
core/schemas/images.go (2)
  • BifrostImageGenerationRequest (4-11)
  • BifrostImageGenerationResponse (33-40)
core/providers/utils/utils.go (1)
  • NewUnsupportedOperationError (391-402)
core/schemas/bifrost.go (2)
  • ImageGenerationRequest (100-100)
  • BifrostStream (340-348)
core/schemas/provider.go (1)
  • PostHookRunner (204-204)
core/providers/openai/images.go (4)
core/providers/openai/openai.go (1)
  • ParseOpenAIError (2080-2108)
core/schemas/images.go (4)
  • ImageGenerationParameters (22-30)
  • ImageData (42-47)
  • ImageUsage (49-52)
  • BifrostImageGenerationStreamResponse (55-65)
core/schemas/bifrost.go (8)
  • BifrostError (378-387)
  • OpenAI (35-35)
  • RequestType (83-83)
  • BifrostResponseExtraFields (306-315)
  • BifrostStream (340-348)
  • ModelProvider (32-32)
  • ErrorField (396-403)
  • BifrostErrorExtraFields (445-449)
core/providers/utils/utils.go (7)
  • CheckOperationAllowed (408-420)
  • NewBifrostOperationError (449-460)
  • SetExtraHeaders (178-208)
  • ShouldSendBackRawResponse (482-487)
  • ProcessAndSendResponse (535-565)
  • GetBifrostResponseForStreamResponse (787-819)
  • ProcessAndSendError (607-653)
framework/streaming/types.go (2)
core/schemas/images.go (2)
  • BifrostImageGenerationResponse (33-40)
  • BifrostImageGenerationStreamResponse (55-65)
core/schemas/provider.go (1)
  • Provider (207-240)

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: 2

🧹 Nitpick comments (8)
core/schemas/responses.go (1)

526-582: New image-generation tool output variant is wired correctly

The added ResponsesImageGenerationCallOutput field and the updated marshal/unmarshal logic cleanly integrate an image-generation variant without breaking existing string/array/computer-output handling.

framework/streaming/types.go (1)

16-19: Align image stream RequestType with other streaming responses

ToBifrostResponse uses non-stream request types (TextCompletionRequest, ChatCompletionRequest, ResponsesRequest, SpeechRequest, TranscriptionRequest) for aggregated streaming results, but the new image branch sets RequestType to ImageGenerationStreamRequest. For consistency and simpler downstream handling (metrics, routing, caching), it should likely use ImageGenerationRequest instead.

-	case StreamTypeImage:
-		imageResp := p.Data.ImageGenerationOutput
-		if imageResp == nil {
-			imageResp = &schemas.BifrostImageGenerationResponse{}
-		}
-		resp.ImageGenerationResponse = imageResp
-		resp.ImageGenerationResponse.ExtraFields = schemas.BifrostResponseExtraFields{
-			RequestType:    schemas.ImageGenerationStreamRequest,
-			Provider:       p.Provider,
-			ModelRequested: p.Model,
-			Latency:        p.Data.Latency,
-		}
+	case StreamTypeImage:
+		imageResp := p.Data.ImageGenerationOutput
+		if imageResp == nil {
+			imageResp = &schemas.BifrostImageGenerationResponse{}
+		}
+		resp.ImageGenerationResponse = imageResp
+		resp.ImageGenerationResponse.ExtraFields = schemas.BifrostResponseExtraFields{
+			RequestType:    schemas.ImageGenerationRequest,
+			Provider:       p.Provider,
+			ModelRequested: p.Model,
+			Latency:        p.Data.Latency,
+		}

Also applies to: 28-48, 98-109, 119-119, 258-269

core/images_test.go (2)

14-92: Parallel subtests in for-range loops: OK on Go 1.22+, consider defensive rebind

TestImageGenerationRequestSerialization and TestImageGenerationRequestDeserialization use for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel(); ... tt ... }) }. On Go 1.22+ the loop variable is captured by value; on older Go versions this pattern is racy. If you still support pre‑1.22, add tc := tt inside the loop and use tc in the closure to avoid accidental test flakiness.

Also applies to: 95-161


198-255: Assert on json.Unmarshal result in TestImageStreamResponseSerialization

The test currently ignores the error from json.Unmarshal(jsonBytes, &data). If the JSON shape drifts, the test could panic later on type assertions rather than failing with a clear message. Consider checking the error and failing explicitly, similar to the other tests in this file.

-			jsonBytes, _ := sonic.Marshal(tt.chunk)
-			var data map[string]interface{}
-			json.Unmarshal(jsonBytes, &data)
+			jsonBytes, err := sonic.Marshal(tt.chunk)
+			if err != nil {
+				t.Fatalf("failed to marshal chunk: %v", err)
+			}
+			var data map[string]interface{}
+			if err := json.Unmarshal(jsonBytes, &data); err != nil {
+				t.Fatalf("failed to unmarshal JSON: %v", err)
+			}
framework/streaming/accumulator.go (1)

19-24: Image stream pooling and accumulator wiring look correct; consider using typed reset in Cleanup

The new imageStreamChunkPool, getImageStreamChunk/putImageStreamChunk, ImageStreamChunks slice, addImageStreamChunk, and prewarming in NewAccumulator mirror the existing patterns for other chunk types and should behave correctly for image streams.

One small consistency nit: in Cleanup you currently return image chunks with a.imageStreamChunkPool.Put(chunk) rather than a.putImageStreamChunk(chunk), so any fields on those structs aren’t cleared before reuse. This matches the existing pattern for other chunk types, but if you later rely on partially populated chunks or add more fields, using the typed put* helpers here (for image and possibly the other chunk kinds) would make the pooling behavior more robust.

Also applies to: 101-116, 118-130, 221-237, 259-261, 488-508

framework/streaming/images.go (1)

13-92: Image reconstruction logic is solid; optionally propagate usage into the final response

buildCompleteImageFromImageStreamChunks correctly:

  • sorts by ImageIndex then ChunkIndex,
  • accumulates PartialB64 per image,
  • preserves per-image RevisedPrompt,
  • and derives a stable ID and Model.

Right now the returned BifrostImageGenerationResponse leaves Usage nil, even though usage is later derived from the last chunk in processAccumulatedImageStreamingChunks. If you want the reconstructed response to be fully self-contained, consider also wiring that ImageUsage into finalResponse.Usage when it’s available from the chunks.

core/bifrost.go (2)

1130-1281: Tool → image request mapping is sound but duplicated; consider extracting a helper

ExecuteImageGenerationTool nicely:

  • validates non-empty arguments and prompt,
  • defaults to openai/dall-e-3 and optionally parses model via ParseModelString,
  • maps size, quality, style, response_format, and n into ImageGenerationParameters,
  • calls ImageGenerationRequest, and
  • returns a tool role ChatMessage containing marshaled imageResp.Data and ToolCallID.

The same arg‑to‑BifrostImageGenerationRequest mapping logic appears again in ExecuteResponsesImageGenerationTool. To keep both call paths in sync as you add parameters (e.g., user, extra_params), consider extracting a small helper like buildImageGenerationRequestFromArgs(args map[string]any) (*schemas.BifrostImageGenerationRequest, *schemas.BifrostError) and reusing it in both executors.


2955-3020: Public image-generation APIs follow existing patterns; just ensure validateRequest covers the new types

ImageGenerationRequest / ImageGenerationStreamRequest:

  • perform nil and prompt validation similar to other public methods,
  • populate a pooled BifrostRequest with RequestType and ImageGenerationRequest,
  • route through handleRequest / handleStreamRequest,
  • and in the non-streaming case, defensively guard against nil or missing ImageGenerationResponse.

This is consistent with the other entrypoints. Just make sure validateRequest (and helpers like IsStreamRequestType) have been updated elsewhere to treat ImageGenerationRequest and ImageGenerationStreamRequest the same way as the existing request types (provider/model required, etc.); otherwise, you may want to add provider/model checks here for symmetry.

Minor nit: the comment // ImageGenerationStreamRequest sends a image generation stream request should read “an image generation…”.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d50deda and b5899e6.

📒 Files selected for processing (28)
  • core/bifrost.go (7 hunks)
  • core/images_test.go (1 hunks)
  • core/internal/testutil/account.go (2 hunks)
  • core/providers/anthropic/anthropic.go (4 hunks)
  • core/providers/bedrock/bedrock.go (6 hunks)
  • core/providers/cerebras/cerebras.go (1 hunks)
  • core/providers/cohere/cohere.go (3 hunks)
  • core/providers/elevenlabs/elevenlabs.go (3 hunks)
  • core/providers/gemini/gemini.go (4 hunks)
  • core/providers/groq/groq.go (1 hunks)
  • core/providers/mistral/mistral.go (1 hunks)
  • core/providers/ollama/ollama.go (1 hunks)
  • core/providers/openai/openai.go (8 hunks)
  • core/providers/openrouter/openrouter.go (1 hunks)
  • core/providers/parasail/parasail.go (1 hunks)
  • core/providers/perplexity/perplexity.go (1 hunks)
  • core/providers/sgl/sgl.go (1 hunks)
  • core/providers/utils/utils.go (2 hunks)
  • core/providers/vertex/vertex.go (1 hunks)
  • core/schemas/mux.go (1 hunks)
  • core/schemas/provider.go (1 hunks)
  • core/schemas/responses.go (3 hunks)
  • core/utils.go (1 hunks)
  • framework/streaming/accumulator.go (10 hunks)
  • framework/streaming/images.go (1 hunks)
  • framework/streaming/types.go (5 hunks)
  • transports/bifrost-http/handlers/inference.go (6 hunks)
  • ui/components/chat/ImageMessage.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (12)
  • core/schemas/mux.go
  • core/utils.go
  • core/schemas/provider.go
  • core/providers/perplexity/perplexity.go
  • core/providers/utils/utils.go
  • core/providers/mistral/mistral.go
  • core/internal/testutil/account.go
  • ui/components/chat/ImageMessage.tsx
  • core/providers/openai/openai.go
  • core/providers/parasail/parasail.go
  • core/providers/openrouter/openrouter.go
  • core/providers/vertex/vertex.go
🧰 Additional context used
📓 Path-based instructions (1)
**

⚙️ CodeRabbit configuration file

always check the stack if there is one for the current PR. do not give localized reviews for the PR, always see all changes in the light of the whole stack of PRs (if there is a stack, if there is no stack you can continue to make localized suggestions/reviews)

Files:

  • core/providers/groq/groq.go
  • core/providers/sgl/sgl.go
  • transports/bifrost-http/handlers/inference.go
  • core/providers/elevenlabs/elevenlabs.go
  • core/images_test.go
  • core/providers/ollama/ollama.go
  • core/schemas/responses.go
  • framework/streaming/types.go
  • core/providers/gemini/gemini.go
  • core/providers/bedrock/bedrock.go
  • core/providers/cohere/cohere.go
  • core/providers/anthropic/anthropic.go
  • core/providers/cerebras/cerebras.go
  • framework/streaming/accumulator.go
  • framework/streaming/images.go
  • core/bifrost.go
🧬 Code graph analysis (10)
core/providers/groq/groq.go (4)
core/schemas/images.go (2)
  • BifrostImageGenerationRequest (4-11)
  • BifrostImageGenerationResponse (33-40)
core/providers/utils/utils.go (1)
  • NewUnsupportedOperationError (391-402)
core/schemas/bifrost.go (2)
  • ImageGenerationRequest (100-100)
  • ImageGenerationStreamRequest (101-101)
core/schemas/provider.go (1)
  • PostHookRunner (277-277)
core/providers/elevenlabs/elevenlabs.go (1)
core/providers/utils/utils.go (3)
  • ProcessAndSendResponse (535-565)
  • GetBifrostResponseForStreamResponse (777-809)
  • NewUnsupportedOperationError (391-402)
core/images_test.go (2)
core/schemas/images.go (7)
  • BifrostImageGenerationRequest (4-11)
  • ImageGenerationInput (18-20)
  • ImageGenerationParameters (22-30)
  • BifrostImageGenerationResponse (33-40)
  • ImageData (42-47)
  • ImageUsage (49-52)
  • BifrostImageGenerationStreamResponse (55-65)
core/schemas/bifrost.go (1)
  • OpenAI (35-35)
core/providers/ollama/ollama.go (4)
core/schemas/images.go (2)
  • BifrostImageGenerationRequest (4-11)
  • BifrostImageGenerationResponse (33-40)
core/providers/utils/utils.go (1)
  • NewUnsupportedOperationError (391-402)
core/schemas/bifrost.go (3)
  • ImageGenerationRequest (100-100)
  • BifrostStream (340-348)
  • ImageGenerationStreamRequest (101-101)
core/schemas/provider.go (1)
  • PostHookRunner (277-277)
framework/streaming/types.go (3)
core/schemas/chatcompletions.go (1)
  • BifrostLLMUsage (648-655)
core/schemas/bifrost.go (4)
  • BifrostError (378-387)
  • BifrostCacheDebug (318-332)
  • BifrostResponseExtraFields (306-315)
  • RequestType (83-83)
core/schemas/images.go (2)
  • BifrostImageGenerationResponse (33-40)
  • BifrostImageGenerationStreamResponse (55-65)
core/providers/gemini/gemini.go (4)
core/providers/utils/utils.go (3)
  • ProcessAndSendResponse (535-565)
  • GetBifrostResponseForStreamResponse (777-809)
  • NewUnsupportedOperationError (391-402)
core/schemas/images.go (2)
  • BifrostImageGenerationRequest (4-11)
  • BifrostImageGenerationResponse (33-40)
core/schemas/bifrost.go (3)
  • BifrostError (378-387)
  • ImageGenerationRequest (100-100)
  • BifrostStream (340-348)
core/schemas/provider.go (1)
  • PostHookRunner (277-277)
core/providers/bedrock/bedrock.go (5)
core/providers/utils/utils.go (3)
  • ProcessAndSendResponse (535-565)
  • GetBifrostResponseForStreamResponse (777-809)
  • NewUnsupportedOperationError (391-402)
core/schemas/account.go (1)
  • Key (8-17)
core/schemas/images.go (2)
  • BifrostImageGenerationRequest (4-11)
  • BifrostImageGenerationResponse (33-40)
core/schemas/bifrost.go (3)
  • BifrostError (378-387)
  • ImageGenerationRequest (100-100)
  • BifrostStream (340-348)
core/schemas/provider.go (1)
  • PostHookRunner (277-277)
core/providers/cohere/cohere.go (4)
core/providers/utils/utils.go (2)
  • GetBifrostResponseForStreamResponse (777-809)
  • NewUnsupportedOperationError (391-402)
core/schemas/images.go (2)
  • BifrostImageGenerationRequest (4-11)
  • BifrostImageGenerationResponse (33-40)
core/schemas/bifrost.go (4)
  • BifrostError (378-387)
  • ImageGenerationRequest (100-100)
  • BifrostStream (340-348)
  • ImageGenerationStreamRequest (101-101)
core/schemas/provider.go (1)
  • PostHookRunner (277-277)
core/providers/anthropic/anthropic.go (4)
core/schemas/account.go (1)
  • Key (8-17)
core/schemas/images.go (2)
  • BifrostImageGenerationRequest (4-11)
  • BifrostImageGenerationResponse (33-40)
core/schemas/bifrost.go (4)
  • BifrostError (378-387)
  • ImageGenerationRequest (100-100)
  • BifrostStream (340-348)
  • ImageGenerationStreamRequest (101-101)
core/schemas/provider.go (1)
  • PostHookRunner (277-277)
framework/streaming/accumulator.go (2)
framework/streaming/types.go (4)
  • ImageStreamChunk (99-109)
  • StreamAccumulator (112-124)
  • ChatStreamChunk (75-84)
  • ResponsesStreamChunk (87-96)
core/schemas/bifrost.go (1)
  • ImageGenerationStreamRequest (101-101)
🔇 Additional comments (23)
core/providers/cerebras/cerebras.go (1)

206-215: ImageGeneration unsupported stubs are consistent and correct

These two methods cleanly implement the provider interface for image generation while marking Cerebras as unsupported. The request-type enums passed to NewUnsupportedOperationError (ImageGenerationRequest vs. ImageGenerationStreamRequest) match the non‑stream/stream split and are consistent with the existing Embedding/Speech/Transcription patterns in this provider. No changes needed.

core/providers/groq/groq.go (1)

247-255: Image generation stubs correctly signal unsupported operations

The new ImageGeneration and ImageGenerationStream methods are consistent with other providers’ unsupported stubs and use the correct request types in NewUnsupportedOperationError.

core/providers/elevenlabs/elevenlabs.go (2)

407-425: Updated streaming helper usage is consistent with new signature

The additional nil argument in GetBifrostResponseForStreamResponse keeps SpeechStream aligned with the extended helper signature while preserving existing behavior.


655-663: Explicit image-generation stubs align Elevenlabs with provider interface

The new ImageGeneration and ImageGenerationStream methods correctly advertise that ElevenLabs does not support image generation via standardized unsupported-operation errors.

core/providers/sgl/sgl.go (1)

215-223: SGL image-generation stubs are consistent and non-disruptive

The added ImageGeneration / ImageGenerationStream methods correctly surface unsupported image generation for SGL via standard errors without affecting existing functionality.

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

141-152: HTTP request schema and route for image generation match existing patterns

The imageParamsKnownFields map, ImageGenerationHTTPRequest struct, and /v1/images/generations route wiring mirror the existing completion/embeddings/speech patterns and look structurally sound.

Also applies to: 190-195, 306-307

core/providers/ollama/ollama.go (1)

218-226: Ollama image-generation stubs correctly expose unsupported feature

The added ImageGeneration / ImageGenerationStream methods cleanly report that Ollama does not support image generation via standardized errors.

core/providers/bedrock/bedrock.go (2)

653-654: Streaming helper updates for Bedrock are consistent with extended signature

All Bedrock streaming paths now pass six arguments to GetBifrostResponseForStreamResponse, with the image-generation stream slot set to nil, maintaining prior behavior for text/chat/responses streams.

Also applies to: 873-884, 1032-1033, 1125-1126


1251-1259: Bedrock image-generation stubs cleanly signal unsupported operations

The new ImageGeneration and ImageGenerationStream methods correctly use ImageGenerationRequest / ImageGenerationStreamRequest with provider.GetProviderKey(), aligning with the common unsupported-operation pattern.

core/providers/cohere/cohere.go (2)

486-490: Streaming response wrapper arity update looks correct

Both ChatCompletionStream and ResponsesStream now call GetBifrostResponseForStreamResponse(nil, response, nil, nil, nil, nil) / (..., nil, nil, response, nil, nil), which matches the extended signature and keeps the non-image paths unchanged.

Also applies to: 716-720


804-812: Cohere image-generation stubs correctly signal unsupported operations

ImageGeneration and ImageGenerationStream cleanly return NewUnsupportedOperationError with ImageGenerationRequest and ImageGenerationStreamRequest respectively, aligning with other unsupported methods in this provider and the new image-generation API surface.

core/images_test.go (2)

257-392: Streaming chunk accumulation tests provide solid coverage

The three subtests around single-image multi-chunk, multi-image interleaved chunks, and out-of-order chunk sorting exercise the key reconstruction behaviors for PartialB64 and ordering by ChunkIndex/image index. This is a good level of coverage for the new image streaming accumulator.


421-477: Cache-key tests and helper align with intended components

generateTestCacheKey now concatenates prompt, size, quality, style, and n via fmt.Sprintf("%d", *req.Params.N), and the tests verify both determinism for identical requests and key changes for each param toggle. This matches the semantic cache requirements for image generation.

Also applies to: 479-514

core/providers/gemini/gemini.go (2)

604-606: Streaming PostHook wrapper calls updated consistently for new response variant

All four ProcessAndSendResponse call sites in SpeechStream and TranscriptionStream now invoke GetBifrostResponseForStreamResponse(nil, nil, nil, response, nil, nil) / (..., nil, response, nil) as appropriate. This matches the extended factory signature and keeps speech/transcription behavior unchanged.

Also applies to: 627-628, 862-863, 890-891


897-905: Gemini image-generation methods correctly report unsupported operations

ImageGeneration and ImageGenerationStream return NewUnsupportedOperationError with ImageGenerationRequest and ImageGenerationStreamRequest respectively, keeping Gemini’s API surface compatible with the core image-generation interfaces while clearly signaling lack of support.

core/providers/anthropic/anthropic.go (2)

606-607: Anthropic streaming helpers correctly adapted to extended stream response factory

Both the chat and responses streaming helpers now use GetBifrostResponseForStreamResponse(nil, response, nil, nil, nil, nil) and (..., nil, response, nil, nil, nil) respectively, which matches the new image-aware signature while leaving existing behaviors unchanged.

Also applies to: 632-633, 937-941


983-991: Anthropic image-generation stubs mirror other unsupported operations

ImageGeneration and ImageGenerationStream cleanly return NewUnsupportedOperationError for ImageGenerationRequest and ImageGenerationStreamRequest, keeping the provider interface complete for the new request types without implying support Anthropic doesn’t have.

framework/streaming/accumulator.go (1)

352-372: Image streaming dispatch integrates cleanly into ProcessStreamingResponse

Adding isImageStreaming := requestType == schemas.ImageGenerationStreamRequest and delegating to processImageStreamingResponse extends the accumulator’s routing without affecting existing chat/audio/responses paths. This keeps the centralized dispatch logic easy to follow as new stream types are introduced.

framework/streaming/images.go (2)

94-174: Accumulated image streaming data covers latency, usage, cost, and cache debug

processAccumulatedImageStreamingChunks correctly:

  • protects the accumulator with mu,
  • computes latency from StartTimestamp/FinalTimestamp,
  • attaches the reconstructed image response,
  • derives TokenUsage from the last chunk’s Usage,
  • and surfaces cost, cache debug info, and FinishReason from the final chunk.

The cleanup-on-final-chunk path (cleanupStreamAccumulator in the defer) is a nice touch to avoid leaking pooled chunks. This looks good.


176-278: processImageStreamingResponse correctly wires per-chunk metadata and final aggregation

The image streaming handler:

  • extracts the request ID and provider/model via GetResponseFields,
  • builds a pooled ImageStreamChunk, copying the stream delta to avoid stack aliasing,
  • marks error vs completed finish reasons appropriately,
  • records pricing and semantic cache debug on the final chunk, and
  • uses the IsComplete flag under lock so accumulated chunks are processed exactly once per request.

The separation between per-chunk delta responses (returned as StreamResponseTypeDelta) and the final aggregated StreamResponseTypeFinal with AccumulatedData is clear and matches the rest of the streaming framework.

core/bifrost.go (3)

1878-1883: Fallback support extended correctly to image-generation requests

The new ImageGenerationRequest branch in prepareFallbackRequest copies the original request and overrides Provider/Model with the fallback’s, consistent with how text/chat/responses/speech/transcription fallbacks are handled. This should make image generation participate in the same fallback flow without surprises.


2555-2560: Provider dispatch now includes image-generation and image-generation-stream requests

Adding ImageGenerationRequest and ImageGenerationStreamRequest cases to handleProviderRequest and handleProviderStreamRequest ensures image flows go through the same centralized dispatch as other request types and benefit from retries, plugins, and fallbacks. This aligns well with the rest of the core routing.

Also applies to: 2585-2587


2748-2758: BifrostRequest pool reset includes image-generation field

Extending resetBifrostRequest to clear ImageGenerationRequest keeps the request pooling safe for reuse now that image-generation is part of the schema, and avoids stale pointers leaking between calls.

Copy link
Collaborator

@Pratham-Mishra04 Pratham-Mishra04 left a comment

Choose a reason for hiding this comment

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

added comments

@Radheshg04 Radheshg04 force-pushed the feature/image-generation-950 branch from b5899e6 to f50dccc Compare December 10, 2025 09:02
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: 0

Caution

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

⚠️ Outside diff range comments (1)
core/internal/testutil/account.go (1)

541-563: Enable image generation scenarios in AllProviderConfigs for OpenAI.

The OpenAI entry sets ImageGenerationModel: "dall-e-2" but doesn't enable ImageGeneration: true or ImageGenerationStream: true in the Scenarios struct. This means image generation tests won't run when using AllProviderConfigs.

 		ImageGenerationModel: "dall-e-2",
 		Scenarios: TestScenarios{
 			TextCompletion:        false, // Not supported
 			TextCompletionStream:  false, // Not supported
 			SimpleChat:            true,
 			CompletionStream:      true,
 			MultiTurnConversation: true,
 			ToolCalls:             true,
 			MultipleToolCalls:     true,
 			End2EndToolCalling:    true,
 			AutomaticFunctionCall: true,
 			ImageURL:              true,
 			ImageBase64:           true,
 			MultipleImages:        true,
 			CompleteEnd2End:       true,
 			SpeechSynthesis:       true, // OpenAI supports TTS
 			SpeechSynthesisStream: true, // OpenAI supports streaming TTS
 			Transcription:         true, // OpenAI supports STT with Whisper
 			TranscriptionStream:   true, // OpenAI supports streaming STT
 			Embedding:             true,
 			Reasoning:             true, // OpenAI supports reasoning via o1 models
 			ListModels:            true,
+			ImageGeneration:       true, // OpenAI supports image generation
+			ImageGenerationStream: true, // OpenAI supports streaming image generation
 		},
♻️ Duplicate comments (1)
transports/bifrost-http/handlers/inference.go (1)

1184-1203: Fix premature context cancellation in imageGeneration streaming path

defer cancel() is executed before the stream check, so for streaming requests the context is canceled as soon as imageGeneration returns, which will terminate the upstream image stream almost immediately instead of only on client disconnect (unlike the other stream handlers).

Align this with textCompletion, chatCompletion, responses, and speech by deferring cancel only for the non‑stream path:

 	// Convert context
 	bifrostCtx, cancel := lib.ConvertToBifrostContext(ctx, h.handlerStore.ShouldAllowDirectKeys())
 	if bifrostCtx == nil {
 		SendError(ctx, fasthttp.StatusInternalServerError, "Failed to convert context")
 		return
 	}
-	defer cancel()
-
-	// Handle streaming image generation
-	if req.BifrostParams.Stream != nil && *req.BifrostParams.Stream {
+
+	// Handle streaming image generation
+	if req.Stream != nil && *req.Stream {
 		if req.ResponseFormat != nil && *req.ResponseFormat == "url" {
 			SendError(ctx, fasthttp.StatusBadRequest, "streaming images must be requested in base64")
 			return
 		}
 		h.handleStreamingImageGeneration(ctx, bifrostReq, bifrostCtx, cancel)
 		return
 	}
+
+	// Non-streaming: cancel on handler exit
+	defer cancel()
 
 	// Execute request
 	resp, bifrostErr := h.client.ImageGenerationRequest(*bifrostCtx, bifrostReq)

This preserves the cancellable context for the duration of the stream and only cancels when handleStreamingResponse detects client disconnects.

🧹 Nitpick comments (9)
transports/bifrost-http/handlers/inference.go (1)

1003-1006: Avoid logging full streaming chunk payload on marshal errors

Including %v of chunk can dump large base64 blobs (and potentially sensitive content) into logs, especially for image streams.

Consider logging only high‑level metadata or a truncated representation instead:

-			chunkJSON, err := sonic.Marshal(chunk)
-			if err != nil {
-				logger.Warn(fmt.Sprintf("Failed to marshal streaming response: %v, chunk: %v", err, chunk))
-				continue
-			}
+			chunkJSON, err := sonic.Marshal(chunk)
+			if err != nil {
+				logger.Warn(fmt.Sprintf("Failed to marshal streaming response: %v (omitting chunk payload)", err))
+				continue
+			}
core/providers/utils/utils.go (1)

586-596: Consider adding image generation stream response handling to error functions for consistency.

ProcessAndSendBifrostError and ProcessAndSendError don't propagate ImageGenerationStreamResponse when processedResponse is non-nil, unlike ProcessAndSendResponse. While error scenarios typically don't carry image data, the inconsistency could cause issues if a post-hook transforms an error response into a partial image response.

core/internal/testutil/image_generation.go (1)

100-105: Consider increasing the minimum base64 length threshold.

A 100-byte threshold is extremely low for validating image data. A 1024x1024 image in base64 would typically be several hundred kilobytes to megabytes. This could allow corrupt or placeholder responses to pass validation.

 		// Validate base64 if present
 		if imageData.B64JSON != "" {
-			if len(imageData.B64JSON) < 100 {
+			if len(imageData.B64JSON) < 10000 { // ~7KB minimum for a reasonable image
 				t.Errorf("❌ Base64 image data too short: %d bytes", len(imageData.B64JSON))
 			}
 		}
core/internal/testutil/test_retry_conditions.go (1)

845-881: EmptyImageGenerationCondition logic is sound; only a minor comment nit

The retry condition correctly covers nil responses, missing ImageGenerationResponse, empty Data, and entries lacking both B64JSON and URL, consistent with other modality “empty” checks. The implementation looks good; consider fixing the comment typo (“image image”) for clarity.

core/internal/testutil/image_generation_load.go (1)

213-307: Avoid sharing a single request instance across cache-load goroutines

In the cache load test, the same request pointer is reused in all goroutines:

  • If client.ImageGenerationRequest or downstream code mutates the request (e.g., adds RawRequestBody or other fields), this can introduce data races when running tests with -race.
  • It’s safer to build a fresh request per goroutine (while reusing cachePrompt and common params) to ensure immutability of arguments.

You can keep the initial “populate cache” request as-is, and construct a per-goroutine request for the parallel section, e.g.:

-        for i := 0; i < numConcurrentRequests; i++ {
+        for i := 0; i < numConcurrentRequests; i++ {
             wg.Add(1)
             go func() {
                 defer wg.Done()
 
-                reqStart := time.Now()
-                response, bifrostErr := client.ImageGenerationRequest(ctx, request)
+                // Build a fresh request per goroutine to avoid shared mutable state.
+                localReq := &schemas.BifrostImageGenerationRequest{
+                    Provider: testConfig.Provider,
+                    Model:    testConfig.ImageGenerationModel,
+                    Input: &schemas.ImageGenerationInput{
+                        Prompt: cachePrompt,
+                    },
+                    Params: &schemas.ImageGenerationParameters{
+                        Size:           bifrost.Ptr("1024x1024"),
+                        ResponseFormat: bifrost.Ptr("b64_json"),
+                    },
+                }
+
+                reqStart := time.Now()
+                response, bifrostErr := client.ImageGenerationRequest(ctx, localReq)
core/internal/testutil/response_validation.go (1)

1053-1122: ImageGeneration field checks are solid; consider extending technical expectations later

The helper functions:

  • Enforce that Data is non-empty and every image has either B64JSON or URL.
  • Optionally enforce a minimum number of images via ProviderSpecific["min_images"].
  • Record expected_size and validate latency when ShouldHaveLatency is set.
  • Collect useful metrics (image count, URL vs B64 counts, usage tokens).

This is a good baseline. If you later need stricter technical validation for images, you could also honor ResponseExpectations.ShouldHaveUsageStats / ShouldHaveModel similarly to chat/text, but that’s an optional enhancement, not a blocker.

core/internal/testutil/test_retry_framework.go (3)

161-249: ImageGeneration retry interfaces and configs integrate cleanly with existing patterns

Adding ImageGenerationRetryCondition and ImageGenerationRetryConfig, plus DefaultImageGenerationRetryConfig and the "ImageGeneration" / "ImageGenerationStream" cases in GetTestRetryConfigForScenario, follows the same structure used for other modalities (chat, speech, embeddings, etc.). Using EmptyImageGenerationCondition + GenericResponseCondition as defaults makes sense and aligns with the new image-specific emptiness check.

The only minor nit is that DefaultImageGenerationRetryConfig returns the generic TestRetryConfig while there is also an ImageGenerationRetryConfig type; this matches existing naming for other helpers (e.g., speech/transcription) but can be slightly confusing at first glance. Not blocking, just something to keep in mind for future naming consistency.

Also applies to: 926-940, 1176-1177


1875-2049: WithImageGenerationRetry behavior matches other typed retry helpers

WithImageGenerationRetry:

  • Runs the operation, validates via ValidateImageGenerationResponse first, and returns immediately on success.
  • Always retries on validation failures (primary purpose), with exponential backoff, and prioritizes timeout errors via isTimeoutError.
  • Incorporates image-specific retry conditions through checkImageGenerationRetryConditions.
  • For error-only cases, it always retries non-structural errors at least once, mirroring the transcription/embedding retry behavior.
  • On exhausting attempts, it emits OnFinalFail and returns a synthetic BifrostError to force test failure.

This is consistent with the rest of the framework and looks correct. There is some inevitable duplication with other With*TestRetry functions; if this pattern grows further, it might be worth considering a generic helper, but that’s an optional future refactor.


2697-2845: Image-generation stream retry wrapper matches other stream validators; small comment/name nit

ImageGenerationStreamValidationResult and WithImageGenerationStreamRetry closely mirror existing speech/chat/responses streaming validation wrappers: they retry on stream setup errors (with special handling for timeouts), validate the full stream via a callback, and always retry on validation failures with exponential backoff and appropriate logging/OnFinalFail hooks. Behavior is consistent and looks good.

Minor nit: the function comment refers to WithImageGenerationStreamValidationRetry, but the function is named WithImageGenerationStreamRetry. Updating the comment to match the actual name would avoid confusion.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b5899e6 and f50dccc.

📒 Files selected for processing (14)
  • core/internal/testutil/account.go (3 hunks)
  • core/internal/testutil/image_generation.go (1 hunks)
  • core/internal/testutil/image_generation_load.go (1 hunks)
  • core/internal/testutil/image_generation_stream.go (1 hunks)
  • core/internal/testutil/response_validation.go (2 hunks)
  • core/internal/testutil/test_retry_conditions.go (1 hunks)
  • core/internal/testutil/test_retry_framework.go (6 hunks)
  • core/internal/testutil/validation_presets.go (2 hunks)
  • core/providers/openai/images.go (1 hunks)
  • core/providers/openai/openai.go (9 hunks)
  • core/providers/openai/openai_test.go (2 hunks)
  • core/providers/openai/types.go (1 hunks)
  • core/providers/utils/utils.go (3 hunks)
  • transports/bifrost-http/handlers/inference.go (7 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • core/internal/testutil/image_generation_stream.go
  • core/providers/openai/images.go
🧰 Additional context used
📓 Path-based instructions (1)
**

⚙️ CodeRabbit configuration file

always check the stack if there is one for the current PR. do not give localized reviews for the PR, always see all changes in the light of the whole stack of PRs (if there is a stack, if there is no stack you can continue to make localized suggestions/reviews)

Files:

  • core/internal/testutil/image_generation.go
  • core/internal/testutil/test_retry_conditions.go
  • core/internal/testutil/account.go
  • core/providers/openai/openai_test.go
  • core/providers/openai/types.go
  • core/providers/openai/openai.go
  • core/internal/testutil/response_validation.go
  • core/internal/testutil/validation_presets.go
  • transports/bifrost-http/handlers/inference.go
  • core/internal/testutil/image_generation_load.go
  • core/internal/testutil/test_retry_framework.go
  • core/providers/utils/utils.go
🧠 Learnings (1)
📚 Learning: 2025-12-09T17:07:42.007Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/schemas/account.go:9-18
Timestamp: 2025-12-09T17:07:42.007Z
Learning: In core/schemas/account.go, the HuggingFaceKeyConfig field within the Key struct is currently unused and reserved for future Hugging Face inference endpoint deployments. Do not flag this field as missing from OpenAPI documentation or require its presence in the API spec until the feature is actively implemented and used. When the feature is added, update the OpenAPI docs accordingly; otherwise, treat this field as non-breaking and not part of the current API surface.

Applied to files:

  • core/internal/testutil/image_generation.go
  • core/internal/testutil/test_retry_conditions.go
  • core/internal/testutil/account.go
  • core/providers/openai/openai_test.go
  • core/providers/openai/types.go
  • core/providers/openai/openai.go
  • core/internal/testutil/response_validation.go
  • core/internal/testutil/validation_presets.go
  • transports/bifrost-http/handlers/inference.go
  • core/internal/testutil/image_generation_load.go
  • core/internal/testutil/test_retry_framework.go
  • core/providers/utils/utils.go
🧬 Code graph analysis (9)
core/internal/testutil/test_retry_conditions.go (2)
core/schemas/bifrost.go (1)
  • BifrostResponse (262-274)
core/internal/testutil/test_retry_framework.go (1)
  • TestRetryContext (174-179)
core/internal/testutil/account.go (1)
core/schemas/bifrost.go (1)
  • Fallback (132-135)
core/providers/openai/types.go (1)
core/providers/openai/images.go (1)
  • ImageGenerationEventType (20-20)
core/providers/openai/openai.go (1)
core/providers/utils/utils.go (2)
  • ProcessAndSendResponse (535-566)
  • GetBifrostResponseForStreamResponse (788-820)
core/internal/testutil/response_validation.go (2)
core/schemas/images.go (1)
  • BifrostImageGenerationResponse (33-40)
core/internal/testutil/error_parser.go (3)
  • ParseBifrostError (47-104)
  • FormatErrorConcise (353-356)
  • LogError (359-366)
core/internal/testutil/validation_presets.go (1)
core/internal/testutil/response_validation.go (1)
  • ResponseExpectations (18-43)
transports/bifrost-http/handlers/inference.go (8)
core/schemas/images.go (3)
  • ImageGenerationInput (18-20)
  • ImageGenerationParameters (22-30)
  • BifrostImageGenerationRequest (4-11)
transports/bifrost-http/lib/middleware.go (1)
  • ChainMiddlewares (12-24)
transports/bifrost-http/handlers/utils.go (2)
  • SendError (35-44)
  • SendBifrostError (47-62)
core/schemas/utils.go (1)
  • ParseModelString (23-36)
core/schemas/models.go (1)
  • Model (109-129)
core/schemas/provider.go (1)
  • Provider (207-240)
core/schemas/bifrost.go (5)
  • ModelProvider (32-32)
  • ImageGenerationRequest (100-100)
  • BifrostStream (340-348)
  • BifrostError (378-387)
  • ImageGenerationStreamRequest (101-101)
transports/bifrost-http/lib/ctx.go (1)
  • ConvertToBifrostContext (72-283)
core/internal/testutil/image_generation_load.go (3)
core/internal/testutil/account.go (1)
  • ComprehensiveTestConfig (49-68)
core/schemas/images.go (4)
  • BifrostImageGenerationRequest (4-11)
  • ImageGenerationInput (18-20)
  • ImageGenerationParameters (22-30)
  • BifrostImageGenerationStreamResponse (55-65)
core/schemas/bifrost.go (2)
  • ImageGenerationRequest (100-100)
  • ImageGenerationStreamRequest (101-101)
core/internal/testutil/test_retry_framework.go (5)
core/schemas/images.go (1)
  • BifrostImageGenerationResponse (33-40)
core/internal/testutil/test_retry_conditions.go (2)
  • EmptyImageGenerationCondition (851-851)
  • GenericResponseCondition (370-370)
core/internal/testutil/response_validation.go (2)
  • ResponseExpectations (18-43)
  • ValidateImageGenerationResponse (241-275)
core/internal/testutil/utils.go (1)
  • GetErrorMessage (606-639)
core/schemas/bifrost.go (1)
  • BifrostStream (340-348)
🔇 Additional comments (26)
transports/bifrost-http/handlers/inference.go (4)

5-25: Imports cleanup looks good

Import set is minimal and all packages are used; the commented schemas import has been properly cleaned up.


141-152: Image params whitelist is consistent

imageParamsKnownFields correctly captures the expected top-level fields for /v1/images/generations, so unknown keys will flow into ExtraParams as intended.


190-194: HTTP request wrapper for images matches existing patterns

Embedding ImageGenerationInput, ImageGenerationParameters, and BifrostParams is consistent with other request types and gives the expected JSON shape.


1212-1222: Streaming image handler wiring is consistent

handleStreamingImageGeneration mirrors the existing streaming helpers (text/chat/responses/speech), delegating to ImageGenerationStreamRequest and the shared SSE handler, so no further changes are needed once the context lifecycle is fixed in the caller.

core/providers/openai/openai_test.go (1)

40-40: LGTM!

The test configuration correctly enables image generation testing with dall-e-3 and enables both streaming and non-streaming test scenarios. This aligns well with the new ImageGeneration and ImageGenerationStream capabilities added to the OpenAI provider.

Also applies to: 63-64

core/providers/utils/utils.go (2)

555-556: LGTM!

The ProcessAndSendResponse function correctly propagates ImageGenerationStreamResponse to the unified stream response, maintaining consistency with other response types (text completion, chat, responses, speech, transcription).


794-817: LGTM!

The GetBifrostResponseForStreamResponse function is correctly extended with the new imageGenerationStreamResponse parameter. The switch-case handling follows the established pattern for other response types.

core/providers/openai/openai.go (8)

550-550: LGTM!

The text completion streaming handlers correctly pass nil for the new imageGenerationStreamResponse parameter to GetBifrostResponseForStreamResponse.

Also applies to: 570-570


938-945: LGTM!

The responses-to-chat-completions fallback streaming path correctly passes nil for the image generation parameter.


1013-1013: LGTM!

The chat completion streaming handler correctly passes nil for the image generation parameter at both chunk emission points.

Also applies to: 1034-1034


1366-1366: LGTM!

The responses streaming handler correctly passes nil for the image generation parameter.

Also applies to: 1373-1373


1741-1741: LGTM!

The speech streaming handler correctly passes nil for the image generation parameter.

Also applies to: 1745-1745


2013-2013: LGTM!

The transcription streaming handler correctly passes nil for the image generation parameter.

Also applies to: 2017-2017


2138-2164: Cannot complete verification due to repository access failure.

The original review comment requests verification of the doRequest method's existence and correctness in handling image generation API calls. This verification cannot be performed without access to the repository code. The review comment's approval is conditional on this verification being completed.


2169-2204: Verify that streaming dependencies are properly defined.

The ImageGenerationStream method calls HandleOpenAIImageGenerationStreaming, simulateImageStreaming, and references StreamingEnabledImageModels. These symbols do not appear in the public package documentation and should be verified to exist in the codebase and be correctly implemented.

core/internal/testutil/account.go (2)

44-45: LGTM!

The TestScenarios struct is correctly extended with ImageGeneration and ImageGenerationStream flags, following the established pattern for other test scenarios.


66-67: LGTM!

The ComprehensiveTestConfig struct correctly adds ImageGenerationModel and ImageGenerationFallbacks fields, consistent with other capability-specific configurations (e.g., TranscriptionModel/TranscriptionFallbacks).

core/internal/testutil/image_generation.go (3)

1-10: LGTM!

Clean imports and package declaration for the new image generation test utility.


24-46: LGTM!

Retry configuration is properly set up using the test framework's retry infrastructure, addressing flakiness concerns for CI. The WithImageGenerationRetry wrapper is correctly used.


48-77: LGTM!

The test operation function correctly builds the BifrostImageGenerationRequest with appropriate defaults and handles the response/error flow properly. The nil response is correctly converted to a BifrostError.

core/internal/testutil/validation_presets.go (2)

188-203: LGTM!

The ImageGenerationExpectations function correctly configures validation expectations for image generation scenarios. The settings appropriately disable text content and choices expectations while enabling metadata validation.


305-311: LGTM!

The GetExpectationsForScenario switch case for "ImageGeneration" correctly handles custom parameters with proper type assertions and provides sensible defaults (1 image, 1024x1024 size).

core/internal/testutil/image_generation_load.go (2)

15-101: ImageGeneration load test is well-structured and concurrency-safe

The load test uses t.Run + t.Parallel, WaitGroup, atomics, and a mutex for shared metrics correctly. Success/error accounting and basic latency/RPS stats look reasonable for exercising the image-generation path under modest concurrency.


103-211: Verify completion Type string and confirm stream-drain safety

Two issues require verification before merge:

  1. The completion check uses Type == "image_generation.completed". The schema documentation mentions values like "image.chunk" / "image.complete" / "error". Confirm this string matches the actual completion marker from BifrostImageGenerationStreamResponse in core/schemas/images.go, or replace with a shared constant to avoid silent failures where all streams count as incomplete.

  2. The loop breaks immediately upon seeing the completion event without draining remaining channel items. Verify the producer guarantees the channel closes immediately after sending the completion event. If bookkeeping events can follow, drain the channel fully to prevent blocking the producer goroutine.

core/internal/testutil/response_validation.go (1)

240-275: Image-generation validator follows existing validation pattern correctly

ValidateImageGenerationResponse mirrors the other modality validators: it treats non-nil err and nil responses as failures, routes errors through ParseBifrostError/LogError, delegates to image-specific field validation, collects metrics, and logs via logValidationResults. The control flow and error handling are consistent and look good.

core/providers/openai/types.go (1)

162-208: OpenAI image generation structs are correctly aligned with current API specification

The request, response, usage, and streaming event structs precisely match the OpenAI images/generations API schema (2025): created timestamp, data array with URL/b64_json/revised_prompt, background/output_format/size/quality fields, and usage tracking with input_tokens_details.

If other provider request types in this codebase expose a Bifrost-specific Fallbacks field for provider→Bifrost round-trips, consider adding it to OpenAIImageGenerationRequest as well for consistency. This is optional and depends on whether your image generation fallback flow requires it.

Copy link
Collaborator

Hello @Radheshg04, can you pls resolve merge conflicts on this

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: 2

🧹 Nitpick comments (1)
core/providers/anthropic/anthropic.go (1)

632-633: Updated stream-response wrapper call is correct, but the “many nils” API is brittle.
Passing the new 6th arg as nil matches the updated GetBifrostResponseForStreamResponse signature; consider a small helper (per stream type) to avoid future arg-order mistakes.

Also applies to: 662-663

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f50dccc and bb970dd.

📒 Files selected for processing (2)
  • core/providers/anthropic/anthropic.go (4 hunks)
  • core/providers/openai/openai.go (9 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**

⚙️ CodeRabbit configuration file

always check the stack if there is one for the current PR. do not give localized reviews for the PR, always see all changes in the light of the whole stack of PRs (if there is a stack, if there is no stack you can continue to make localized suggestions/reviews)

Files:

  • core/providers/openai/openai.go
  • core/providers/anthropic/anthropic.go
🧠 Learnings (2)
📚 Learning: 2025-12-09T17:07:42.007Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/schemas/account.go:9-18
Timestamp: 2025-12-09T17:07:42.007Z
Learning: In core/schemas/account.go, the HuggingFaceKeyConfig field within the Key struct is currently unused and reserved for future Hugging Face inference endpoint deployments. Do not flag this field as missing from OpenAPI documentation or require its presence in the API spec until the feature is actively implemented and used. When the feature is added, update the OpenAPI docs accordingly; otherwise, treat this field as non-breaking and not part of the current API surface.

Applied to files:

  • core/providers/openai/openai.go
  • core/providers/anthropic/anthropic.go
📚 Learning: 2025-12-11T11:58:25.307Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1000
File: core/providers/openai/responses.go:42-84
Timestamp: 2025-12-11T11:58:25.307Z
Learning: In core/providers/openai/responses.go (and related OpenAI response handling), document and enforce the API format constraint: if ResponsesReasoning != nil and the response contains content blocks, all content blocks should be treated as reasoning blocks by default. Implement type guards or parsing logic accordingly, and add unit tests to verify that when ResponsesReasoning is non-nil, content blocks are labeled as reasoning blocks. Include clear comments in the code explaining the rationale and ensure downstream consumers rely on this behavior.

Applied to files:

  • core/providers/openai/openai.go
🧬 Code graph analysis (2)
core/providers/openai/openai.go (1)
core/providers/utils/utils.go (1)
  • GetBifrostResponseForStreamResponse (788-820)
core/providers/anthropic/anthropic.go (3)
core/providers/utils/utils.go (3)
  • ProcessAndSendResponse (535-566)
  • GetBifrostResponseForStreamResponse (788-820)
  • NewUnsupportedOperationError (391-402)
core/schemas/bifrost.go (2)
  • BifrostError (378-387)
  • BifrostStream (340-348)
core/schemas/provider.go (1)
  • PostHookRunner (204-204)
🔇 Additional comments (3)
core/providers/anthropic/anthropic.go (2)

981-982: Responses stream wrapper update looks consistent with new image-stream slot.
These call sites correctly populate only the responsesStreamResponse position and pass nil for the new image-stream parameter.

Also applies to: 984-985


1026-1035: The ImageGenerationStream parameter type is correct. The interface (core/schemas/provider.go:238-239) defines the parameter as *BifrostImageGenerationRequest, and the Anthropic provider's implementation matches this exactly. All other providers consistently use the same parameter type. The error constant schemas.ImageGenerationStreamRequest is for error messaging only, not a request type.

core/providers/openai/openai.go (1)

578-578: LGTM! Mechanical updates correctly handle signature change.

All calls to GetBifrostResponseForStreamResponse have been correctly updated to pass nil for the new imageGenerationStreamResponse parameter. This maintains consistency across all existing streaming handlers while accommodating the new image generation streaming support.

Also applies to: 602-602, 983-983, 990-990, 1064-1064, 1089-1089, 1434-1434, 1441-1441, 1817-1817, 1821-1821, 2089-2089, 2093-2093

@Radheshg04 Radheshg04 force-pushed the feature/image-generation-950 branch from bb970dd to f50dccc Compare December 12, 2025 15:20
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: 20

Caution

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

⚠️ Outside diff range comments (1)
core/internal/testutil/tests.go (1)

24-62: Update printTestSummary to include new image-generation scenarios.

Right now the runner executes image-generation tests, but the summary table doesn’t mention them, so it will under-report enabled coverage.

Also applies to: 73-101

♻️ Duplicate comments (3)
transports/bifrost-http/handlers/inference.go (1)

1260-1270: Fix premature context cancellation in streaming image generation handler.

Line 1260 calls defer cancel() before checking if the request is streaming (line 1263). For streaming requests, this means the context will be canceled as soon as the handler returns at line 1269, terminating the upstream image generation stream prematurely instead of only on client disconnect.

This issue was previously flagged in past review comments. Align with the pattern used in other streaming handlers (textCompletion, chatCompletion, responses, speech):

Apply this diff:

 	// Convert context
 	bifrostCtx, cancel := lib.ConvertToBifrostContext(ctx, h.handlerStore.ShouldAllowDirectKeys())
 	if bifrostCtx == nil {
 		SendError(ctx, fasthttp.StatusInternalServerError, "Failed to convert context")
 		return
 	}
-	defer cancel()
 
 	// Handle streaming image generation
 	if req.BifrostParams.Stream != nil && *req.BifrostParams.Stream {
 		if req.ResponseFormat != nil && *req.ResponseFormat == "url" {
 			SendError(ctx, fasthttp.StatusBadRequest, "streaming images must be requested in base64")
 			return
 		}
 		h.handleStreamingImageGeneration(ctx, bifrostReq, bifrostCtx, cancel)
 		return
 	}
 
+	// Non-streaming: cancel on handler exit
+	defer cancel()
+
 	// Execute request
 	resp, bifrostErr := h.client.ImageGenerationRequest(*bifrostCtx, bifrostReq)
core/providers/openai/openai.go (1)

2154-2179: Raw request handling gaps in ImageGeneration (+ missing sendBackRawRequest in streaming) — still unresolved.
This matches prior review feedback: non-streaming image generation doesn’t appear to honor raw request/response flags, and the streaming call to HandleOpenAIImageGenerationStreaming passes only sendBackRawResponse (no sendBackRawRequest).

Also applies to: 2204-2219

core/bifrost.go (1)

1411-1570: Responses image-gen tool output Type looks mislabeled (call vs output).
This still sets outputType := schemas.ResponsesMessageTypeImageGenerationCall while building an output payload (ResponsesImageGenerationCallOutput). If downstream switches on ResponsesMessage.Type, this can break routing. (This matches the earlier review note.)

🧹 Nitpick comments (19)
ui/components/chat/ImageMessage.tsx (1)

5-57: Use stable keys + avoid “Loading… undefined%”; small accessibility/UX tweaks.
Right now key={idx} can cause remounts if the array is reordered; and streamProgress being optional can render as undefined%.

 interface ImageMessageProps {
@@
   isStreaming?: boolean;
   streamProgress?: number; // 0-100
 }

 export const ImageMessage: React.FC<ImageMessageProps> = ({
   images,
   isStreaming,
-  streamProgress
+  streamProgress = 0,
 }) => {
   return (
-    <div className="grid grid-cols-2 gap-4 my-4">
+    <div className="grid grid-cols-1 sm:grid-cols-2 gap-4 my-4">
       {images.map((img, idx) => (
-        <Card key={idx} className="overflow-hidden">
+        <Card key={img.index} className="overflow-hidden">
@@
-    <div className="absolute bottom-2 left-2 text-sm text-muted-foreground">
-      Loading... {streamProgress}%
+    <div className="absolute bottom-2 left-2 text-sm text-muted-foreground">
+      Loading... {Math.max(0, Math.min(100, streamProgress))}%
     </div>
@@
     <img
       src={img.url || `data:image/png;base64,${img.b64_json}`}
-      alt={img.revised_prompt || `Generated image ${idx + 1}`}
+      alt={img.revised_prompt || `Generated image ${img.index + 1}`}
       className="w-full h-auto"
       loading="lazy"
     />
ui/hooks/useImageStream.ts (1)

88-176: Free per-image chunk buffers after completion to avoid unbounded memory growth.
Once an image is completed and assembled, keeping all chunk strings around is unnecessary.

       if (type === "image_generation.completed") {
         const completedImage = buildImageFromChunks(index);
+        // Free chunk buffers for this image after assembly
+        imageChunksRef.current.delete(index);
plugins/semanticcache/utils.go (2)

383-416: Avoid duplicating large image payloads in metadata (response vs image_b64).

metadata["response"] already contains the marshaled BifrostResponse (likely including base64). Adding image_b64 duplicates large data and can inflate vectorstore metadata size.

  • Consider storing only image_urls/revised_prompts (and maybe image_b64_sha256 / byte length) and rely on response for the canonical payload.

655-661: Normalize input + params extraction is sensible; consider including all cache-relevant params in key.

This normalization/extraction looks consistent with other request types. Just ensure getImageCacheKey(...) accounts for any params that materially change output (e.g., response_format, extra params) so the short-circuit hash doesn’t under-key.

Also applies to: 960-986

core/internal/testutil/test_retry_framework.go (1)

161-165: Retry config surface is OK; consider reducing type explosion over time.

This mirrors other modalities, but it’s trending toward a lot of near-duplicate retry wrappers/configs.

Also applies to: 241-249

core/internal/testutil/account.go (1)

21-68: Align image-generation enablement: model is set, but scenario flags aren’t.

OpenAI sets ImageGenerationModel: "dall-e-2" (Line 524) but TestScenarios doesn’t set ImageGeneration / ImageGenerationStream to true. Depending on how RunImageGenerationTest gates execution, this can cause confusing behavior (tests run while summary reports “skipped”, or vice versa).

  • Either set Scenarios.ImageGeneration = true (+ stream if supported) in AllProviderConfigs, or make the scenario functions/summary consistently key off ImageGenerationModel presence (and drop the flags).

Also applies to: 514-546

core/internal/testutil/image_generation_stream.go (1)

62-119: Consider enhancing stream validation checks.

The current validation (lines 62-119) checks for:

  • Received data (line 64)
  • Completion event (lines 93-95)
  • Stream errors (line 66)

However, the validation could be more thorough to match the documented streaming behavior:

  • Track and verify that partial base64 chunks (PartialB64) are received during streaming
  • Validate that usage data is present on the final completion chunk
  • Verify chunk ordering via ChunkIndex
  • Ensure RevisedPrompt appears on first chunk if present

Consider adding these checks inside the stream validation loop:

 						if response.BifrostImageGenerationStreamResponse != nil {
 							receivedData = true
 							imgResp := response.BifrostImageGenerationStreamResponse
 
+							// Track partial chunks
+							if imgResp.PartialB64 != "" {
+								// Validate partial base64 chunk
+							}
+
 							if imgResp.Type == string(openai.ImageGenerationCompleted) {
 								hasCompleted = true
+								// Validate final chunk has usage
+								if imgResp.Usage == nil {
+									validationErrors = append(validationErrors, "Final chunk missing usage data")
+								}
 							}
 						}
core/images_test.go (1)

452-477: Cache-key helper is collision-prone (no separators) — ensure it mirrors prod logic.
generateTestCacheKey concatenates prompt+params without delimiters, so different tuples can collide ("ab"+"c" vs "a"+"bc"). Since this is meant to emulate the semantic cache key, either import/use the real generator (preferred) or add separators and update expectations accordingly.

@@
  sb.WriteString(req.Input.Prompt)
 
  if req.Params != nil {
      if req.Params.Size != nil {
-         sb.WriteString(*req.Params.Size)
+         sb.WriteString("|size=")
+         sb.WriteString(*req.Params.Size)
      }
      if req.Params.Quality != nil {
-         sb.WriteString(*req.Params.Quality)
+         sb.WriteString("|quality=")
+         sb.WriteString(*req.Params.Quality)
      }
      if req.Params.Style != nil {
-         sb.WriteString(*req.Params.Style)
+         sb.WriteString("|style=")
+         sb.WriteString(*req.Params.Style)
      }
      if req.Params.N != nil {
-         sb.WriteString(fmt.Sprintf("%d", *req.Params.N))
+         sb.WriteString("|n=")
+         sb.WriteString(fmt.Sprintf("%d", *req.Params.N))
      }
  }
core/providers/bedrock/bedrock.go (1)

1296-1311: getModelPath: consider setting deployment even when ARN is absent (if that’s intended).
Right now deployment is only set when Deployments[model] exists and ARN != nil. If callers rely on ModelDeployment for metrics/routing, you may want deployment = inferenceProfileID even without ARN (while leaving path unchanged).

core/internal/testutil/validation_presets.go (2)

188-203: Remove stray “add this function” comment; keep presets clean.


305-312: customParams type assertions may be too strict (often float64 for numbers).
If customParams can come from decoded JSON, min_images will typically be float64 not int, so the override won’t apply. Consider accepting both.

core/internal/testutil/image_generation_errors.go (1)

25-153: Avoid emojis + reduce repeated strings.ToLower calls (test output hygiene).
Not a blocker, but the current output can get noisy in CI, and you call strings.ToLower(errorMsg) repeatedly per assertion.

core/internal/testutil/image_generation_load.go (1)

15-101: Make prompts truly unique to avoid collisions under concurrency. Second-level timestamps can collide across goroutines.

- Prompt: "A test image for load testing - " + time.Now().Format("20060102150405"),
+ Prompt: fmt.Sprintf("A test image for load testing - %s - g=%d r=%d", time.Now().Format(time.RFC3339Nano), goroutineID, j),

(Will require adding fmt import.)

framework/streaming/accumulator.go (1)

380-411: Prefer using putImageStreamChunk in Cleanup() for consistent zeroing. Right now Cleanup() puts pooled chunks back without clearing fields.

- for _, chunk := range accumulator.ImageStreamChunks {
-     a.imageStreamChunkPool.Put(chunk)
- }
+ for _, chunk := range accumulator.ImageStreamChunks {
+     a.putImageStreamChunk(chunk)
+ }
core/providers/openai/images.go (1)

61-96: Consider setting ExtraFields.RequestType on image responses for consistency. Today ToBifrostImageResponse sets Provider/Latency but leaves RequestType empty (unless set elsewhere).

 ExtraFields: schemas.BifrostResponseExtraFields{
+    RequestType: schemas.ImageGenerationRequest,
     Provider: schemas.OpenAI,
     Latency:  latency.Milliseconds(),
 },
core/bifrost.go (1)

3160-3225: Add ExtraFields context to validation errors for consistency/debuggability.
Unlike other public APIs, the early validation errors here (nil req / missing prompt / nil provider response) don’t populate ExtraFields (request type/provider/model). This makes logs/metrics harder to correlate.

Suggested diff:

 if req == nil {
     return nil, &schemas.BifrostError{
         IsBifrostError: false,
         Error: &schemas.ErrorField{Message: "image generation request is nil"},
+        ExtraFields: schemas.BifrostErrorExtraFields{RequestType: schemas.ImageGenerationRequest},
     }
 }
 if req.Input == nil || req.Input.Prompt == "" {
     return nil, &schemas.BifrostError{
         IsBifrostError: false,
         Error: &schemas.ErrorField{Message: "prompt not provided for image generation request"},
+        ExtraFields: schemas.BifrostErrorExtraFields{
+            RequestType: schemas.ImageGenerationRequest, Provider: req.Provider, ModelRequested: req.Model,
+        },
     }
 }
core/providers/azure/images.go (3)

15-47: Structs look reasonable, but consider consistency with OpenAI/Azure response shapes.
One note: AzureImageResponse.Usage is not tagged omitempty, so it’ll marshal as null if absent; confirm Azure always returns usage for images in your targeted API versions.


81-107: Parameter mapping ignores ExtraParams (intentional?).
schemas.ImageGenerationParameters includes ExtraParams map[string]interface{} but it’s not forwarded. If Azure supports provider-specific image params via extra fields, consider plumbing them through (or explicitly documenting they’re ignored for Azure images).


171-229: Streaming auth/header handling: avoid sending empty Bearer tokens; validate request nil.

  • If ctx.Value(AzureAuthorizationTokenKey) exists but is empty, you’ll send Authorization: Bearer and skip api-key; consider authToken != "".
  • Also consider a defensive if request == nil / request.Input == nil early return for symmetry with non-streaming.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bb970dd and d41adc5.

📒 Files selected for processing (53)
  • core/bifrost.go (7 hunks)
  • core/image_generation_tools_test.go (1 hunks)
  • core/images_test.go (1 hunks)
  • core/internal/testutil/account.go (3 hunks)
  • core/internal/testutil/image_generation.go (1 hunks)
  • core/internal/testutil/image_generation_cache.go (1 hunks)
  • core/internal/testutil/image_generation_errors.go (1 hunks)
  • core/internal/testutil/image_generation_fallback.go (1 hunks)
  • core/internal/testutil/image_generation_load.go (1 hunks)
  • core/internal/testutil/image_generation_stream.go (1 hunks)
  • core/internal/testutil/response_validation.go (2 hunks)
  • core/internal/testutil/test_retry_conditions.go (1 hunks)
  • core/internal/testutil/test_retry_framework.go (6 hunks)
  • core/internal/testutil/tests.go (1 hunks)
  • core/internal/testutil/validation_presets.go (2 hunks)
  • core/providers/anthropic/anthropic.go (4 hunks)
  • core/providers/azure/images.go (1 hunks)
  • core/providers/bedrock/bedrock.go (6 hunks)
  • core/providers/cerebras/cerebras.go (1 hunks)
  • core/providers/cohere/cohere.go (3 hunks)
  • core/providers/elevenlabs/elevenlabs.go (3 hunks)
  • core/providers/gemini/gemini.go (4 hunks)
  • core/providers/groq/groq.go (1 hunks)
  • core/providers/mistral/mistral.go (1 hunks)
  • core/providers/ollama/ollama.go (1 hunks)
  • core/providers/openai/images.go (1 hunks)
  • core/providers/openai/openai.go (9 hunks)
  • core/providers/openai/openai_image_test.go (1 hunks)
  • core/providers/openai/openai_test.go (2 hunks)
  • core/providers/openai/types.go (1 hunks)
  • core/providers/openrouter/openrouter.go (1 hunks)
  • core/providers/parasail/parasail.go (1 hunks)
  • core/providers/perplexity/perplexity.go (1 hunks)
  • core/providers/sgl/sgl.go (1 hunks)
  • core/providers/utils/utils.go (3 hunks)
  • core/providers/vertex/vertex.go (1 hunks)
  • core/schemas/bifrost.go (10 hunks)
  • core/schemas/chatcompletions.go (1 hunks)
  • core/schemas/images.go (1 hunks)
  • core/schemas/mux.go (1 hunks)
  • core/schemas/provider.go (1 hunks)
  • core/schemas/responses.go (3 hunks)
  • core/utils.go (1 hunks)
  • framework/streaming/accumulator.go (10 hunks)
  • framework/streaming/images.go (1 hunks)
  • framework/streaming/types.go (5 hunks)
  • plugins/semanticcache/main.go (4 hunks)
  • plugins/semanticcache/search.go (2 hunks)
  • plugins/semanticcache/stream.go (1 hunks)
  • plugins/semanticcache/utils.go (7 hunks)
  • transports/bifrost-http/handlers/inference.go (7 hunks)
  • ui/components/chat/ImageMessage.tsx (1 hunks)
  • ui/hooks/useImageStream.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (19)
  • plugins/semanticcache/stream.go
  • core/providers/groq/groq.go
  • plugins/semanticcache/search.go
  • core/providers/openai/openai_test.go
  • core/utils.go
  • core/schemas/provider.go
  • core/schemas/responses.go
  • core/providers/openai/types.go
  • plugins/semanticcache/main.go
  • core/providers/ollama/ollama.go
  • core/providers/cerebras/cerebras.go
  • core/providers/mistral/mistral.go
  • core/internal/testutil/test_retry_conditions.go
  • framework/streaming/types.go
  • core/internal/testutil/image_generation_cache.go
  • core/providers/parasail/parasail.go
  • core/schemas/chatcompletions.go
  • core/providers/elevenlabs/elevenlabs.go
  • core/image_generation_tools_test.go
🧰 Additional context used
📓 Path-based instructions (1)
**

⚙️ CodeRabbit configuration file

always check the stack if there is one for the current PR. do not give localized reviews for the PR, always see all changes in the light of the whole stack of PRs (if there is a stack, if there is no stack you can continue to make localized suggestions/reviews)

Files:

  • transports/bifrost-http/handlers/inference.go
  • core/providers/openrouter/openrouter.go
  • core/internal/testutil/image_generation_fallback.go
  • core/internal/testutil/validation_presets.go
  • core/images_test.go
  • core/internal/testutil/image_generation_stream.go
  • ui/hooks/useImageStream.ts
  • core/providers/cohere/cohere.go
  • core/providers/perplexity/perplexity.go
  • core/providers/bedrock/bedrock.go
  • core/schemas/images.go
  • core/internal/testutil/image_generation.go
  • core/providers/sgl/sgl.go
  • framework/streaming/images.go
  • core/providers/vertex/vertex.go
  • core/providers/gemini/gemini.go
  • core/internal/testutil/tests.go
  • core/providers/openai/openai.go
  • core/internal/testutil/image_generation_errors.go
  • core/internal/testutil/response_validation.go
  • core/schemas/mux.go
  • ui/components/chat/ImageMessage.tsx
  • core/providers/utils/utils.go
  • core/internal/testutil/account.go
  • core/providers/anthropic/anthropic.go
  • core/providers/openai/openai_image_test.go
  • core/internal/testutil/image_generation_load.go
  • core/providers/azure/images.go
  • plugins/semanticcache/utils.go
  • core/schemas/bifrost.go
  • core/internal/testutil/test_retry_framework.go
  • core/providers/openai/images.go
  • core/bifrost.go
  • framework/streaming/accumulator.go
🧠 Learnings (3)
📚 Learning: 2025-12-09T17:07:42.007Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/schemas/account.go:9-18
Timestamp: 2025-12-09T17:07:42.007Z
Learning: In core/schemas/account.go, the HuggingFaceKeyConfig field within the Key struct is currently unused and reserved for future Hugging Face inference endpoint deployments. Do not flag this field as missing from OpenAPI documentation or require its presence in the API spec until the feature is actively implemented and used. When the feature is added, update the OpenAPI docs accordingly; otherwise, treat this field as non-breaking and not part of the current API surface.

Applied to files:

  • transports/bifrost-http/handlers/inference.go
  • core/providers/openrouter/openrouter.go
  • core/internal/testutil/image_generation_fallback.go
  • core/internal/testutil/validation_presets.go
  • core/images_test.go
  • core/internal/testutil/image_generation_stream.go
  • core/providers/cohere/cohere.go
  • core/providers/perplexity/perplexity.go
  • core/providers/bedrock/bedrock.go
  • core/schemas/images.go
  • core/internal/testutil/image_generation.go
  • core/providers/sgl/sgl.go
  • framework/streaming/images.go
  • core/providers/vertex/vertex.go
  • core/providers/gemini/gemini.go
  • core/internal/testutil/tests.go
  • core/providers/openai/openai.go
  • core/internal/testutil/image_generation_errors.go
  • core/internal/testutil/response_validation.go
  • core/schemas/mux.go
  • core/providers/utils/utils.go
  • core/internal/testutil/account.go
  • core/providers/anthropic/anthropic.go
  • core/providers/openai/openai_image_test.go
  • core/internal/testutil/image_generation_load.go
  • core/providers/azure/images.go
  • plugins/semanticcache/utils.go
  • core/schemas/bifrost.go
  • core/internal/testutil/test_retry_framework.go
  • core/providers/openai/images.go
  • core/bifrost.go
  • framework/streaming/accumulator.go
📚 Learning: 2025-12-12T08:25:02.629Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1000
File: transports/bifrost-http/integrations/router.go:709-712
Timestamp: 2025-12-12T08:25:02.629Z
Learning: In transports/bifrost-http/**/*.go, update streaming response handling to align with OpenAI Responses API: use typed SSE events such as response.created, response.output_text.delta, response.done, etc., and do not rely on the legacy data: [DONE] termination marker. Note that data: [DONE] is only used by the older Chat Completions and Text Completions streaming APIs. Ensure parsers, writers, and tests distinguish SSE events from the [DONE] sentinel and handle each event type accordingly for correct stream termination and progress updates.

Applied to files:

  • transports/bifrost-http/handlers/inference.go
📚 Learning: 2025-12-11T11:58:25.307Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1000
File: core/providers/openai/responses.go:42-84
Timestamp: 2025-12-11T11:58:25.307Z
Learning: In core/providers/openai/responses.go (and related OpenAI response handling), document and enforce the API format constraint: if ResponsesReasoning != nil and the response contains content blocks, all content blocks should be treated as reasoning blocks by default. Implement type guards or parsing logic accordingly, and add unit tests to verify that when ResponsesReasoning is non-nil, content blocks are labeled as reasoning blocks. Include clear comments in the code explaining the rationale and ensure downstream consumers rely on this behavior.

Applied to files:

  • core/providers/openai/openai.go
  • core/providers/openai/openai_image_test.go
  • core/providers/openai/images.go
🧬 Code graph analysis (26)
core/providers/openrouter/openrouter.go (4)
core/schemas/images.go (2)
  • BifrostImageGenerationRequest (4-11)
  • BifrostImageGenerationResponse (33-40)
core/schemas/bifrost.go (4)
  • BifrostError (383-392)
  • ImageGenerationRequest (100-100)
  • BifrostStream (345-353)
  • ImageGenerationStreamRequest (101-101)
core/providers/utils/utils.go (1)
  • NewUnsupportedOperationError (458-469)
core/schemas/provider.go (1)
  • PostHookRunner (279-279)
core/internal/testutil/image_generation_fallback.go (3)
core/internal/testutil/account.go (1)
  • ComprehensiveTestConfig (49-68)
core/schemas/images.go (3)
  • BifrostImageGenerationRequest (4-11)
  • ImageGenerationInput (18-20)
  • ImageGenerationParameters (22-30)
core/schemas/bifrost.go (1)
  • ImageGenerationRequest (100-100)
core/internal/testutil/validation_presets.go (1)
core/internal/testutil/response_validation.go (1)
  • ResponseExpectations (18-43)
core/images_test.go (2)
core/schemas/images.go (7)
  • BifrostImageGenerationRequest (4-11)
  • ImageGenerationInput (18-20)
  • ImageGenerationParameters (22-30)
  • BifrostImageGenerationResponse (33-40)
  • ImageData (42-47)
  • ImageUsage (49-52)
  • BifrostImageGenerationStreamResponse (55-65)
core/schemas/bifrost.go (1)
  • OpenAI (35-35)
core/internal/testutil/image_generation_stream.go (5)
core/internal/testutil/account.go (1)
  • ComprehensiveTestConfig (49-68)
core/internal/testutil/test_retry_framework.go (3)
  • TestRetryContext (174-179)
  • WithImageGenerationStreamRetry (2707-2845)
  • ImageGenerationStreamValidationResult (2697-2703)
core/schemas/images.go (4)
  • BifrostImageGenerationRequest (4-11)
  • ImageGenerationInput (18-20)
  • ImageGenerationParameters (22-30)
  • BifrostImageGenerationStreamResponse (55-65)
core/schemas/bifrost.go (2)
  • BifrostStream (345-353)
  • ImageGenerationStreamRequest (101-101)
core/providers/openai/images.go (1)
  • ImageGenerationCompleted (24-24)
ui/hooks/useImageStream.ts (2)
ui/lib/store/apis/baseApi.ts (1)
  • getTokenFromStorage (10-25)
ui/lib/utils/port.ts (1)
  • getEndpointUrl (117-127)
core/providers/cohere/cohere.go (2)
core/providers/utils/utils.go (3)
  • ProcessAndSendResponse (612-643)
  • GetBifrostResponseForStreamResponse (855-887)
  • NewUnsupportedOperationError (458-469)
core/schemas/bifrost.go (2)
  • BifrostError (383-392)
  • BifrostStream (345-353)
core/providers/perplexity/perplexity.go (4)
core/schemas/images.go (2)
  • BifrostImageGenerationRequest (4-11)
  • BifrostImageGenerationResponse (33-40)
core/schemas/bifrost.go (3)
  • BifrostError (383-392)
  • ImageGenerationRequest (100-100)
  • BifrostStream (345-353)
core/providers/utils/utils.go (1)
  • NewUnsupportedOperationError (458-469)
core/schemas/provider.go (1)
  • PostHookRunner (279-279)
core/providers/bedrock/bedrock.go (4)
core/providers/utils/utils.go (1)
  • ProcessAndSendResponse (612-643)
core/schemas/images.go (2)
  • BifrostImageGenerationRequest (4-11)
  • BifrostImageGenerationResponse (33-40)
core/schemas/bifrost.go (4)
  • BifrostError (383-392)
  • ImageGenerationRequest (100-100)
  • BifrostStream (345-353)
  • ImageGenerationStreamRequest (101-101)
core/schemas/provider.go (1)
  • PostHookRunner (279-279)
core/schemas/images.go (2)
core/schemas/provider.go (1)
  • Provider (282-315)
core/schemas/bifrost.go (4)
  • ModelProvider (32-32)
  • Fallback (136-139)
  • BifrostResponseExtraFields (310-320)
  • BifrostError (383-392)
core/providers/sgl/sgl.go (5)
core/schemas/account.go (1)
  • Key (8-17)
core/schemas/images.go (2)
  • BifrostImageGenerationRequest (4-11)
  • BifrostImageGenerationResponse (33-40)
core/schemas/bifrost.go (3)
  • BifrostError (383-392)
  • ImageGenerationRequest (100-100)
  • ImageGenerationStreamRequest (101-101)
core/providers/utils/utils.go (1)
  • NewUnsupportedOperationError (458-469)
core/schemas/provider.go (1)
  • PostHookRunner (279-279)
framework/streaming/images.go (3)
framework/streaming/types.go (1)
  • ImageStreamChunk (104-114)
core/schemas/images.go (3)
  • BifrostImageGenerationResponse (33-40)
  • ImageData (42-47)
  • BifrostImageGenerationStreamResponse (55-65)
core/utils.go (2)
  • GetResponseFields (216-223)
  • IsFinalChunk (198-213)
core/providers/vertex/vertex.go (4)
core/schemas/images.go (2)
  • BifrostImageGenerationRequest (4-11)
  • BifrostImageGenerationResponse (33-40)
core/schemas/bifrost.go (2)
  • BifrostError (383-392)
  • ImageGenerationRequest (100-100)
core/providers/utils/utils.go (1)
  • NewUnsupportedOperationError (458-469)
core/schemas/provider.go (1)
  • PostHookRunner (279-279)
core/providers/gemini/gemini.go (2)
core/providers/utils/utils.go (3)
  • ProcessAndSendResponse (612-643)
  • GetBifrostResponseForStreamResponse (855-887)
  • NewUnsupportedOperationError (458-469)
core/schemas/bifrost.go (2)
  • BifrostError (383-392)
  • BifrostStream (345-353)
core/internal/testutil/tests.go (6)
core/internal/testutil/image_generation.go (1)
  • RunImageGenerationTest (13-126)
core/internal/testutil/image_generation_stream.go (1)
  • RunImageGenerationStreamTest (17-133)
core/internal/testutil/image_generation_fallback.go (1)
  • RunImageGenerationFallbackTest (14-80)
core/internal/testutil/image_generation_cache.go (1)
  • RunImageGenerationCacheTest (14-140)
core/internal/testutil/image_generation_errors.go (1)
  • RunImageGenerationErrorTest (14-154)
core/internal/testutil/image_generation_load.go (3)
  • RunImageGenerationLoadTest (16-101)
  • RunImageGenerationStreamLoadTest (104-211)
  • RunImageGenerationCacheLoadTest (214-307)
core/providers/openai/openai.go (3)
core/providers/utils/utils.go (2)
  • ProcessAndSendResponse (612-643)
  • GetBifrostResponseForStreamResponse (855-887)
core/schemas/bifrost.go (3)
  • BifrostError (383-392)
  • ImageGenerationRequest (100-100)
  • BifrostStream (345-353)
core/schemas/provider.go (2)
  • Provider (282-315)
  • PostHookRunner (279-279)
core/internal/testutil/image_generation_errors.go (2)
core/internal/testutil/account.go (1)
  • ComprehensiveTestConfig (49-68)
core/schemas/images.go (3)
  • BifrostImageGenerationRequest (4-11)
  • ImageGenerationInput (18-20)
  • ImageGenerationParameters (22-30)
core/internal/testutil/response_validation.go (3)
core/schemas/images.go (1)
  • BifrostImageGenerationResponse (33-40)
core/internal/testutil/error_parser.go (3)
  • ParseBifrostError (47-104)
  • FormatErrorConcise (353-356)
  • LogError (359-366)
plugins/mocker/main.go (1)
  • Latency (125-129)
core/schemas/mux.go (3)
core/providers/gemini/types.go (1)
  • Type (780-780)
core/schemas/chatcompletions.go (1)
  • ChatToolTypeImageGeneration (241-241)
core/schemas/responses.go (1)
  • ResponsesToolImageGeneration (1336-1347)
core/providers/utils/utils.go (1)
core/schemas/images.go (1)
  • BifrostImageGenerationStreamResponse (55-65)
core/internal/testutil/account.go (1)
core/schemas/bifrost.go (1)
  • Fallback (136-139)
core/providers/azure/images.go (7)
core/providers/azure/azure.go (2)
  • AzureProvider (22-28)
  • AzureAuthorizationTokenKey (19-19)
core/schemas/images.go (6)
  • BifrostImageGenerationRequest (4-11)
  • BifrostImageGenerationResponse (33-40)
  • ImageGenerationParameters (22-30)
  • ImageData (42-47)
  • ImageUsage (49-52)
  • BifrostImageGenerationStreamResponse (55-65)
core/schemas/bifrost.go (5)
  • BifrostError (383-392)
  • ImageGenerationRequest (100-100)
  • BifrostResponseExtraFields (310-320)
  • Azure (36-36)
  • BifrostStream (345-353)
core/providers/openai/images.go (1)
  • ToBifrostImageResponse (62-96)
core/schemas/provider.go (2)
  • Provider (282-315)
  • PostHookRunner (279-279)
core/utils.go (1)
  • Ptr (51-53)
core/providers/azure/types.go (1)
  • AzureAPIVersionDefault (4-4)
core/schemas/bifrost.go (9)
core/schemas/models.go (2)
  • BifrostListModelsRequest (23-34)
  • Model (109-129)
core/schemas/textcompletions.go (2)
  • BifrostTextCompletionRequest (10-17)
  • BifrostTextCompletionResponse (69-77)
core/schemas/chatcompletions.go (2)
  • BifrostChatRequest (12-19)
  • BifrostChatResponse (26-41)
core/schemas/responses.go (3)
  • BifrostResponsesRequest (32-39)
  • BifrostResponsesResponse (45-85)
  • BifrostResponsesStreamResponse (1452-1491)
core/schemas/embedding.go (2)
  • BifrostEmbeddingRequest (9-16)
  • BifrostEmbeddingResponse (22-28)
core/schemas/speech.go (2)
  • BifrostSpeechRequest (9-16)
  • BifrostSpeechResponse (22-29)
core/schemas/transcriptions.go (2)
  • BifrostTranscriptionRequest (3-10)
  • BifrostTranscriptionResponse (16-26)
core/schemas/images.go (2)
  • BifrostImageGenerationRequest (4-11)
  • BifrostImageGenerationResponse (33-40)
core/schemas/provider.go (1)
  • Provider (282-315)
core/internal/testutil/test_retry_framework.go (5)
core/schemas/images.go (1)
  • BifrostImageGenerationResponse (33-40)
core/internal/testutil/test_retry_conditions.go (2)
  • EmptyImageGenerationCondition (851-851)
  • GenericResponseCondition (370-370)
core/internal/testutil/response_validation.go (2)
  • ResponseExpectations (18-43)
  • ValidateImageGenerationResponse (241-275)
core/internal/testutil/utils.go (1)
  • GetErrorMessage (606-639)
core/schemas/bifrost.go (1)
  • BifrostStream (345-353)
core/providers/openai/images.go (4)
core/providers/openai/types.go (3)
  • OpenAIImageGenerationRequest (292-302)
  • OpenAIImageGenerationResponse (305-317)
  • OpenAIImageStreamResponse (331-336)
core/schemas/bifrost.go (8)
  • BifrostResponseExtraFields (310-320)
  • OpenAI (35-35)
  • BifrostError (383-392)
  • ImageGenerationRequest (100-100)
  • ModelProvider (32-32)
  • ErrorField (401-408)
  • BifrostErrorExtraFields (450-454)
  • RequestType (83-83)
core/providers/utils/utils.go (1)
  • SetExtraHeaders (179-209)
core/providers/openai/errors.go (1)
  • ParseOpenAIError (10-42)
framework/streaming/accumulator.go (2)
framework/streaming/types.go (4)
  • ImageStreamChunk (104-114)
  • StreamAccumulator (117-129)
  • ChatStreamChunk (78-88)
  • ResponsesStreamChunk (91-101)
core/schemas/bifrost.go (1)
  • ImageGenerationStreamRequest (101-101)
🔇 Additional comments (33)
core/providers/vertex/vertex.go (1)

1409-1417: ImageGeneration/ImageGenerationStream stubs are consistent with “unsupported provider” pattern.

core/providers/perplexity/perplexity.go (1)

255-263: Perplexity ImageGeneration/ImageGenerationStream stubs look correct (and request types match).

core/providers/gemini/gemini.go (2)

1424-1432: Gemini ImageGeneration/ImageGenerationStream stubs look correct (and stream uses ImageGenerationStreamRequest).


1115-1142: Unable to verify: The specified file and function references could not be located in the repository.

The review comment references core/providers/gemini/gemini.go with specific line ranges (1115-1142, 1385-1418) and claims that GetBifrostResponseForStreamResponse calls have inconsistent arity (5 vs 6 arguments). However, these files and function references could not be found during verification. Please confirm:

  • The correct file path for the Gemini provider implementation
  • The actual function name and its current signature
  • Whether these changes have been committed to the branch

Once confirmed, the arity mismatch can be properly verified.

ui/hooks/useImageStream.ts (1)

217-253: Backend sends single-line JSON only—no multi-line data blocks to handle.

The backend uses sonic.Marshal() for compact JSON serialization (line 1073, inference.go) and writes responses as data: {json}\n\n in a single line (lines 1096, 1102). Image generation streams do not receive event: lines at all (only BifrostResponsesStreamResponse sets includeEventType = true). The frontend code correctly handles the actual format the backend sends; multi-line SSE payloads are not a concern here.

plugins/semanticcache/utils.go (1)

175-179: Embedding text extraction for image prompts looks good; keep metadata+prompt consistent.

Line 337-344’s prompt validation + normalization is a solid safeguard for cache/embedding consistency.

Also applies to: 336-345

core/providers/anthropic/anthropic.go (2)

1027-1035: ImageGeneration and ImageGenerationStream method signatures correctly match the Provider interface.

Both method signatures in AnthropicProvider implementation are properly aligned with the Provider interface definitions. The ImageGeneration and ImageGenerationStream methods have the correct parameter order and return types, and returning NewUnsupportedOperationError for unimplemented operations is the appropriate pattern.


632-633: All GetBifrostResponseForStreamResponse(...) parameter positions are correct and semantically appropriate.

The function signature has 6 parameters: textCompletionResponse, chatResponse, responsesStreamResponse, speechStreamResponse, transcriptionStreamResponse, imageGenerationStreamResponse. All callsites correctly map their response objects to the appropriate parameter position—chat responses to position 2 (lines 632, 662) and responses stream responses to position 3 (lines 981, 984). The new imageGenerationStreamResponse parameter is properly set to nil across all existing callsites.

core/internal/testutil/test_retry_framework.go (1)

926-940: No nil-safety bug exists here. Both EmptyImageGenerationCondition and GenericResponseCondition explicitly handle nil responses: the former returns true, "response is nil" (line 856), and the latter returns false, "" (line 372-374). The checkStreamRetryConditions function intentionally passes nil for streaming operations, as documented in its comment (line 718): "Pass nil response since streaming doesn't have a single response." This design is safe and deliberate.

Likely an incorrect or invalid review comment.

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

632-632: LGTM! Image generation streaming support correctly integrated.

The changes consistently extend the streaming infrastructure to support image generation:

  • Line 632 properly propagates ImageGenerationStreamResponse in ProcessAndSendResponse
  • Line 861 extends GetBifrostResponseForStreamResponse signature with the new parameter
  • Lines 882-884 add the corresponding case handling

This follows the established pattern used for other streaming response types (speech, transcription, etc.).

Also applies to: 861-861, 882-884

core/providers/openrouter/openrouter.go (1)

285-293: LGTM! Unsupported operations correctly declared.

Both ImageGeneration and ImageGenerationStream methods appropriately return unsupported operation errors using NewUnsupportedOperationError, consistent with other unsupported operations in the OpenRouter provider (lines 262, 267, 272, 277, 282).

core/providers/cohere/cohere.go (2)

486-486: LGTM! Streaming calls correctly updated with new parameter.

Lines 486, 489, 728, and 731 correctly add the sixth parameter (nil for imageGenerationStreamResponse) to GetBifrostResponseForStreamResponse calls, aligning with the extended signature in core/providers/utils/utils.go.

Also applies to: 489-489, 728-728, 731-731


821-829: LGTM! Unsupported image generation operations correctly declared.

Both ImageGeneration and ImageGenerationStream methods appropriately return unsupported operation errors, consistent with other unsupported operations in the Cohere provider (lines 802-819).

core/internal/testutil/image_generation.go (1)

24-79: LGTM! Retry framework correctly integrated.

The test properly uses the retry framework as requested in past review comments:

  • Line 24: GetTestRetryConfigForScenario to get retry configuration
  • Lines 39-46: Sets up ImageGenerationRetryConfig with retry handlers
  • Line 79: WithImageGenerationRetry wrapper wraps the operation

This addresses the previous concern about flaky tests in CI.

core/schemas/images.go (1)

3-65: LGTM! Image generation schemas well-structured.

The schema definitions are well-designed and consistent:

  • BifrostImageGenerationRequest follows the pattern of other Bifrost request types (includes Provider, Model, Input, Params, Fallbacks, RawRequestBody)
  • GetRawRequestBody() implements the RequestBodyGetter interface correctly (lines 14-16)
  • Parameter struct (lines 22-30) includes all standard OpenAI image generation parameters (n, size, quality, style, response_format, user)
  • Response structs (lines 33-52) align with OpenAI's response format
  • Streaming response (lines 55-65) properly supports chunked delivery with type, index, chunk_index, partial base64, and usage fields
  • All JSON tags follow Go conventions
core/internal/testutil/image_generation_fallback.go (1)

14-80: Add retry framework wrapper to fallback test.

Based on past review comments, all new tests should use the retry framework to avoid flakiness in CI. This test currently executes the request directly (line 46) without retry logic.

Wrap the request execution with the retry framework:

 	t.Run("ImageGenerationFallback", func(t *testing.T) {
 		if os.Getenv("SKIP_PARALLEL_TESTS") != "true" {
 			t.Parallel()
 		}
 
+		retryConfig := GetTestRetryConfigForScenario("ImageGenerationFallback", testConfig)
+		retryContext := TestRetryContext{
+			ScenarioName: "ImageGenerationFallback",
+			ExpectedBehavior: map[string]interface{}{
+				"fallback_enabled": true,
+			},
+			TestMetadata: map[string]interface{}{
+				"provider": testConfig.Provider,
+				"model":    testConfig.ImageGenerationModel,
+			},
+		}
+
+		imageGenerationRetryConfig := ImageGenerationRetryConfig{
+			MaxAttempts: retryConfig.MaxAttempts,
+			BaseDelay:   retryConfig.BaseDelay,
+			MaxDelay:    retryConfig.MaxDelay,
+			Conditions:  []ImageGenerationRetryCondition{},
+			OnRetry:     retryConfig.OnRetry,
+			OnFinalFail: retryConfig.OnFinalFail,
+		}
+
 		// Create request with primary provider that should fail, triggering fallback
-		// Note: This test assumes the primary provider will fail (e.g., invalid key)
-		// In practice, you might need to configure a failing provider for this test
 		request := &schemas.BifrostImageGenerationRequest{
 			Provider: testConfig.Provider,
 			Model:    testConfig.ImageGenerationModel,
 			Input: &schemas.ImageGenerationInput{
 				Prompt: "A beautiful mountain landscape at dawn",
 			},
 			Params: &schemas.ImageGenerationParameters{
 				Size:           bifrost.Ptr("1024x1024"),
 				ResponseFormat: bifrost.Ptr("b64_json"),
 			},
 			Fallbacks: testConfig.ImageGenerationFallbacks,
 		}
 
-		response, bifrostErr := client.ImageGenerationRequest(ctx, request)
+		response, bifrostErr := WithImageGenerationRetry(t, imageGenerationRetryConfig, retryContext, GetExpectationsForScenario("ImageGenerationFallback", testConfig, map[string]interface{}{}), "ImageGenerationFallback", func() (*schemas.BifrostImageGenerationResponse, *schemas.BifrostError) {
+			return client.ImageGenerationRequest(ctx, request)
+		})

Likely an incorrect or invalid review comment.

core/providers/bedrock/bedrock.go (2)

667-668: Updated streaming wrapper arity looks consistent with new GetBifrostResponseForStreamResponse signature.

Also applies to: 894-909, 1066-1067, 1160-1161


1286-1294: Bedrock image generation “unsupported” stubs are fine for interface parity.

core/providers/sgl/sgl.go (1)

223-231: SGL “unsupported image generation” stubs look good for provider-surface consistency.

framework/streaming/images.go (1)

164-172: Bug: FinishReason assignment is incorrectly gated by SemanticCacheDebug != nil.
Finish reason should be set independently of cache debug presence.

@@
-    if len(acc.ImageStreamChunks) > 0 {
-        lastChunk := acc.ImageStreamChunks[len(acc.ImageStreamChunks)-1]
-        if lastChunk.SemanticCacheDebug != nil {
-            data.CacheDebug = lastChunk.SemanticCacheDebug
-        }
-        data.FinishReason = lastChunk.FinishReason
-    }
+    if len(acc.ImageStreamChunks) > 0 {
+        lastChunk := acc.ImageStreamChunks[len(acc.ImageStreamChunks)-1]
+        if lastChunk.SemanticCacheDebug != nil {
+            data.CacheDebug = lastChunk.SemanticCacheDebug
+        }
+        data.FinishReason = lastChunk.FinishReason
+    }

Likely an incorrect or invalid review comment.

core/internal/testutil/response_validation.go (2)

240-275: Nice parity with other validators (error handling + metrics + logging).


1064-1109: Fix potential nil-map panic when ProviderSpecific is unset. expectations.ProviderSpecific["..."] will panic if ProviderSpecific == nil.

 func validateImageGenerationFields(t *testing.T, response *schemas.BifrostImageGenerationResponse, expectations ResponseExpectations, result *ValidationResult) {
@@
 	// Check minimum number of images if specified
-	if minImages, ok := expectations.ProviderSpecific["min_images"].(int); ok {
-		actualCount := len(response.Data)
-		if actualCount < minImages {
-			result.Passed = false
-			result.Errors = append(result.Errors,
-				fmt.Sprintf("Too few images: got %d, expected at least %d", actualCount, minImages))
-		} else {
-			result.MetricsCollected["image_count"] = actualCount
-		}
-	}
+	if expectations.ProviderSpecific != nil {
+		if minImages, ok := expectations.ProviderSpecific["min_images"].(int); ok {
+			actualCount := len(response.Data)
+			if actualCount < minImages {
+				result.Passed = false
+				result.Errors = append(result.Errors,
+					fmt.Sprintf("Too few images: got %d, expected at least %d", actualCount, minImages))
+			} else {
+				result.MetricsCollected["image_count"] = actualCount
+			}
+		}
+	}
@@
 	// Validate image size if specified
-	if expectedSize, ok := expectations.ProviderSpecific["expected_size"].(string); ok {
-		result.MetricsCollected["expected_size"] = expectedSize
-		// Note: Actual size validation would require downloading/decoding images
-	}
+	if expectations.ProviderSpecific != nil {
+		if expectedSize, ok := expectations.ProviderSpecific["expected_size"].(string); ok {
+			result.MetricsCollected["expected_size"] = expectedSize
+			// Note: Actual size validation would require downloading/decoding images
+		}
+	}

Likely an incorrect or invalid review comment.

framework/streaming/accumulator.go (1)

468-515: Confirm Go version compatibility for for range 1000. This requires Go 1.22+. If the repo isn’t on 1.22+, use a traditional loop.

- for range 1000 {
+ for i := 0; i < 1000; i++ {
     a.chatStreamChunkPool.Put(&ChatStreamChunk{})
     a.responsesStreamChunkPool.Put(&ResponsesStreamChunk{})
     a.audioStreamChunkPool.Put(&AudioStreamChunk{})
     a.transcriptionStreamChunkPool.Put(&TranscriptionStreamChunk{})
     a.imageStreamChunkPool.Put(&ImageStreamChunk{})
   }
core/schemas/bifrost.go (1)

141-307: Schema wiring for image generation request/response/stream looks consistent with existing modalities.

Verification ask: please ensure the image generation response path consistently populates BifrostResponseExtraFields.RequestType (and ModelRequested if expected) the same way other modalities do.

Also applies to: 343-375

core/providers/openai/images.go (1)

20-31: Go version check: maps.Copy requires Go 1.21+. Please confirm repo toolchain supports this.

core/bifrost.go (5)

17-17: Confirm github.com/bytedance/sonic behavior matches expectations (number decoding, map semantics).
You rely on sonic.Unmarshal into map[string]interface{} and then do type switches (float64/int/int64). Please confirm sonic’s decoding rules here match your assumptions (especially for integers and large values).


2006-2011: Fallback propagation for ImageGenerationRequest looks correct.
Cloning the request and overriding provider/model mirrors the other request types.


2748-2754: Provider routing for ImageGenerationRequest looks fine.
Straight-through call to provider.ImageGeneration and wiring into response.ImageGenerationResponse matches existing patterns.


2784-2786: Streaming routing for ImageGenerationStreamRequest looks fine.
Routes to provider.ImageGenerationStream and passes the post-hook runner consistently with other stream request types.


2962-2963: Good: pooled request reset clears ImageGenerationRequest.
Prevents cross-request leakage when reusing schemas.BifrostRequest from the pool.

core/providers/azure/images.go (3)

52-79: Non-streaming ImageGeneration flow is clean and sets useful ExtraFields.
Validation → transform → DoRequest → map response mirrors other providers well.


109-145: Response mapping: double-check token field semantics.
You map PromptTokens = InputTokens and TotalTokens = TotalTokens. If Azure’s image usage differs from OpenAI’s (or doesn’t provide these fields consistently), ensure this doesn’t mislead callers.


146-169: DoRequest path looks fine; ensure completeRequest matches Azure images requirements.
Given Azure images typically use deployment-based routing, confirm provider.completeRequest(..., "images/generations", ..., azureRequest.Model, ...) selects the correct deployment/model for images (and doesn’t require a different path or payload shape).

Copy link
Collaborator

Hey @Radheshg04, PR looks solid but few checkboxes before we merge this

  • Resolve all the comments
  • Add to core/changelog.md, frameworks/changelog.md, plugins/semanticcace/changelog.md and transports/changelog.md about there respective changes
  • Add a case for image gen handlers in transports/integrations
  • Update the docs in https://docs.getbifrost.ai/quickstart/gateway/multimodal, https://docs.getbifrost.ai/quickstart/go-sdk/multimodal and https://docs.getbifrost.ai/features/unified-interface#provider-support-matrix

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

🧹 Nitpick comments (12)
core/providers/azure/types.go (1)

49-69: Consider extracting anonymous struct for better reusability.

The Data field uses an anonymous struct. If this data structure is used elsewhere (e.g., in tests or response handling), extracting it as a named type improves maintainability.

+// AzureImageData represents a single image result from Azure.
+type AzureImageData struct {
+	URL           string `json:"url,omitempty"`
+	B64JSON       string `json:"b64_json,omitempty"`
+	RevisedPrompt string `json:"revised_prompt,omitempty"`
+}
+
 // AzureImageResponse is the struct for Image Generation responses by Azure.
 type AzureImageResponse struct {
 	Created int64 `json:"created"`
-	Data    []struct {
-		URL           string `json:"url,omitempty"`
-		B64JSON       string `json:"b64_json,omitempty"`
-		RevisedPrompt string `json:"revised_prompt,omitempty"`
-	} `json:"data"`
+	Data    []AzureImageData `json:"data"`
 	Usage *AzureImageGenerationUsage `json:"usage"`
 }
ui/hooks/useImageStream.ts (2)

173-179: options in dependency array may cause unnecessary re-renders.

If options object is recreated on each render (common pattern), processChunk will be recreated, potentially causing stale closure issues or unnecessary work.

Consider memoizing callbacks from options or using refs:

+  const onCompleteRef = useRef(options.onComplete);
+  const onErrorRef = useRef(options.onError);
+
+  useEffect(() => {
+    onCompleteRef.current = options.onComplete;
+    onErrorRef.current = options.onError;
+  }, [options.onComplete, options.onError]);

   // In processChunk:
-        options.onError?.(errorMsg);
+        onErrorRef.current?.(errorMsg);

251-256: Silent failure on JSON parse errors may hide issues.

Parse errors are logged but not surfaced to the user. Consider whether malformed chunks should trigger error state.

               try {
                 const chunk: ImageStreamChunk = JSON.parse(data);
                 processChunk(chunk);
               } catch (parseError) {
                 console.error("Failed to parse SSE chunk:", parseError);
+                // Optionally: count errors and fail if too many
               }
framework/streaming/images.go (1)

14-92: Guard against nil *ImageStreamChunk entries before sorting.
sort.Slice dereferences chunks[i]/chunks[j]; a nil entry would panic. If nils are impossible by construction, a short comment/assert would still help.

 func (a *Accumulator) buildCompleteImageFromImageStreamChunks(chunks []*ImageStreamChunk) *schemas.BifrostImageGenerationResponse {
+    // Defensive: drop nil entries to avoid panics in sort/comparators.
+    filtered := chunks[:0]
+    for _, c := range chunks {
+        if c != nil {
+            filtered = append(filtered, c)
+        }
+    }
+    chunks = filtered
+
     // Sort chunks by ImageIndex, then ChunkIndex
     sort.Slice(chunks, func(i, j int) bool {
         if chunks[i].ImageIndex != chunks[j].ImageIndex {
             return chunks[i].ImageIndex < chunks[j].ImageIndex
         }
         return chunks[i].ChunkIndex < chunks[j].ChunkIndex
     })
core/providers/azure/azure.go (1)

846-908: Azure ImageGenerationStream: consider aligning deployment lookup helper.
This uses deployment := key.AzureKeyConfig.Deployments[request.Model] while non-streaming uses getModelDeployment. Not wrong, but it’s easy to drift; consider using getModelDeployment in both for consistent error messages and future behavior.

core/providers/openai/openai.go (1)

2156-2231: Non-streaming ImageGeneration: consider CheckAndDecodeBody for parity with other ops.
Most other operations decode body (gzip, etc.) via providerUtils.CheckAndDecodeBody; here you unmarshal httpResp.Body() directly. This can be fine, but it’s an inconsistency that can bite if compression is enabled.

core/schemas/bifrost.go (2)

185-202: Set helpers: ok, but consider a defensive “else” for misconfigured requests.*
If multiple request pointers are ever set, these switch { case ... != nil } helpers will silently update only the first match. Optional: add a debug log or panic in dev builds to catch invariant violations.

Also applies to: 204-221, 223-240, 242-259


263-305: GetExtraFields(): good extension, but returning a new empty struct can hide bugs.
Returning &BifrostResponseExtraFields{} makes it hard to detect “no response payload set” issues; consider returning nil (and handle nil at callsites) or logging when no known response is present.

core/providers/openai/images.go (1)

24-50: Request mapping is fine; consider passing through provider-specific extra params if intended.
mapImageParams ignores schemas.ImageGenerationParameters.ExtraParams (if OpenAI supports any additional image params in your gateway). If that’s intentional, ok; otherwise you may need an allowlist mapping to OpenAI request fields.

framework/streaming/accumulator.go (1)

382-413: Use putImageStreamChunk (and other put helpers) in Cleanup() for consistency.*
Cleanup() currently returns chunks via pool.Put directly; if Cleanup() is ever called and the accumulator reused, stale fields could bleed. Low risk if Cleanup() is strictly shutdown-only, but easy to make consistent.

- for _, chunk := range accumulator.ImageStreamChunks {
-     a.imageStreamChunkPool.Put(chunk)
- }
+ for _, chunk := range accumulator.ImageStreamChunks {
+     a.putImageStreamChunk(chunk)
+ }
core/bifrost.go (2)

870-935: Public ImageGeneration APIs: wire-up looks correct; add ExtraFields on validation errors for consistency.*
Right now nil/empty-prompt errors don’t include BifrostErrorExtraFields{RequestType, Provider, ModelRequested} like other public APIs do. This makes client-side debugging and log correlation harder.

 if req == nil {
   return nil, &schemas.BifrostError{
     IsBifrostError: false,
     Error: &schemas.ErrorField{ Message: "image generation request is nil" },
+    ExtraFields: schemas.BifrostErrorExtraFields{
+      RequestType: schemas.ImageGenerationRequest,
+    },
   }
 }
 if req.Input == nil || req.Input.Prompt == "" {
   return nil, &schemas.BifrostError{
     IsBifrostError: false,
     Error: &schemas.ErrorField{ Message: "prompt not provided for image generation request" },
+    ExtraFields: schemas.BifrostErrorExtraFields{
+      RequestType:    schemas.ImageGenerationRequest,
+      Provider:       req.Provider,
+      ModelRequested: req.Model,
+    },
   }
 }

870-911: Nit: grammar in doc comments (“a image” -> “an image”).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d41adc5 and 3738f96.

📒 Files selected for processing (19)
  • core/bifrost.go (5 hunks)
  • core/images_test.go (1 hunks)
  • core/internal/testutil/image_generation.go (1 hunks)
  • core/internal/testutil/image_generation_load.go (1 hunks)
  • core/providers/azure/azure.go (2 hunks)
  • core/providers/azure/images.go (1 hunks)
  • core/providers/azure/types.go (1 hunks)
  • core/providers/gemini/gemini.go (8 hunks)
  • core/providers/openai/images.go (1 hunks)
  • core/providers/openai/openai.go (10 hunks)
  • core/providers/openai/openai_image_test.go (1 hunks)
  • core/providers/openai/types.go (1 hunks)
  • core/providers/vertex/vertex.go (1 hunks)
  • core/schemas/bifrost.go (10 hunks)
  • framework/streaming/accumulator.go (10 hunks)
  • framework/streaming/images.go (1 hunks)
  • plugins/semanticcache/utils.go (7 hunks)
  • transports/bifrost-http/handlers/inference.go (7 hunks)
  • ui/hooks/useImageStream.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • core/providers/openai/openai_image_test.go
  • core/providers/openai/types.go
  • transports/bifrost-http/handlers/inference.go
  • core/providers/vertex/vertex.go
🧰 Additional context used
📓 Path-based instructions (1)
**

⚙️ CodeRabbit configuration file

always check the stack if there is one for the current PR. do not give localized reviews for the PR, always see all changes in the light of the whole stack of PRs (if there is a stack, if there is no stack you can continue to make localized suggestions/reviews)

Files:

  • core/providers/gemini/gemini.go
  • framework/streaming/images.go
  • framework/streaming/accumulator.go
  • core/providers/azure/images.go
  • core/internal/testutil/image_generation.go
  • core/schemas/bifrost.go
  • core/internal/testutil/image_generation_load.go
  • core/providers/azure/azure.go
  • plugins/semanticcache/utils.go
  • core/bifrost.go
  • core/images_test.go
  • core/providers/openai/images.go
  • core/providers/azure/types.go
  • ui/hooks/useImageStream.ts
  • core/providers/openai/openai.go
🧠 Learnings (2)
📚 Learning: 2025-12-09T17:07:42.007Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/schemas/account.go:9-18
Timestamp: 2025-12-09T17:07:42.007Z
Learning: In core/schemas/account.go, the HuggingFaceKeyConfig field within the Key struct is currently unused and reserved for future Hugging Face inference endpoint deployments. Do not flag this field as missing from OpenAPI documentation or require its presence in the API spec until the feature is actively implemented and used. When the feature is added, update the OpenAPI docs accordingly; otherwise, treat this field as non-breaking and not part of the current API surface.

Applied to files:

  • core/providers/gemini/gemini.go
  • framework/streaming/images.go
  • framework/streaming/accumulator.go
  • core/providers/azure/images.go
  • core/internal/testutil/image_generation.go
  • core/schemas/bifrost.go
  • core/internal/testutil/image_generation_load.go
  • core/providers/azure/azure.go
  • plugins/semanticcache/utils.go
  • core/bifrost.go
  • core/images_test.go
  • core/providers/openai/images.go
  • core/providers/azure/types.go
  • core/providers/openai/openai.go
📚 Learning: 2025-12-11T11:58:25.307Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1000
File: core/providers/openai/responses.go:42-84
Timestamp: 2025-12-11T11:58:25.307Z
Learning: In core/providers/openai/responses.go (and related OpenAI response handling), document and enforce the API format constraint: if ResponsesReasoning != nil and the response contains content blocks, all content blocks should be treated as reasoning blocks by default. Implement type guards or parsing logic accordingly, and add unit tests to verify that when ResponsesReasoning is non-nil, content blocks are labeled as reasoning blocks. Include clear comments in the code explaining the rationale and ensure downstream consumers rely on this behavior.

Applied to files:

  • core/providers/openai/images.go
  • core/providers/openai/openai.go
🧬 Code graph analysis (8)
framework/streaming/images.go (3)
framework/streaming/types.go (1)
  • ImageStreamChunk (104-114)
core/schemas/images.go (3)
  • BifrostImageGenerationResponse (33-40)
  • ImageData (42-47)
  • BifrostImageGenerationStreamResponse (55-65)
core/utils.go (2)
  • GetResponseFields (216-223)
  • IsFinalChunk (198-213)
core/schemas/bifrost.go (4)
ui/lib/types/config.ts (1)
  • RequestType (122-134)
core/schemas/models.go (2)
  • BifrostListModelsRequest (23-34)
  • Model (109-129)
core/schemas/images.go (3)
  • BifrostImageGenerationRequest (4-11)
  • BifrostImageGenerationResponse (33-40)
  • BifrostImageGenerationStreamResponse (55-65)
core/schemas/provider.go (1)
  • Provider (282-315)
core/internal/testutil/image_generation_load.go (3)
core/internal/testutil/account.go (1)
  • ComprehensiveTestConfig (49-68)
core/schemas/images.go (3)
  • BifrostImageGenerationRequest (4-11)
  • ImageGenerationInput (18-20)
  • ImageGenerationParameters (22-30)
core/schemas/bifrost.go (2)
  • ImageGenerationRequest (98-98)
  • ImageGenerationStreamRequest (99-99)
plugins/semanticcache/utils.go (2)
core/schemas/bifrost.go (3)
  • RequestType (83-83)
  • ImageGenerationRequest (98-98)
  • ImageGenerationStreamRequest (99-99)
core/schemas/images.go (2)
  • ImageGenerationInput (18-20)
  • ImageGenerationParameters (22-30)
core/bifrost.go (2)
core/schemas/bifrost.go (6)
  • ImageGenerationRequest (98-98)
  • BifrostError (381-390)
  • RequestType (83-83)
  • ImageGenerationStreamRequest (99-99)
  • BifrostStream (343-351)
  • BifrostRequest (150-161)
core/schemas/images.go (2)
  • BifrostImageGenerationRequest (4-11)
  • BifrostImageGenerationResponse (33-40)
core/images_test.go (2)
core/schemas/images.go (7)
  • BifrostImageGenerationRequest (4-11)
  • ImageGenerationInput (18-20)
  • ImageGenerationParameters (22-30)
  • BifrostImageGenerationResponse (33-40)
  • ImageData (42-47)
  • ImageUsage (49-52)
  • BifrostImageGenerationStreamResponse (55-65)
core/schemas/bifrost.go (1)
  • OpenAI (35-35)
core/providers/openai/images.go (2)
core/providers/openai/types.go (2)
  • OpenAIImageGenerationRequest (294-304)
  • OpenAIImageGenerationResponse (307-319)
core/schemas/images.go (5)
  • BifrostImageGenerationRequest (4-11)
  • ImageGenerationParameters (22-30)
  • BifrostImageGenerationResponse (33-40)
  • ImageData (42-47)
  • ImageUsage (49-52)
ui/hooks/useImageStream.ts (2)
ui/lib/store/apis/baseApi.ts (1)
  • getTokenFromStorage (10-25)
ui/lib/utils/port.ts (1)
  • getEndpointUrl (117-127)
🔇 Additional comments (34)
core/images_test.go (5)

14-98: Well-structured serialization tests with proper error handling.

The test cases cover both full request serialization and minimal request omission of nil fields. Error handling improvements from past reviews have been incorporated correctly with t.Fatalf on unmarshal failures and safe type assertions with ok checks.


100-167: Deserialization tests cover key scenarios appropriately.

Good coverage of valid JSON parsing, invalid JSON error handling, and missing optional fields behavior. The wantErr pattern is properly implemented.


207-280: Streaming response serialization tests are thorough.

Tests correctly verify both partial chunks and completed chunks with usage data. Type assertions include proper ok checks and t.Fatalf calls on failure.


282-417: Stream chunk accumulation tests effectively validate reconstruction logic.

The tests cover single-image multi-chunk, multi-image parallel chunks, and out-of-order chunk sorting. This aligns well with the streaming accumulator requirements in the PR objectives.


477-502: Cache key generation helper correctly uses fmt.Sprintf for integer conversion.

The previous issue with string(rune(*req.Params.N)) has been addressed by using fmt.Sprintf("%d", *req.Params.N) on line 497.

plugins/semanticcache/utils.go (4)

87-94: Image generation hash handling correctly short-circuits for dedicated cache key.

The special handling for ImageGenerationRequest and ImageGenerationStreamRequest appropriately returns a dedicated image cache key. The nil check on line 90 prevents potential panics.

Note: Past review comment about typo "styele" appears to be addressed - the comment now correctly reads "style".


175-179: Image generation parameter extraction follows established patterns.

Consistent with other request type handlers in the switch statement.


336-345: Image generation text extraction correctly normalizes the prompt.

The implementation properly handles the nil checks and returns the normalized prompt for embedding generation.


960-986: Image generation parameter extraction is complete and follows conventions.

All parameters from ImageGenerationParameters struct are correctly extracted to metadata, including the ExtraParams map copy.

core/providers/azure/types.go (1)

37-47: Azure image request type is well-structured.

Fields align with Azure OpenAI image generation API parameters. Optional fields correctly use pointer types with omitempty tags.

ui/hooks/useImageStream.ts (4)

54-77: State management and reset logic are correctly implemented.

Initial state is well-defined and the reset callback properly clears all refs and state.


79-86: Cancel function correctly handles abort controller.

Properly aborts in-flight requests and nullifies the ref.


107-179: Chunk processing logic handles all stream event types correctly.

Error handling, chunk accumulation, and completion detection are well-implemented. The progress estimation using expectedImagesRef.current * 10 is reasonable for UX purposes.


181-194: Stream initiation correctly cancels existing requests first.

The past review concern about concurrent SSE readers has been addressed - cancel() is called before reset() on line 184, and placeholder images are pre-seeded on line 192.

core/providers/azure/images.go (4)

10-23: Request conversion is clean with proper nil guards.

The function correctly handles nil input and delegates parameter mapping to a helper function.


25-36: Parameter mapping helper is appropriately scoped as unexported.

Direct pointer assignments are correct here since both source and target use the same pointer types.


38-73: Response conversion correctly maps all fields including ExtraFields.

The implementation properly:

  • Handles nil response
  • Maps image data with correct indexing
  • Conditionally creates usage struct
  • Populates ExtraFields with provider and latency metadata

54-60: The InputTokens to PromptTokens mapping is consistent with the OpenAI provider and follows the standard pattern across the codebase. Both Azure and OpenAI providers use identical field mappings for their respective API responses, so no changes are needed.

core/providers/gemini/gemini.go (2)

494-500: Streaming response builder arity update looks consistent.
All updated GetBifrostResponseForStreamResponse(...) call sites appear to pass the correct “slot” plus the new trailing nil for images.

Also applies to: 802-803, 812-813, 1115-1116, 1141-1142, 1385-1386, 1417-1418


1424-1432: Unsupported Gemini image-generation stubs look correct.
Returning NewUnsupportedOperationError with schemas.ImageGenerationRequest / schemas.ImageGenerationStreamRequest matches the provider’s “not supported” pattern.

core/internal/testutil/image_generation.go (1)

12-126: Test helper wiring + basic validations look good (and uses retry wrapper).
The WithImageGenerationRetry(...) usage and the stronger base64 length threshold are good for CI stability and catching truncation.

core/providers/openai/openai.go (1)

17-18: The dependency github.com/google/uuid v1.6.0 is already properly pinned in core/go.mod and aligns with existing project dependencies. No action needed.

core/schemas/bifrost.go (2)

139-161: BifrostRequest image field wiring looks consistent.
ImageGenerationRequest is included in the “only one field” set and correctly plumbed through GetRequestFields().

Also applies to: 163-183


341-373: Stream marshaling for image chunks is correct (single-non-nil selection).
The added BifrostImageGenerationStreamResponse branch is consistent with existing marshal behavior.

framework/streaming/accumulator.go (4)

124-136: Accumulator integration for image chunks looks consistent.
Initialization, append order, start/final timestamps, and pool-return on cleanup all follow the patterns used for other stream types.

Also applies to: 227-243, 245-270


346-380: Routing by RequestType is fine—event “type” naming still needs one canonical contract.
This dispatch will work, but only if upstream consistently sets ExtraFields.RequestType and stream chunks use the expected BifrostImageGenerationStreamResponse.Type values.


470-517: Pool prewarm includes image chunks: nice.
Prewarming the new pool matches the existing perf strategy.


13-123: All ImageStreamChunk fields are properly reset in putImageStreamChunk.

The struct contains: Timestamp, Delta, FinishReason, ChunkIndex, ImageIndex, ErrorDetails, Cost, SemanticCacheDebug, and TokenUsage. Your reset logic covers all of these—nil'ing all pointers and zeroing integer and time fields. Unlike ChatStreamChunk, ImageStreamChunk has no RawResponse field, so the current reset is complete and safe for object pool reuse.

core/internal/testutil/image_generation_load.go (2)

15-101: Load test concurrency approach looks fine.
Requests are constructed per call, counters are atomic, and duration aggregation is mutex-protected.


220-325: Good: avoids shared request pointer races for cache-load goroutines.
The per-goroutine localRequest avoids the data race risk that often shows up in load tests.

core/bifrost.go (3)

1758-1763: Fallback propagation for image generation is consistent with other request types.
Provider/model override is correctly applied for fallbacks.


2500-2506: Provider dispatch for image generation (sync + stream) looks correct.
The new cases route through the provider interface as expected.

Also applies to: 2536-2538


2705-2715: Request pool reset includes ImageGenerationRequest: good.
Clearing the new pointer avoids stale reuse from the pool.

core/providers/openai/images.go (1)

52-87: The fields RequestType and ModelRequested are already being populated in the ImageGeneration handler (lines 2220–2222 of core/providers/openai/openai.go), immediately after calling ToBifrostImageResponse:

bifrostResp.ExtraFields.Provider = provider.GetProviderKey()
bifrostResp.ExtraFields.ModelRequested = openaiRequest.Model
bifrostResp.ExtraFields.RequestType = schemas.ImageGenerationRequest

This is the established pattern across the codebase (see cerebras, vertex, and other OpenAI handlers). The conversion function returns a bare response; the handler enriches ExtraFields. No changes needed in ToBifrostImageResponse.

Likely an incorrect or invalid review comment.

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: 1

Caution

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

⚠️ Outside diff range comments (2)
transports/bifrost-http/handlers/inference.go (1)

1038-1124: Don’t log full streaming chunks; also make includeEventType sticky to avoid emitting [DONE] for Responses streams.

 func (h *CompletionHandler) handleStreamingResponse(ctx *fasthttp.RequestCtx, getStream func() (chan *schemas.BifrostStream, *schemas.BifrostError), cancel context.CancelFunc) {
@@
-	var includeEventType bool
+	// Sticky: once we detect Responses streaming, we must not emit legacy `[DONE]`.
+	var includeEventType bool
@@
 		for chunk := range stream {
 			if chunk == nil {
 				continue
 			}
 
-			includeEventType = false
-			if chunk.BifrostResponsesStreamResponse != nil ||
-				(chunk.BifrostError != nil && chunk.BifrostError.ExtraFields.RequestType == schemas.ResponsesStreamRequest) {
-				includeEventType = true
-			}
+			// Sticky decision for this stream (don’t flip-flop per chunk).
+			if !includeEventType {
+				if chunk.BifrostResponsesStreamResponse != nil ||
+					(chunk.BifrostError != nil && chunk.BifrostError.ExtraFields.RequestType == schemas.ResponsesStreamRequest) {
+					includeEventType = true
+				}
+			}
@@
 			chunkJSON, err := sonic.Marshal(chunk)
 			if err != nil {
-				logger.Warn(fmt.Sprintf("Failed to marshal streaming response: %v, chunk: %v", err, chunk))
+				// Avoid logging full chunk (may include large base64 payloads / sensitive content)
+				logger.Warn(fmt.Sprintf("Failed to marshal streaming response: %v", err))
 				continue
 			}
@@
 		if !includeEventType {
 			// Send the [DONE] marker to indicate the end of the stream (only for non-responses APIs)
 			if _, err := fmt.Fprint(w, "data: [DONE]\n\n"); err != nil {
 				logger.Warn(fmt.Sprintf("Failed to write SSE [DONE] marker: %v", err))
 				cancel() // Client disconnected (write error), cancel upstream stream
 				return
 			}
 		}

Based on learnings, don’t use [DONE] for Responses API streaming.

ui/lib/types/config.ts (1)

121-154: Update AllowedRequests schema and form initializers to include image_generation and image_generation_stream fields.

The TypeScript type definition includes both new fields, but multiple schema and initialization locations are missing them:

  1. ui/lib/types/schemas.ts (line ~310): allowedRequestsSchema is missing image_generation and image_generation_stream
  2. ui/lib/schemas/providerForm.ts (line ~55): AllowedRequestsSchema is missing both new fields plus list_models
  3. ui/app/workspace/providers/dialogs/addNewCustomProviderSheet.tsx (line ~43): Default values missing both new fields
  4. ui/app/workspace/providers/fragments/apiStructureFormFragment.tsx (line ~40): Default values missing both new fields

Forms will fail validation when the new fields are submitted, and existing providers won't have these fields initialized. Add image_generation: z.boolean() and image_generation_stream: z.boolean() to both Zod schemas and both form default initializations.

♻️ Duplicate comments (1)
transports/bifrost-http/handlers/inference.go (1)

222-227: (Optional) Keep request unmarshalling consistent with Chat/Responses if you run into embedded-pointer + sonic edge cases.

🧹 Nitpick comments (1)
core/providers/openai/openai.go (1)

2165-2170: Minor: Redundant provider name retrieval.

Lines 2165 and 2170 both retrieve the provider key/name. Consider removing the redundant call.

Apply this diff:

 	if err := providerUtils.CheckOperationAllowed(schemas.OpenAI, provider.customProviderConfig, schemas.ImageGenerationRequest); err != nil {
 		return nil, err // Handle error
 	}
 	providerKey := provider.GetProviderKey()
 	openaiRequest := ToOpenAIImageGenerationRequest(req)
 	if openaiRequest == nil {
 		return nil, providerUtils.NewBifrostOperationError("invalid request: input is required", nil, providerKey)
 	}
-	providerName := provider.GetProviderKey()
+	providerName := providerKey
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3738f96 and e9d1aee.

📒 Files selected for processing (13)
  • core/changelog.md (1 hunks)
  • core/internal/testutil/image_generation_load.go (1 hunks)
  • core/providers/azure/azure.go (2 hunks)
  • core/providers/openai/openai.go (10 hunks)
  • core/schemas/images.go (1 hunks)
  • framework/changelog.md (1 hunks)
  • plugins/semanticcache/changelog.md (1 hunks)
  • transports/bifrost-http/handlers/inference.go (7 hunks)
  • transports/bifrost-http/integrations/openai.go (1 hunks)
  • transports/changelog.md (1 hunks)
  • ui/app/workspace/providers/fragments/allowedRequestsFields.tsx (2 hunks)
  • ui/lib/constants/logs.ts (3 hunks)
  • ui/lib/types/config.ts (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • framework/changelog.md
🧰 Additional context used
📓 Path-based instructions (1)
**

⚙️ CodeRabbit configuration file

always check the stack if there is one for the current PR. do not give localized reviews for the PR, always see all changes in the light of the whole stack of PRs (if there is a stack, if there is no stack you can continue to make localized suggestions/reviews)

Files:

  • transports/bifrost-http/integrations/openai.go
  • core/changelog.md
  • ui/lib/constants/logs.ts
  • ui/app/workspace/providers/fragments/allowedRequestsFields.tsx
  • plugins/semanticcache/changelog.md
  • core/providers/openai/openai.go
  • ui/lib/types/config.ts
  • core/internal/testutil/image_generation_load.go
  • core/schemas/images.go
  • transports/changelog.md
  • transports/bifrost-http/handlers/inference.go
  • core/providers/azure/azure.go
🧠 Learnings (4)
📚 Learning: 2025-12-09T17:07:42.007Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/schemas/account.go:9-18
Timestamp: 2025-12-09T17:07:42.007Z
Learning: In core/schemas/account.go, the HuggingFaceKeyConfig field within the Key struct is currently unused and reserved for future Hugging Face inference endpoint deployments. Do not flag this field as missing from OpenAPI documentation or require its presence in the API spec until the feature is actively implemented and used. When the feature is added, update the OpenAPI docs accordingly; otherwise, treat this field as non-breaking and not part of the current API surface.

Applied to files:

  • transports/bifrost-http/integrations/openai.go
  • core/providers/openai/openai.go
  • core/internal/testutil/image_generation_load.go
  • core/schemas/images.go
  • transports/bifrost-http/handlers/inference.go
  • core/providers/azure/azure.go
📚 Learning: 2025-12-12T08:25:02.629Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1000
File: transports/bifrost-http/integrations/router.go:709-712
Timestamp: 2025-12-12T08:25:02.629Z
Learning: In transports/bifrost-http/**/*.go, update streaming response handling to align with OpenAI Responses API: use typed SSE events such as response.created, response.output_text.delta, response.done, etc., and do not rely on the legacy data: [DONE] termination marker. Note that data: [DONE] is only used by the older Chat Completions and Text Completions streaming APIs. Ensure parsers, writers, and tests distinguish SSE events from the [DONE] sentinel and handle each event type accordingly for correct stream termination and progress updates.

Applied to files:

  • transports/bifrost-http/integrations/openai.go
  • transports/bifrost-http/handlers/inference.go
📚 Learning: 2025-12-11T11:58:25.307Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1000
File: core/providers/openai/responses.go:42-84
Timestamp: 2025-12-11T11:58:25.307Z
Learning: In core/providers/openai/responses.go (and related OpenAI response handling), document and enforce the API format constraint: if ResponsesReasoning != nil and the response contains content blocks, all content blocks should be treated as reasoning blocks by default. Implement type guards or parsing logic accordingly, and add unit tests to verify that when ResponsesReasoning is non-nil, content blocks are labeled as reasoning blocks. Include clear comments in the code explaining the rationale and ensure downstream consumers rely on this behavior.

Applied to files:

  • core/providers/openai/openai.go
📚 Learning: 2025-12-14T14:43:30.902Z
Learnt from: Radheshg04
Repo: maximhq/bifrost PR: 980
File: core/providers/openai/images.go:10-22
Timestamp: 2025-12-14T14:43:30.902Z
Learning: Enforce the OpenAI image generation SSE event type values across the OpenAI image flow in the repository: use "image_generation.partial_image" for partial chunks, "image_generation.completed" for the final result, and "error" for errors. Apply this consistently in schemas, constants, tests, accumulator routing, and UI code within core/providers/openai (and related Go files) to ensure uniform event typing and avoid mismatches.

Applied to files:

  • core/providers/openai/openai.go
🧬 Code graph analysis (5)
transports/bifrost-http/integrations/openai.go (2)
core/providers/openai/types.go (1)
  • OpenAIImageGenerationRequest (294-304)
core/schemas/models.go (1)
  • Model (109-129)
core/providers/openai/openai.go (5)
core/providers/utils/utils.go (2)
  • GetBifrostResponseForStreamResponse (855-887)
  • ReleaseStreamingResponse (845-852)
core/schemas/images.go (4)
  • BifrostImageGenerationRequest (4-11)
  • BifrostImageGenerationResponse (33-40)
  • BifrostImageGenerationStreamResponse (55-65)
  • ImageUsage (49-52)
core/providers/openai/images.go (6)
  • ToOpenAIImageGenerationRequest (25-37)
  • ToBifrostImageResponse (53-87)
  • StreamingEnabledImageModels (18-22)
  • ImageGenerationCompleted (12-12)
  • ImageGenerationPartial (11-11)
  • ImageGenerationChunkSize (15-15)
core/providers/openai/errors.go (1)
  • ParseOpenAIError (10-42)
core/providers/openai/types.go (3)
  • OpenAIImageGenerationResponse (307-319)
  • OpenAIImageGenerationRequest (294-304)
  • OpenAIImageStreamResponse (333-338)
core/schemas/images.go (2)
core/schemas/provider.go (1)
  • Provider (282-315)
core/schemas/bifrost.go (4)
  • ModelProvider (32-32)
  • Fallback (134-137)
  • BifrostResponseExtraFields (308-318)
  • BifrostError (381-390)
transports/bifrost-http/handlers/inference.go (5)
core/schemas/images.go (3)
  • ImageGenerationInput (18-20)
  • ImageGenerationParameters (22-30)
  • BifrostImageGenerationRequest (4-11)
core/schemas/utils.go (1)
  • ParseModelString (23-36)
core/schemas/provider.go (1)
  • Provider (282-315)
core/schemas/bifrost.go (5)
  • ModelProvider (32-32)
  • ImageGenerationRequest (98-98)
  • BifrostStream (343-351)
  • BifrostError (381-390)
  • ImageGenerationStreamRequest (99-99)
transports/bifrost-http/lib/ctx.go (1)
  • ConvertToBifrostContext (72-283)
core/providers/azure/azure.go (7)
core/schemas/account.go (2)
  • Key (8-17)
  • AzureKeyConfig (21-25)
core/schemas/images.go (2)
  • BifrostImageGenerationRequest (4-11)
  • BifrostImageGenerationResponse (33-40)
core/providers/azure/images.go (1)
  • ToAzureImageRequest (11-23)
core/providers/azure/types.go (1)
  • AzureImageResponse (50-58)
core/schemas/utils.go (1)
  • Ptr (16-18)
core/utils.go (1)
  • Ptr (51-53)
core/providers/openai/openai.go (1)
  • HandleOpenAIImageGenerationStreaming (2281-2550)
🔇 Additional comments (16)
plugins/semanticcache/changelog.md (1)

1-2: Changelog entry looks fine; just ensure the leading - formatting matches the repo’s changelog conventions.

transports/changelog.md (1)

7-8: Changelog update matches the transport-layer change.

ui/app/workspace/providers/fragments/allowedRequestsFields.tsx (1)

21-37: UI request-type + endpoint placeholder additions are consistent with /v1/images/generations.

Also applies to: 61-76

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

49-64: Good: Azure pre-hook now normalizes image-generation model names the same way as other OpenAI request types.

core/changelog.md (1)

6-7: Changelog entry aligns with the new core image-generation surface.

ui/lib/constants/logs.ts (1)

28-42: Nice: request-type labels/colors are wired for image generation and streaming.

Also applies to: 81-137

transports/bifrost-http/handlers/inference.go (3)

141-152: Known-fields list for image extra-params extraction looks correct.


358-371: Route registration matches the intended OpenAI-compatible path.


1282-1293: Streaming plumbing reuses the shared SSE writer consistently.

core/providers/azure/azure.go (2)

787-851: LGTM! Past issues resolved.

The non-streaming ImageGeneration implementation correctly:

  • Uses the deployment-scoped path openai/deployments/{deployment}/images/generations (Line 818).
  • Returns bifrostErr directly on deployment resolution failure (Line 812).
  • Maps request.Model into the Bifrost response (Line 834).

853-915: LGTM! Streaming implementation follows Azure patterns.

The ImageGenerationStream method correctly:

  • Validates the key configuration.
  • Resolves the deployment and constructs the deployment-scoped URL.
  • Handles Bearer token vs. api-key authentication.
  • Enforces streaming support per model via StreamingEnabledImageModels.
  • Delegates to the OpenAI-compatible streaming handler with appropriate parameters.
core/providers/openai/openai.go (2)

2233-2280: LGTM! Past issues resolved.

The ImageGenerationStream method now correctly:

  • Guards against nil request (Line 2243-2245).
  • Uses ImageGenerationStreamRequest for URL building (Line 2267).
  • Passes sendBackRawRequest parameter to the streaming handler (Line 2271).

2462-2539: LGTM! Completion chunk logic fixed.

The streaming chunk handling now correctly:

  • Increments chunkIndex for each base64 chunk (Line 2472).
  • Increments chunkIndex again for the completion chunk (Line 2503), ensuring it has a unique, strictly increasing index.
  • Returns after sending the completion chunk (Line 2538), preventing further processing.

This resolves the past issue where the completion chunk reused the same index and the goroutine continued scanning.

core/internal/testutil/image_generation_load.go (3)

17-102: LGTM! Concurrent load test implementation is safe.

The function correctly:

  • Creates a fresh request within each goroutine iteration (Lines 47-58), avoiding data races.
  • Uses mutex-protected updates for totalDuration (Lines 64-66).
  • Uses atomic operations for success/error counters.
  • Validates a minimum 80% success rate.

104-222: LGTM! Stream load test correctly manages context lifecycle.

The function now correctly:

  • Creates a fresh cancel function per stream (Line 148).
  • Calls cancel() explicitly on error paths (Lines 152, 159, 173, 181).
  • Calls cancel() after stream processing completes (Line 187).
  • Uses the canonical string(openai.ImageGenerationCompleted) constant for completion checks (Line 179).

This resolves the past issues with defer cancel() placement and hardcoded event type strings.


224-329: LGTM! Cache load test avoids data races.

The function now correctly creates a fresh localRequest within each goroutine (Lines 275-285), preventing data races on the shared request pointer. The cache hit/miss tracking and metrics aggregation are properly mutex-protected or use atomic operations.

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: 0

🧹 Nitpick comments (2)
framework/changelog.md (1)

1-1: Clarify the scope and add detail to the changelog entry.

The entry "feat: add image generation streaming accumulation support" is quite vague and underspecifies what framework changes are included. Based on the PR objectives, the framework likely contributes more than just streaming accumulation (e.g., streaming handlers, chunk reassembly, streaming finalization logic).

Consider expanding the entry to be more descriptive of the actual framework changes, such as:

feat: add image generation streaming support with accumulation and chunk reassembly

or

feat: add streaming framework for image generation with base64 chunk accumulation and progressive delivery

This ensures the changelog is self-documenting and helps users and maintainers understand the scope of framework changes at a glance.

Please verify that this entry comprehensively captures all framework-level changes related to image generation in this PR. If there are other changes (e.g., new handler registration, serialization/deserialization helpers, or observability hooks), they should be reflected in the changelog entry as well.

core/changelog.md (1)

1-1: Improve changelog entry specificity.

The changelog entry is quite generic and doesn't reflect the comprehensive scope of this PR. Based on the PR objectives, this release includes streaming support, semantic caching integration, UI components, and provider implementations—not just request/response support.

Consider expanding the entry to better communicate the scope to users:

-feat: added image generation request and response support
+feat: image generation support with streaming, semantic caching, and provider implementations

Or break it into multiple entries if the changelog format supports detailed feature descriptions.

Please verify:

  1. That this changelog entry follows your project's format conventions (see the full core/changelog.md file for consistency).
  2. That corresponding updates are being made to frameworks/changelog.md, plugins/semanticcache/changelog.md, and transports/changelog.md as noted in the pre-merge checklist.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e9d1aee and 7e4f581.

📒 Files selected for processing (6)
  • core/changelog.md (1 hunks)
  • framework/changelog.md (1 hunks)
  • plugins/semanticcache/changelog.md (1 hunks)
  • transports/bifrost-http/handlers/inference.go (7 hunks)
  • transports/bifrost-http/integrations/router.go (6 hunks)
  • transports/changelog.md (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • transports/changelog.md
  • plugins/semanticcache/changelog.md
🧰 Additional context used
📓 Path-based instructions (1)
**

⚙️ CodeRabbit configuration file

always check the stack if there is one for the current PR. do not give localized reviews for the PR, always see all changes in the light of the whole stack of PRs (if there is a stack, if there is no stack you can continue to make localized suggestions/reviews)

Files:

  • transports/bifrost-http/integrations/router.go
  • framework/changelog.md
  • core/changelog.md
  • transports/bifrost-http/handlers/inference.go
🧠 Learnings (2)
📚 Learning: 2025-12-09T17:07:42.007Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/schemas/account.go:9-18
Timestamp: 2025-12-09T17:07:42.007Z
Learning: In core/schemas/account.go, the HuggingFaceKeyConfig field within the Key struct is currently unused and reserved for future Hugging Face inference endpoint deployments. Do not flag this field as missing from OpenAPI documentation or require its presence in the API spec until the feature is actively implemented and used. When the feature is added, update the OpenAPI docs accordingly; otherwise, treat this field as non-breaking and not part of the current API surface.

Applied to files:

  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/handlers/inference.go
📚 Learning: 2025-12-12T08:25:02.629Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1000
File: transports/bifrost-http/integrations/router.go:709-712
Timestamp: 2025-12-12T08:25:02.629Z
Learning: In transports/bifrost-http/**/*.go, update streaming response handling to align with OpenAI Responses API: use typed SSE events such as response.created, response.output_text.delta, response.done, etc., and do not rely on the legacy data: [DONE] termination marker. Note that data: [DONE] is only used by the older Chat Completions and Text Completions streaming APIs. Ensure parsers, writers, and tests distinguish SSE events from the [DONE] sentinel and handle each event type accordingly for correct stream termination and progress updates.

Applied to files:

  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/handlers/inference.go
🧬 Code graph analysis (2)
transports/bifrost-http/integrations/router.go (2)
core/schemas/images.go (2)
  • BifrostImageGenerationResponse (33-40)
  • BifrostImageGenerationStreamResponse (55-65)
core/schemas/bifrost.go (2)
  • ImageGenerationRequest (98-98)
  • ImageGenerationStreamRequest (99-99)
transports/bifrost-http/handlers/inference.go (4)
core/schemas/images.go (3)
  • ImageGenerationInput (18-20)
  • ImageGenerationParameters (22-30)
  • BifrostImageGenerationRequest (4-11)
core/schemas/utils.go (1)
  • ParseModelString (23-36)
core/schemas/bifrost.go (2)
  • ModelProvider (32-32)
  • ImageGenerationRequest (98-98)
transports/bifrost-http/lib/ctx.go (1)
  • ConvertToBifrostContext (72-283)
🔇 Additional comments (4)
transports/bifrost-http/integrations/router.go (1)

133-139: LGTM—image generation converters are well integrated.

The new ImageGenerationResponseConverter and ImageGenerationStreamResponseConverter types follow the established pattern for other modalities. Integration into StreamConfig, RouteConfig, non-streaming dispatch (lines 579-601), streaming dispatch (lines 653-654), and the central streaming flow (lines 839-840) is consistent with existing request types. PostCallback support and nil checks are properly included.

Also applies to: 192-192, 221-221, 579-601, 653-654, 839-840

transports/bifrost-http/handlers/inference.go (3)

141-152: LGTM—imageParamsKnownFields is complete.

The map correctly includes all known parameters for image generation: model, prompt, fallbacks, stream, n, size, quality, style, response_format, and user. This aligns with ImageGenerationParameters fields in core/schemas/images.go.


1205-1282: LGTM—image generation handler is well structured.

The imageGeneration handler follows the established pattern for other modalities:

  • Parses model in provider/model format and validates prompt (lines 1215-1224)
  • Extracts extra params using imageParamsKnownFields (lines 1226-1237)
  • Parses fallbacks (lines 1238-1243)
  • Properly manages context lifecycle: calls cancel() on nil check (line 1257), calls cancel() before validation error return (line 1265), and defers cancel() for non-streaming path (line 1272)
  • Validates streaming constraints: rejects "url" response format when streaming is requested (lines 1264-1266)

1284-1295: LGTM—streaming helper follows the established pattern.

handleStreamingImageGeneration creates a cancellable stream context, wraps the Bifrost call in a closure, and delegates to the shared handleStreamingResponse helper. This matches the structure of handleStreamingChatCompletion, handleStreamingResponses, and other streaming helpers.

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: 4

🧹 Nitpick comments (1)
docs/quickstart/go-sdk/multimodal.mdx (1)

65-75: Nil/empty handling: tighten the example to avoid silent no-op.

Right now, if len(response.Data) > 0 but response.Data[0].URL == nil, nothing is printed; consider adding an else to log/print that the response format wasn’t url (or that the provider returned b64).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7e4f581 and 96e06c0.

📒 Files selected for processing (4)
  • core/schemas/images.go (1 hunks)
  • docs/features/unified-interface.mdx (1 hunks)
  • docs/quickstart/gateway/multimodal.mdx (2 hunks)
  • docs/quickstart/go-sdk/multimodal.mdx (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**

⚙️ CodeRabbit configuration file

always check the stack if there is one for the current PR. do not give localized reviews for the PR, always see all changes in the light of the whole stack of PRs (if there is a stack, if there is no stack you can continue to make localized suggestions/reviews)

Files:

  • docs/quickstart/gateway/multimodal.mdx
  • core/schemas/images.go
  • docs/features/unified-interface.mdx
  • docs/quickstart/go-sdk/multimodal.mdx
🧠 Learnings (1)
📚 Learning: 2025-12-09T17:07:42.007Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/schemas/account.go:9-18
Timestamp: 2025-12-09T17:07:42.007Z
Learning: In core/schemas/account.go, the HuggingFaceKeyConfig field within the Key struct is currently unused and reserved for future Hugging Face inference endpoint deployments. Do not flag this field as missing from OpenAPI documentation or require its presence in the API spec until the feature is actively implemented and used. When the feature is added, update the OpenAPI docs accordingly; otherwise, treat this field as non-breaking and not part of the current API surface.

Applied to files:

  • core/schemas/images.go
🔇 Additional comments (9)
docs/quickstart/gateway/multimodal.mdx (2)

3-3: Doc description update looks good; keep capability list consistent with the rest of the docs IA
If other pages use a canonical set/order of “vision / image / speech / transcription” capabilities, consider matching that wording/order here to avoid drift.


49-85: Update created timestamp and clarify unified model naming in request/response

The example response shows an old epoch timestamp (created: 1713833628); update to a current or placeholder value like 0 or a realistic recent timestamp to prevent user confusion about staleness.

Regarding the model field: the example correctly shows the unified format (openai/dall-e-3) in the request and provider-native format (dall-e-3) in the response—this matches Bifrost's design. However, to reduce user confusion when copy-pasting, consider adding a brief comment noting that Bifrost normalizes the unified model name to the provider-native format in responses.

Streaming image generation is implemented in Bifrost (via "stream": true with base64 response format), but adding a streaming example appears to be outside the current PR scope focused on basic image generation. Defer streaming documentation to a follow-up if planned.

docs/features/unified-interface.mdx (1)

88-89: LGTM! Table header correctly extended.

The new "Image Generation" and "Image Generation (stream)" columns are properly added to the provider support matrix header.

core/schemas/images.go (6)

1-16: LGTM! Request structure follows Bifrost patterns.

The BifrostImageGenerationRequest correctly follows the established Bifrost request pattern with Provider, Model, Input, Params, and Fallbacks. The GetRawRequestBody method properly implements the interface for raw body access.


18-20: LGTM! Clean input structure.

The ImageGenerationInput struct is appropriately simple with just the required Prompt field.


32-40: LGTM! Response structure is consistent.

The BifrostImageGenerationResponse follows the established Bifrost response pattern with standard fields (ID, Created, Model, Usage, ExtraFields).


42-47: LGTM! ImageData structure aligns with standards.

The ImageData struct correctly represents individual image results with URL or B64JSON options, RevisedPrompt, and Index fields, consistent with OpenAI's image API format.


54-65: LGTM! Well-designed streaming response structure.

The BifrostImageGenerationStreamResponse is well-structured for progressive image delivery with clear semantics:

  • Type differentiates event types
  • Index identifies which image (for multi-image requests)
  • ChunkIndex maintains chunk ordering
  • PartialB64 carries base64 chunks

The optional fields (RevisedPrompt, Usage, Error) are appropriately placed for their respective events.


49-52: ImageUsage struct fields are correctly aligned with provider implementations.

The ImageUsage struct with PromptTokens and TotalTokens matches the actual usage information returned by both OpenAI and Azure image generation APIs. Both providers map their API responses (InputTokensPromptTokens, TotalTokensTotalTokens) directly to these fields. No CompletionTokens field is needed for image generation, as image APIs only return input and total token counts, not completion tokens.

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: 1

♻️ Duplicate comments (1)
docs/quickstart/go-sdk/multimodal.mdx (1)

3-3: Add an immediate provider-matrix link (the “across various providers” claim is easy to over-read).

You already link to Unified Interface later (Line 382-384), but it’s far from the top-level capability claim. Suggest adding a parenthetical link right in the description so readers don’t assume all providers support image generation.

-description: "Process multiple types of content including images, audio, and text with AI models. Bifrost supports vision analysis, image generation, speech synthesis, and audio transcription across various providers."
+description: "Process multiple types of content including images, audio, and text with AI models. Bifrost supports vision analysis, image generation, speech synthesis, and audio transcription (see Unified Interface provider support) across various providers."
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 96e06c0 and 01deafd.

📒 Files selected for processing (3)
  • core/schemas/images.go (1 hunks)
  • docs/features/unified-interface.mdx (1 hunks)
  • docs/quickstart/go-sdk/multimodal.mdx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • core/schemas/images.go
🧰 Additional context used
📓 Path-based instructions (1)
**

⚙️ CodeRabbit configuration file

always check the stack if there is one for the current PR. do not give localized reviews for the PR, always see all changes in the light of the whole stack of PRs (if there is a stack, if there is no stack you can continue to make localized suggestions/reviews)

Files:

  • docs/features/unified-interface.mdx
  • docs/quickstart/go-sdk/multimodal.mdx
🔇 Additional comments (1)
docs/quickstart/go-sdk/multimodal.mdx (1)

48-89: The code example compiles correctly; no changes needed. The ImageData fields (URL, B64JSON, RevisedPrompt) are defined as string types in schemas.ImageData, not *string pointers. Similarly, ImageUsage.TotalTokens is an int, not a pointer. The documentation example correctly treats these as non-pointer types and will compile without the suggested nil-safety changes.

r.Model = setAzureModelName(r.Model, deploymentIDStr)
case *openai.OpenAIEmbeddingRequest:
r.Model = setAzureModelName(r.Model, deploymentIDStr)
case *openai.OpenAIImageGenerationRequest:
Copy link
Collaborator

Choose a reason for hiding this comment

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

you will also need to add a route for image gen right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes, I’ve added it now and wired it up properly. should be all set

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: 1

🧹 Nitpick comments (1)
core/providers/openai/openai_image_test.go (1)

199-207: Minor: Consider restructuring the nil response test case.

The "nil response returns nil" test case passes a nil OpenAI response, and the validate function checks resp != nil. While this works, the pattern is slightly awkward since you're validating a nil pointer. Consider using the wantNil pattern from TestImageGenerationStreamingRequestConversion for consistency:

{
    name:    "nil response returns nil",
    openai:  nil,
    wantNil: true,
},

Then handle it in the test runner similar to lines 99-105.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 01deafd and 502f360.

📒 Files selected for processing (4)
  • core/providers/openai/images.go (1 hunks)
  • core/providers/openai/openai_image_test.go (1 hunks)
  • core/providers/openai/types.go (1 hunks)
  • transports/bifrost-http/integrations/openai.go (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**

⚙️ CodeRabbit configuration file

always check the stack if there is one for the current PR. do not give localized reviews for the PR, always see all changes in the light of the whole stack of PRs (if there is a stack, if there is no stack you can continue to make localized suggestions/reviews)

Files:

  • transports/bifrost-http/integrations/openai.go
  • core/providers/openai/images.go
  • core/providers/openai/openai_image_test.go
  • core/providers/openai/types.go
🧠 Learnings (5)
📚 Learning: 2025-12-09T17:07:42.007Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/schemas/account.go:9-18
Timestamp: 2025-12-09T17:07:42.007Z
Learning: In core/schemas/account.go, the HuggingFaceKeyConfig field within the Key struct is currently unused and reserved for future Hugging Face inference endpoint deployments. Do not flag this field as missing from OpenAPI documentation or require its presence in the API spec until the feature is actively implemented and used. When the feature is added, update the OpenAPI docs accordingly; otherwise, treat this field as non-breaking and not part of the current API surface.

Applied to files:

  • transports/bifrost-http/integrations/openai.go
  • core/providers/openai/images.go
  • core/providers/openai/openai_image_test.go
  • core/providers/openai/types.go
📚 Learning: 2025-12-12T08:25:02.629Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1000
File: transports/bifrost-http/integrations/router.go:709-712
Timestamp: 2025-12-12T08:25:02.629Z
Learning: In transports/bifrost-http/**/*.go, update streaming response handling to align with OpenAI Responses API: use typed SSE events such as response.created, response.output_text.delta, response.done, etc., and do not rely on the legacy data: [DONE] termination marker. Note that data: [DONE] is only used by the older Chat Completions and Text Completions streaming APIs. Ensure parsers, writers, and tests distinguish SSE events from the [DONE] sentinel and handle each event type accordingly for correct stream termination and progress updates.

Applied to files:

  • transports/bifrost-http/integrations/openai.go
📚 Learning: 2025-12-11T11:58:25.307Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1000
File: core/providers/openai/responses.go:42-84
Timestamp: 2025-12-11T11:58:25.307Z
Learning: In core/providers/openai/responses.go (and related OpenAI response handling), document and enforce the API format constraint: if ResponsesReasoning != nil and the response contains content blocks, all content blocks should be treated as reasoning blocks by default. Implement type guards or parsing logic accordingly, and add unit tests to verify that when ResponsesReasoning is non-nil, content blocks are labeled as reasoning blocks. Include clear comments in the code explaining the rationale and ensure downstream consumers rely on this behavior.

Applied to files:

  • core/providers/openai/images.go
  • core/providers/openai/openai_image_test.go
  • core/providers/openai/types.go
📚 Learning: 2025-12-14T14:43:30.902Z
Learnt from: Radheshg04
Repo: maximhq/bifrost PR: 980
File: core/providers/openai/images.go:10-22
Timestamp: 2025-12-14T14:43:30.902Z
Learning: Enforce the OpenAI image generation SSE event type values across the OpenAI image flow in the repository: use "image_generation.partial_image" for partial chunks, "image_generation.completed" for the final result, and "error" for errors. Apply this consistently in schemas, constants, tests, accumulator routing, and UI code within core/providers/openai (and related Go files) to ensure uniform event typing and avoid mismatches.

Applied to files:

  • core/providers/openai/images.go
  • core/providers/openai/openai_image_test.go
  • core/providers/openai/types.go
📚 Learning: 2025-12-15T10:16:21.909Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/providers/huggingface/huggingface_test.go:12-63
Timestamp: 2025-12-15T10:16:21.909Z
Learning: In provider tests under core/providers/<provider>/*_test.go, do not require or flag the use of defer for Shutdown(); instead call client.Shutdown() at the end of each test function. This pattern appears consistent across all provider tests. Apply this rule only within this path; for other tests or resources, defer may still be appropriate.

Applied to files:

  • core/providers/openai/openai_image_test.go
🧬 Code graph analysis (4)
transports/bifrost-http/integrations/openai.go (4)
core/providers/openai/types.go (1)
  • OpenAIImageGenerationRequest (294-302)
transports/bifrost-http/integrations/router.go (6)
  • RouteConfig (207-226)
  • RouteConfigTypeOpenAI (199-199)
  • RequestConverter (82-82)
  • ImageGenerationResponseConverter (135-135)
  • ErrorConverter (143-143)
  • StreamConfig (186-194)
core/schemas/bifrost.go (4)
  • BifrostRequest (150-161)
  • ImageGenerationRequest (98-98)
  • OpenAI (35-35)
  • BifrostError (381-390)
core/schemas/images.go (2)
  • BifrostImageGenerationResponse (33-40)
  • BifrostImageGenerationStreamResponse (55-65)
core/providers/openai/images.go (3)
core/providers/openai/types.go (2)
  • OpenAIImageGenerationRequest (294-302)
  • OpenAIImageGenerationResponse (305-317)
core/schemas/images.go (6)
  • BifrostImageGenerationRequest (4-11)
  • ImageGenerationParameters (22-30)
  • BifrostImageGenerationResponse (33-40)
  • ImageData (42-47)
  • ImageUsage (49-52)
  • ImageGenerationInput (18-20)
core/schemas/utils.go (2)
  • ParseModelString (23-36)
  • ParseFallbacks (53-68)
core/providers/openai/openai_image_test.go (5)
core/schemas/images.go (4)
  • BifrostImageGenerationRequest (4-11)
  • ImageGenerationInput (18-20)
  • ImageGenerationParameters (22-30)
  • BifrostImageGenerationResponse (33-40)
core/providers/openai/types.go (3)
  • OpenAIImageGenerationRequest (294-302)
  • OpenAIImageGenerationResponse (305-317)
  • OpenAIImageGenerationUsage (319-328)
core/schemas/bifrost.go (2)
  • OpenAI (35-35)
  • Azure (36-36)
core/providers/openai/images.go (2)
  • ToOpenAIImageGenerationRequest (25-39)
  • ToBifrostImageResponse (42-76)
core/providers/azure/images.go (1)
  • ToBifrostImageResponse (39-73)
core/providers/openai/types.go (1)
core/schemas/images.go (1)
  • ImageGenerationParameters (22-30)
🔇 Additional comments (11)
transports/bifrost-http/integrations/openai.go (2)

60-61: LGTM!

The Azure endpoint pre-hook case for OpenAIImageGenerationRequest follows the established pattern for other request types, correctly applying the setAzureModelName logic with the deployment ID.


378-425: Image generation endpoint configuration looks well-structured.

The route configuration correctly:

  • Exposes the standard OpenAI image generation paths including Azure deployment support
  • Uses ToBifrostImageGenerationRequest() for request conversion
  • Returns resp.Type as the SSE event type in the streaming converter (lines 414, 417), which aligns with the OpenAI spec ("image_generation.partial_image", "image_generation.completed"). Based on learnings, this is the correct approach for typed SSE events.
core/providers/openai/openai_image_test.go (3)

14-108: Well-structured table-driven test for request conversion.

The test comprehensively covers:

  • Full parameter mapping
  • Nil request and nil input edge cases
  • Nil params scenario

Good use of parallel subtests and validation functions.


110-139: LGTM!

JSON serialization test properly handles the marshal error and verifies key fields are present in the output. The previous review comment about ignoring the sonic.Marshal error has been addressed.


236-420: Comprehensive test coverage for Bifrost request conversion.

Excellent coverage including:

  • Provider prefix parsing (openai/, azure/)
  • Fallback chain parsing with mixed providers
  • Partial parameters preservation
  • Empty vs nil fallbacks distinction

The test cases thoroughly validate the ToBifrostImageGenerationRequest method.

core/providers/openai/types.go (1)

304-336: LGTM!

The response types correctly model the OpenAI image generation API:

  • OpenAIImageGenerationResponse with inline Data struct matching the API shape
  • OpenAIImageGenerationUsage with detailed token breakdown including InputTokensDetails
  • OpenAIImageStreamResponse using the typed ImageGenerationEventType for event classification
core/providers/openai/images.go (5)

10-16: LGTM! Constants align with OpenAI specification.

The SSE event type constants "image_generation.partial_image" and "image_generation.completed" correctly match the OpenAI specification, as confirmed in previous review discussions. The 128KB chunk size is appropriate for base64 streaming.

Based on learnings, these values should be used consistently across schemas, tests, accumulator routing, and UI code.


18-22: LGTM!

The StreamingEnabledImageModels map correctly identifies that only gpt-image-1 supports streaming, while dall-e-2 and dall-e-3 do not. Unknown models will return false by default (safe behavior).


24-39: LGTM!

Clean nil-safe conversion with proper parameter mapping. The struct copy at line 36 is safe since ImageGenerationParameters contains only primitives and pointers.


41-76: LGTM!

The response conversion correctly:

  • Maps OpenAI's InputTokens to Bifrost's PromptTokens (intentional naming difference)
  • Preserves image index ordering
  • Converts latency to milliseconds for consistency
  • Generates a unique ID for the Bifrost response

78-91: LGTM!

The method correctly:

  • Parses provider prefix from model string (e.g., "azure/dall-e-3"Azure, "dall-e-3")
  • Takes a pointer to the embedded ImageGenerationParameters for the Params field
  • Uses ParseFallbacks for proper fallback chain construction

Comment on lines +290 to +302

type ImageGenerationEventType string

// OpenAIImageGenerationRequest is the struct for Image Generation requests by OpenAI.
type OpenAIImageGenerationRequest struct {
Model string `json:"model"`
Prompt string `json:"prompt"`

schemas.ImageGenerationParameters

Stream *bool `json:"stream,omitempty"`
Fallbacks []string `json:"fallbacks,omitempty"`
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash

# Search for StreamingRequest interface definition
echo "=== Looking for StreamingRequest interface ==="
rg -n "type StreamingRequest" --type=go

echo ""
echo "=== Looking for IsStreamingRequested implementations ==="
rg -n "func.*IsStreamingRequested" --type=go

echo ""
echo "=== Looking for OpenAIImageGenerationRequest ==="
rg -n "type OpenAIImageGenerationRequest" --type=go -A 10

echo ""
echo "=== Checking if OpenAIImageGenerationRequest has IsStreamingRequested ==="
rg -n "func.*OpenAIImageGenerationRequest.*IsStreamingRequested" --type=go

Repository: maximhq/bifrost

Length of output: 2167


🏁 Script executed:

#!/bin/bash

# Get StreamingRequest interface definition
echo "=== StreamingRequest Interface Definition ==="
rg -n "type StreamingRequest" --type=go -A 5

echo ""
echo "=== Example IsStreamingRequested implementation ==="
rg -n "func.*OpenAIChatRequest.*IsStreamingRequested" --type=go -A 3

echo ""
echo "=== Check if OpenAIImageGenerationRequest is used anywhere ==="
rg -n "OpenAIImageGenerationRequest" --type=go | head -20

echo ""
echo "=== Check git log for context on related types ==="
git log --oneline -20 --all

Repository: maximhq/bifrost

Length of output: 3419


🏁 Script executed:

#!/bin/bash

# Check the router integration for OpenAIImageGenerationRequest handling
echo "=== Router integration with OpenAIImageGenerationRequest ==="
rg -n "OpenAIImageGenerationRequest" transports/bifrost-http/integrations/openai.go -B 3 -A 3 | head -30

echo ""
echo "=== Check if OpenAIImageGenerationRequest is used as StreamingRequest ==="
grep -n "case \*openai.OpenAIImageGenerationRequest" transports/bifrost-http/integrations/openai.go -A 10

Repository: maximhq/bifrost

Length of output: 1413


🏁 Script executed:

#!/bin/bash

# Check if router calls IsStreamingRequested on requests
echo "=== Check router.go for IsStreamingRequested calls ==="
rg -n "IsStreamingRequested" transports/bifrost-http/integrations/router.go -B 2 -A 2

echo ""
echo "=== Check broader handler context for streaming request handling ==="
rg -n "StreamingRequest" transports/bifrost-http/integrations/ --type=go -B 2 -A 2

echo ""
echo "=== Look at recent commit for image generation ==="
git log -1 --format="%H %s" 502f360

echo ""
echo "=== Check if there's a PR stack or related context ==="
git log --oneline --graph --all | head -30

Repository: maximhq/bifrost

Length of output: 3760


Add IsStreamingRequested method to OpenAIImageGenerationRequest.

Image generation requests are routed through streaming detection logic that requires this method. Without it, streaming requests will be mishandled as non-streaming. All other request types with Stream fields implement this method consistently.

// IsStreamingRequested implements the StreamingRequest interface
func (r *OpenAIImageGenerationRequest) IsStreamingRequested() bool {
	return r.Stream != nil && *r.Stream
}
🤖 Prompt for AI Agents
In core/providers/openai/types.go around lines 290 to 302, the
OpenAIImageGenerationRequest type lacks the IsStreamingRequested method required
by the StreamingRequest interface; add a pointer-receiver method named
IsStreamingRequested that returns true only when Stream is non-nil and true
(i.e., return r.Stream != nil && *r.Stream) so streaming detection treats image
generation requests correctly.

@Radheshg04
Copy link
Contributor Author

@Pratham-Mishra04 openai just released new image gen models, should we add support now or defer that work to a later version?

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.

[Feature]: Support for Image Generation

2 participants