Skip to content

plugins v2 architecture#888

Merged
akshaydeo merged 2 commits intov1.4.0from
11-19-plugins_v2_architecture
Dec 29, 2025
Merged

plugins v2 architecture#888
akshaydeo merged 2 commits intov1.4.0from
11-19-plugins_v2_architecture

Conversation

@akshaydeo
Copy link
Copy Markdown
Contributor

@akshaydeo akshaydeo commented Nov 19, 2025

Summary

Replace the TransportInterceptor plugin interface with a more flexible HTTPTransportMiddleware approach that follows the standard middleware pattern used in HTTP servers.

Changes

  • Replaced TransportInterceptor method in the Plugin interface with HTTPTransportMiddleware that returns a middleware function
  • Added a new BifrostHTTPMiddleware type in the core schemas package
  • Updated all plugins to implement the new middleware interface
  • Modified the HTTP transport layer to use the new middleware pattern
  • Updated the middleware chain implementation to work with the new approach
  • Refactored the governance plugin to use the new middleware pattern for request interception

Type of change

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

Affected areas

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

How to test

Test that plugins can still intercept and modify HTTP requests:

# Core/Transports
go version
go test ./...

# Test with a custom plugin that implements HTTPTransportMiddleware
cd examples/plugins/hello-world
go build -buildmode=plugin -o hello-world.so .

Then configure Bifrost to use this plugin and verify that the middleware is called during HTTP requests.

Breaking changes

  • Yes
  • No

This is a breaking change for plugin developers. Any custom plugins that implement the TransportInterceptor method will need to be updated to implement the new HTTPTransportMiddleware method instead.

Migration instructions:

  1. Replace TransportInterceptor with HTTPTransportMiddleware in your plugin
  2. Update the function signature to return a middleware function
  3. Move your interception logic into the middleware function

Security considerations

The new middleware pattern provides better isolation between plugins and more control over the request/response lifecycle, which could improve security by reducing the risk of plugins interfering with each other.

Checklist

  • I added/updated tests where appropriate
  • I verified builds succeed (Go and UI)

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Nov 19, 2025

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • End-to-end distributed tracing and observability (trace forwarding) with a dev profiling endpoint and in-app Dev Profiler UI.
    • Plugin HTTP transport now supports middleware-style hooks; migration guide and updated docs added.
  • Breaking Changes

    • Plugin transport API replaced — plugins must adopt the new HTTP transport middleware.
  • Improvements

    • Streaming telemetry: Time‑to‑First‑Token, deferred trace finalization for streams, centralized accumulators for lower memory use.

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

Walkthrough

Replaces plugin TransportInterceptor with an HTTP middleware API, adds end-to-end tracing (Trace/Span types, Tracer interface, TraceStore, W3C propagation), integrates tracing into streaming/LLM flows (deferred completion, TTFT), introduces tracing middleware and observability plugin hook, adds dev /pprof endpoint and UI, and updates handlers/tests/docs.

Changes

Cohort / File(s) Summary
Plugin interface & loader
core/schemas/plugin.go, framework/plugins/*.go, framework/plugins/dynamicplugin.go, framework/plugins/dynamicplugin_test.go
Replaces TransportInterceptor with HTTPTransportMiddleware() returning BifrostHTTPMiddleware; DynamicPlugin loads/export HTTPTransportMiddleware; tests updated to fasthttp middleware style.
Example & plugin updates
examples/plugins/hello-world/*, plugins/*/main.go, plugins/*/changelog.md
Example plugins and many plugin implementations updated to add HTTPTransportMiddleware() (often returning nil) and remove TransportInterceptor; some plugins implement ObservabilityPlugin.Inject.
Tracing schemas & core types
core/schemas/trace.go, core/schemas/tracer.go, core/schemas/bifrost.go, core/schemas/context.go
Adds Trace/Span/SpanEvent types, SpanKind/SpanStatus, many gen_ai.* attributes, Tracer interface and NoOpTracer, new Bifrost config Tracer field and context keys.
Core integration
core/bifrost.go, core/bifrost_test.go, core/providers/utils/utils.go, core/utils.go
Threads tracer through request lifecycle, adds SetTracer/GetChunkCount/FinalizeStreamingPostHookSpans, deferred-span completion logic, tests updated to pass DefaultTracer, and adds sanitizeSpanName helper.
Tracing framework & store
framework/tracing/{store,tracer,helpers,llmspan,propagation}.go
Implements TraceStore (TTL/pooling/cleanup), Tracer adapter (TraceStore-backed, accumulator integration), W3C tracecontext extraction/injection, and tracing helper utilities.
Streaming accumulator & TTFT
framework/streaming/*, framework/streaming/types.go, framework/streaming/accumulator.go, framework/streaming/{chat,responses,transcription,audio}.go
Adds FirstChunkTimestamp, CreateStreamAccumulator, StartTimestamp handling and computes TimeToFirstToken (TTFT) included in AccumulatedData.
HTTP transport & middlewares
transports/bifrost-http/handlers/*, transports/bifrost-http/lib/*, transports/bifrost-http/integrations/router.go, transports/bifrost-http/lib/middleware.go, transports/bifrost-http/handlers/middlewares.go, transports/bifrost-http/handlers/middlewares_test.go
Migrates middleware types to schemas.BifrostHTTPMiddleware, updates RegisterRoutes signatures, replaces per-request interception with plugin middleware chaining, adds TracingMiddleware (trace lifecycle, deferred completion), and updates ChainMiddlewares.
Server, config & loader simplification
transports/bifrost-http/server/server.go, transports/bifrost-http/lib/config.go, transports/bifrost-http/lib/config_test.go
Removes EnterpriseOverrides from LoadConfig/LoadPlugins and related call sites; simplifies initialization; wires tracer and devPprof into server bootstrap.
Dev profiling & UI
transports/bifrost-http/handlers/devpprof*.go, ui/components/devProfiler.tsx, ui/lib/store/apis/devApi.ts, ui/app/clientLayout.tsx
Adds dev /api/dev/pprof endpoint, MetricsCollector and DevPprofHandler, DevProfiler React component and RTK Query slice; client layout includes DevProfiler.
OTEL & observability
plugins/otel/*, plugins/otel/converter.go
Otel plugin refactored to accept traces via ObservabilityPlugin.Inject, conversion helpers added, TTL sync map removed, compile-time assertion added.
Routing & integrations
transports/bifrost-http/handlers/*, transports/bifrost-http/integrations/router.go, transports/bifrost-http/handlers/inference.go
Propagates middleware type changes; streaming handlers now defer trace completion until stream end; header-filter wiring tweaks.
Lib adjustments
transports/bifrost-http/lib/middleware.go, transports/bifrost-http/lib/config.go
Removes local BifrostHTTPMiddleware type, ChainMiddlewares now accepts schemas.BifrostHTTPMiddleware; config APIs drop EnterpriseOverrides.
Docs, changelogs & versions
docs/plugins/*, core/changelog.md, transports/changelog.md, core/version, framework/version, transports/version, .github/workflows/*
Adds migration guide and docs showing TransportInterceptor→HTTPTransportMiddleware; bumps core/framework/transports versions; CI scripts made branch-aware.
Misc UI & small cleanups
ui/components/*, ui/app/workspace/logs/*, assorted minor files
UI style tweaks, log-details dropdown, minor whitespace/formatting edits and test signature updates.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant TracingMW as TracingMiddleware
    participant Server as RootHandler
    participant Pipeline as PluginPipeline
    participant Store as TraceStore
    participant Obs as ObservabilityPlugin

    Client->>TracingMW: HTTP request (trace headers optional)
    activate TracingMW
    TracingMW->>Store: CreateTrace(parent?)
    activate Store
    Store-->>TracingMW: traceID
    deactivate Store

    TracingMW->>Server: call next(ctx with traceID)
    activate Server
    Server->>Pipeline: PreHook → LLM call / stream → PostHook
    activate Pipeline
    Pipeline-->>Server: response (streaming may defer finalization)
    deactivate Pipeline
    Server-->>TracingMW: response returned
    deactivate Server

    alt streaming deferred
        TracingMW->>Store: CompleteTrace(traceID) after final chunk
        activate Store
        Store-->>TracingMW: completedTrace
        deactivate Store
    end

    TracingMW->>Obs: Inject(ctx, completedTrace)
    activate Obs
    Obs-->>TracingMW: ack
    deactivate Obs

    TracingMW-->>Client: HTTP response (with trace headers)
    deactivate TracingMW
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐇 I hopped through spans and stitched their names,

I traced the streams and counted tiny frames.
Middleware nudged the chain along its way,
Deferred the final hop until the sway.
Dev pprof hums — I wiggle, sniff, and play.

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title "plugins v2 architecture" is vague and generic, lacking specific details about the primary change (TransportInterceptor to HTTPTransportMiddleware). Consider a more descriptive title such as "Replace TransportInterceptor with HTTPTransportMiddleware" to clearly communicate the main change.
✅ Passed checks (2 passed)
Check name Status Explanation
Description check ✅ Passed The PR description covers all required sections including summary, changes, type of change, affected areas, testing instructions, breaking changes, and a checklist, meeting the template requirements adequately.
Docstring Coverage ✅ Passed Docstring coverage is 93.50% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 11-19-plugins_v2_architecture

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

@github-actions
Copy link
Copy Markdown
Contributor

🧪 Test Suite Available

This PR can be tested by a repository admin.

Run tests for PR #888

@akshaydeo akshaydeo force-pushed the 11-19-plugins_v2_architecture branch 2 times, most recently from ec9703b to de6897f Compare November 20, 2025 13:03
@akshaydeo akshaydeo marked this pull request as ready for review November 20, 2025 13:06
@akshaydeo akshaydeo force-pushed the 11-19-plugins_v2_architecture branch from de6897f to e32d6a2 Compare November 20, 2025 13:08
Copy link
Copy Markdown
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

Caution

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

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

48-48: Update comment reference to use HTTPTransportMiddleware.

Line 48 still references "TransportInterceptor" in the execution order documentation. This should be updated to "HTTPTransportMiddleware" for consistency.

Apply this diff:

-// 1. TransportInterceptor (HTTP transport only, modifies raw headers/body before entering Bifrost core)
+// 1. HTTPTransportMiddleware (HTTP transport only, modifies raw headers/body before entering Bifrost core)
transports/bifrost-http/handlers/middlewares.go (1)

48-81: Critical: Middleware execution instead of composition.

Line 78 immediately invokes the chained handler with (ctx) instead of returning it. This breaks the middleware pattern—the function should return a handler, not execute it.

Apply this diff:

-			lib.ChainMiddlewares(next, pluginsMiddlewareChain...)(ctx)			
+			lib.ChainMiddlewares(next, pluginsMiddlewareChain...)(ctx)
 		}
 	}
 }

Wait, let me reconsider. Looking at the structure:

func TransportInterceptorMiddleware(config *lib.Config) schemas.BifrostHTTPMiddleware {
	return func(next fasthttp.RequestHandler) fasthttp.RequestHandler {
		return func(ctx *fasthttp.RequestCtx) {
			// ... logic ...
			lib.ChainMiddlewares(next, pluginsMiddlewareChain...)(ctx)
		}
	}
}

Actually, the outer function returns func(next) handler, and the inner function returns func(ctx). At line 78, we're inside the innermost function that takes ctx, so calling (ctx) makes sense here. But the issue is that lib.ChainMiddlewares returns a handler, and we need to call it with ctx.

Actually, on closer inspection, this looks correct. The pattern is:

  1. TransportInterceptorMiddleware returns a middleware (a function that takes a handler and returns a handler)
  2. That middleware's returned handler is a function that takes ctx
  3. Inside that function, we chain middlewares and invoke the result with ctx

So the code is correct. Let me reconsider...

Actually, I need to look more carefully. Line 78:

lib.ChainMiddlewares(next, pluginsMiddlewareChain...)(ctx)

This chains the middlewares starting with next as the base handler, wrapping it with the plugin middlewares, and then calls the resulting handler with ctx. This seems correct for the pattern.

But wait - there's a potential issue. If any of the earlier paths call next(ctx), and then we also call the chained middlewares, we'd be calling the handler twice. Let me check the early returns...

Lines 54-56: If no plugins, call next(ctx) and return ✓
Lines 66-68: If no governance, call next(ctx) and return ✓
Line 78: Otherwise, call chained middlewares

This looks correct actually. My initial concern was wrong.

However, I notice another issue: if a plugin returns nil for HTTPTransportMiddleware(), we skip it (lines 73-75), but what if ALL plugins return nil? Then pluginsMiddlewareChain would be empty, and we'd call ChainMiddlewares with just next and empty variadic args, which should work fine according to lines 12-15 of lib/middleware.go.

Actually, I think the code is correct. Let me reconsider if there are any other issues...

One thing I notice: the governance skip logic (lines 57-68) only executes the middleware chain if the governance plugin is loaded. This seems like a significant behavioral change - previously, TransportInterceptor would be called on all plugins, but now the HTTP middleware is only chained if governance is present. This might be intentional but should be verified.
[scratchpad_end] -->

♻️ Duplicate comments (2)
plugins/telemetry/main.go (1)

278-281: Same nil-middleware semantics concern as logging plugin

This HTTPTransportMiddleware stub mirrors logging’s pattern (returning nil to signal “no transport middleware”). That’s fine as long as the framework checks for nil before chaining plugin middlewares; otherwise a nil call will panic.

Please ensure the shared plugin wiring handles nil consistently; no change needed here beyond that.

plugins/mocker/main.go (1)

483-486: Mocker plugin’s HTTPTransportMiddleware stub follows the same pattern

Returning nil here is consistent with the other plugins and is a reasonable “no HTTP middleware” signal, provided the caller treats nil as “don’t apply this middleware” rather than invoking it.

Given the repeated pattern, it’s worth double‑checking the central wiring once to ensure nils are filtered out before chaining.

🧹 Nitpick comments (3)
plugins/governance/go.mod (1)

10-11: Verify version appropriateness and assess architectural necessity of direct fasthttp/sonic dependencies.

The governance plugin now has direct dependencies on bytedance/sonic and valyala/fasthttp, which aligns with the new HTTPTransportMiddleware pattern. However, clarify:

  1. Whether these versions are secure and stable (especially given the "chill" review context, but still important).
  2. Whether the governance plugin itself directly uses these libraries, or if they're being exposed as transitive dependencies from the core/framework modules.

If the governance plugin doesn't directly instantiate fasthttp handlers or use sonic for serialization, consider whether these dependencies should be imported only in the transport layer (core or framework) rather than propagating to plugins.

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

9-32: HealthHandler middleware type updated; small comment mismatch

The change to ...schemas.BifrostHTTPMiddleware plus the new schemas import correctly aligns the health route with the shared middleware type and keeps /health behavior intact.

The doc comment for getHealth still says GET /api/health while the actual route is /health; consider updating the comment to avoid confusion.

plugins/logging/main.go (1)

186-189: Document and validate the semantics of a nil HTTPTransportMiddleware

HTTPTransportMiddleware returning nil is a reasonable “no-op” implementation, but only if the transport wiring checks for nil before composing middleware chains. Otherwise, a nil call could panic at runtime.

I’d recommend:

  • Treating nil explicitly as “no middleware” in the code that collects and applies plugin middlewares, and
  • Leaving this method as-is here, given the comment and to avoid extra fasthttp dependencies.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d6626f2 and e32d6a2.

⛔ Files ignored due to path filters (1)
  • examples/plugins/hello-world/go.sum is excluded by !**/*.sum
📒 Files selected for processing (32)
  • core/schemas/plugin.go (3 hunks)
  • examples/plugins/hello-world/go.mod (1 hunks)
  • examples/plugins/hello-world/main.go (2 hunks)
  • framework/plugins/dynamicplugin.go (3 hunks)
  • framework/plugins/dynamicplugin_test.go (2 hunks)
  • plugins/governance/go.mod (1 hunks)
  • plugins/governance/main.go (2 hunks)
  • plugins/jsonparser/main.go (1 hunks)
  • plugins/logging/main.go (1 hunks)
  • plugins/maxim/main.go (1 hunks)
  • plugins/mocker/main.go (1 hunks)
  • plugins/otel/main.go (1 hunks)
  • plugins/semanticcache/main.go (1 hunks)
  • plugins/telemetry/main.go (1 hunks)
  • transports/bifrost-http/handlers/cache.go (1 hunks)
  • transports/bifrost-http/handlers/config.go (2 hunks)
  • transports/bifrost-http/handlers/governance.go (2 hunks)
  • transports/bifrost-http/handlers/health.go (2 hunks)
  • transports/bifrost-http/handlers/inference.go (1 hunks)
  • transports/bifrost-http/handlers/integrations.go (2 hunks)
  • transports/bifrost-http/handlers/logging.go (1 hunks)
  • transports/bifrost-http/handlers/mcp.go (1 hunks)
  • transports/bifrost-http/handlers/middlewares.go (5 hunks)
  • transports/bifrost-http/handlers/middlewares_test.go (8 hunks)
  • transports/bifrost-http/handlers/plugins.go (1 hunks)
  • transports/bifrost-http/handlers/providers.go (1 hunks)
  • transports/bifrost-http/handlers/session.go (2 hunks)
  • transports/bifrost-http/handlers/ui.go (2 hunks)
  • transports/bifrost-http/handlers/websocket.go (2 hunks)
  • transports/bifrost-http/integrations/router.go (2 hunks)
  • transports/bifrost-http/lib/middleware.go (1 hunks)
  • transports/bifrost-http/server/server.go (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (29)
transports/bifrost-http/handlers/integrations.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/config.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/integrations/router.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/websocket.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/cache.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
plugins/logging/main.go (2)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (20-25)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
plugins/semanticcache/main.go (2)
core/schemas/plugin.go (2)
  • Plugin (71-97)
  • BifrostHTTPMiddleware (38-38)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (20-25)
plugins/maxim/main.go (2)
core/schemas/plugin.go (2)
  • Plugin (71-97)
  • BifrostHTTPMiddleware (38-38)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (20-25)
transports/bifrost-http/handlers/governance.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/middlewares.go (4)
transports/bifrost-http/lib/config.go (1)
  • Config (139-169)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (20-25)
transports/bifrost-http/lib/middleware.go (1)
  • ChainMiddlewares (11-23)
transports/bifrost-http/lib/middleware.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/mcp.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/plugins.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
framework/plugins/dynamicplugin.go (2)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (20-25)
transports/bifrost-http/handlers/inference.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/providers.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/logging.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/session.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
plugins/telemetry/main.go (2)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (20-25)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/health.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/ui.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
plugins/otel/main.go (2)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (20-25)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
framework/plugins/dynamicplugin_test.go (1)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (20-25)
plugins/mocker/main.go (2)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (20-25)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
plugins/jsonparser/main.go (2)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (20-25)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/middlewares_test.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
plugins/governance/main.go (2)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (20-25)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/server/server.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
core/schemas/plugin.go (1)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (20-25)
🔇 Additional comments (38)
examples/plugins/hello-world/go.mod (2)

10-22: Indirect dependencies properly declared.

The transitive dependencies from fasthttp are correctly listed with // indirect markers. This formatting aligns with Go module best practices and makes the dependency graph transparent.


5-8: ---

Dependencies are appropriately versioned for the middleware architecture migration.

The addition of github.com/valyala/fasthttp v1.67.0 and github.com/maximhq/bifrost/core v1.2.27 correctly supports the HTTPTransportMiddleware pattern. Both versions are valid and stable, with fasthttp v1.67.0 including security fixes for URL validation and host parsing.

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

11-39: Align SessionHandler middleware type with core schemas

RegisterRoutes now uses ...schemas.BifrostHTTPMiddleware and the new schemas import; this cleanly aligns session routes with the centralized middleware type while preserving the existing routing behavior via lib.ChainMiddlewares.

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

26-29: CacheHandler RegisterRoutes migrated to schemas middleware type

Using ...schemas.BifrostHTTPMiddleware for cache routes is consistent with the shared middleware abstraction and works seamlessly with lib.ChainMiddlewares.

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

41-47: LoggingHandler now uses schemas.BifrostHTTPMiddleware

Switching RegisterRoutes to ...schemas.BifrostHTTPMiddleware standardizes logging routes on the new middleware type without altering any logging behavior.

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

78-86: ProviderHandler RegisterRoutes aligned with schemas middleware

The provider management routes now take ...schemas.BifrostHTTPMiddleware, matching the shared middleware type across the transport layer while preserving all existing provider CRUD semantics.

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

42-50: MCPHandler routes migrated to schemas.BifrostHTTPMiddleware

Using ...schemas.BifrostHTTPMiddleware here brings MCP endpoints in line with the new middleware contract while keeping all MCP behavior and routing identical.

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

5-40: IntegrationHandler standardized on schemas.BifrostHTTPMiddleware

Adding the schemas import and updating RegisterRoutes to ...schemas.BifrostHTTPMiddleware cleanly propagates the new middleware type into all integration extensions via extension.RegisterRoutes(r, middlewares...) without changing routing semantics.

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

14-55: ConfigHandler routes moved to schemas.BifrostHTTPMiddleware

The added schemas import and updated RegisterRoutes signature (...schemas.BifrostHTTPMiddleware) bring config and version endpoints in line with the unified middleware abstraction while leaving all config behavior as-is.

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

53-59: Aligning PluginsHandler with shared middleware type looks correct

Switching RegisterRoutes to ...schemas.BifrostHTTPMiddleware keeps handler wiring consistent with the new central middleware contract while preserving existing route behavior via lib.ChainMiddlewares. No issues from this change alone.

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

6-6: Tests correctly updated to use the shared BifrostHTTPMiddleware type

Switching the middleware constructions to schemas.BifrostHTTPMiddleware (with the new import) keeps the test behavior identical while aligning with the central middleware type definition. The ordering, short‑circuit, and context‑mutation scenarios are all still clearly exercised, and the explicit conversions are type‑correct.

Also applies to: 295-301, 322-342, 368-373, 395-402, 405-418, 459-465, 467-475, 477-483

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

15-22: Governance routes now correctly use the shared middleware contract

Updating RegisterRoutes to accept ...schemas.BifrostHTTPMiddleware and leaving the lib.ChainMiddlewares calls intact cleanly aligns governance handlers with the new middleware model without changing route behavior.

Also applies to: 154-175

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

274-285: Inference handler middleware signature migration is consistent

CompletionHandler.RegisterRoutes now taking ...schemas.BifrostHTTPMiddleware matches the repository‑wide middleware type migration and should remain compatible with lib.ChainMiddlewares as updated in this PR.

examples/plugins/hello-world/main.go (1)

8-9: Example HTTPTransportMiddleware implementation looks correct

The hello‑world plugin’s HTTPTransportMiddleware has the expected func(next fasthttp.RequestHandler) fasthttp.RequestHandler signature, logs once per request, and correctly forwards to next(ctx), making it a clear, minimal example for plugin authors.

Also applies to: 20-25

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

70-70: LGTM! Consistent middleware type migration.

The migration from lib.BifrostHTTPMiddleware to schemas.BifrostHTTPMiddleware is clean and consistent across both the interface definition and implementation. This centralizes the middleware type in the schemas package, improving code organization.

Also applies to: 232-232


11-11: LGTM! Clean middleware type migration.

The handler correctly adopts the new schemas.BifrostHTTPMiddleware type with the necessary import addition. Consistent with the broader architectural refactor.

Also applies to: 29-29

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

14-14: LGTM! Consistent with the middleware migration.

The WebSocket handler correctly adopts the new middleware type from the schemas package.

Also applies to: 51-51

framework/plugins/dynamicplugin_test.go (1)

53-83: LGTM! Well-structured test for the new middleware API.

The test correctly verifies the middleware pattern:

  1. Creates a mock next handler
  2. Wraps it with the plugin's middleware
  3. Verifies the wrapped handler calls the next handler

This properly validates the middleware contract established by the new HTTPTransportMiddleware API.

plugins/jsonparser/main.go (1)

87-90: LGTM! Appropriate no-op middleware.

The plugin correctly implements the new HTTPTransportMiddleware method, returning nil to indicate no middleware is needed. This is the expected pattern for plugins that don't require HTTP transport interception.

core/schemas/plugin.go (1)

36-38: LGTM! Well-defined middleware type.

The BifrostHTTPMiddleware type follows the standard fasthttp middleware pattern: func(next fasthttp.RequestHandler) fasthttp.RequestHandler. This is a clean and idiomatic approach for HTTP middleware.

plugins/semanticcache/main.go (1)

338-341: LGTM! Consistent no-op middleware implementation.

The semantic cache plugin correctly implements the new middleware API, returning nil as it doesn't require HTTP transport interception.

plugins/otel/main.go (1)

146-149: LGTM! Consistent with the plugins v2 architecture.

The OTEL plugin correctly implements the new HTTPTransportMiddleware method, returning nil as it doesn't need HTTP-level middleware.

transports/bifrost-http/lib/middleware.go (1)

3-23: LGTM! Clean type migration.

The refactor correctly centralizes the middleware type definition in core/schemas while preserving the chaining logic.

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

20-45: LGTM! Type signature updated correctly.

The CORS middleware logic remains unchanged; only the return type was updated to use the centralized type definition.


99-200: LGTM! Type signature updated correctly.

The authentication middleware logic is preserved; only the return type signature was updated.


57-68: Governance gate is functionally correct but adds hidden behavioral coupling.

The governance gate (lines 57-68) is safe—only the governance plugin provides non-nil middleware, so gating execution when governance is absent doesn't skip any plugins. However, it introduces a brittle assumption: the code assumes governance is the exclusive middleware provider. If another plugin adds middleware in the future, it would silently bypass the gate and never execute.

Rather than gating on a specific plugin name, the code should filter collected middlewares and check if any are non-nil, making intent explicit and future-proof.

pluginsMiddlewareChain := []schemas.BifrostHTTPMiddleware{}
for _, plugin := range plugins {
	middleware := plugin.HTTPTransportMiddleware()
	if middleware == nil {
		continue
	}
	pluginsMiddlewareChain = append(pluginsMiddlewareChain, middleware)
}
if len(pluginsMiddlewareChain) > 0 {
	lib.ChainMiddlewares(next, pluginsMiddlewareChain...)(ctx)
} else {
	next(ctx)
}
plugins/maxim/main.go (1)

125-128: LGTM! Correct no-op middleware implementation.

The maxim plugin correctly returns nil for HTTP transport middleware since it operates at the PreHook/PostHook level rather than the HTTP transport layer.

transports/bifrost-http/server/server.go (5)

844-850: LGTM! Method signature updated correctly.

The route registration methods now properly accept the centralized middleware type.


853-930: LGTM! Consistent middleware type usage.

All handler registrations correctly use the updated middleware type throughout the method.


933-936: LGTM! UI route registration updated correctly.

The method signature properly reflects the middleware type change.


965-976: LGTM! Common middleware preparation updated correctly.

The method correctly builds and returns a slice of the centralized middleware type.


1077-1082: LGTM! Server middleware chain configured correctly.

The middleware composition properly chains CORS → TransportInterceptor → Router, ensuring the correct execution order.

plugins/governance/main.go (3)

158-174: LGTM! Middleware structure and early validation correct.

The middleware factory pattern is properly implemented with appropriate early returns for missing or invalid virtual keys.


195-209: LGTM! Load balancing integration correct.

The middleware properly modifies the request body with provider load balancing before forwarding to the next handler.


213-305: LGTM! Load balancing logic is sound.

The weighted random selection with budget/rate limit awareness and fallback handling is well-implemented with appropriate defensive checks.

framework/plugins/dynamicplugin.go (2)

26-40: LGTM! Dynamic plugin API updated correctly.

The field and method changes properly align with the new middleware-based plugin interface.


141-148: LGTM! Symbol lookup updated correctly.

The dynamic plugin loader properly looks up and casts the new HTTPTransportMiddleware symbol with clear error messages.

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

11-31: Middleware type migration to core/schemas looks consistent

Switching RegisterRoutes to use schemas.BifrostHTTPMiddleware and importing core/schemas aligns this handler with the shared middleware contract used elsewhere. The variadic signature and continued use of lib.ChainMiddlewares keep the API surface unchanged for callers while centralizing the type definition in core.

Comment thread core/schemas/plugin.go
Comment thread plugins/governance/main.go
@akshaydeo akshaydeo force-pushed the 11-19-plugins_v2_architecture branch 2 times, most recently from 9be42ea to c08c82f Compare November 24, 2025 07:22
Copy link
Copy Markdown
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 (1)
core/schemas/plugin.go (1)

71-80: Update execution-order docs to reference HTTPTransportMiddleware instead of TransportInterceptor

The Plugin interface now exposes HTTPTransportMiddleware() but the execution-order comments above still refer to TransportInterceptor; updating those comments to describe HTTPTransportMiddleware and its place in the pipeline will avoid confusion for plugin authors.

♻️ Duplicate comments (1)
plugins/governance/main.go (1)

214-227: Fix misleading log message on JSON unmarshal error

The log message at the sonic.Unmarshal failure path still says “failed to marshal”, which is inaccurate and repeats a previously flagged issue.

Apply this diff to clarify the error:

-			err = sonic.Unmarshal(ctx.Request.Body(), &payload)
-			if err != nil {
-				p.logger.Error("failed to marshal request body to check for virtual key: %v", err)
+			err = sonic.Unmarshal(ctx.Request.Body(), &payload)
+			if err != nil {
+				p.logger.Error("failed to unmarshal request body to check for virtual key: %v", err)
 				next(ctx)
 				return
 			}
🧹 Nitpick comments (3)
transports/bifrost-http/lib/middleware.go (1)

3-22: Middleware chaining now correctly standardizes on schemas.BifrostHTTPMiddleware

Switching ChainMiddlewares to use schemas.BifrostHTTPMiddleware keeps behavior identical while centralizing the type in schemas; just ensure all callers continue to filter out nil middlewares, or consider skipping nil inside the loop as a small safety net in the future.

plugins/governance/main.go (2)

13-20: HTTP middleware and virtual key parsing look sound; consider minor robustness tweaks

The new parseVirtualKey + HTTPTransportMiddleware flow is well-structured: it cleanly short-circuits when there’s no/invalid VK, validates via p.store.GetVirtualKey, enriches headers via addMCPIncludeTools, and rewrites the JSON body with loadBalanceProvider while always falling back to next(ctx) on errors. That gives safe behavior even with malformed or non-JSON bodies.

Two small optional improvements:

  1. Body presence check
    ctx.Request.Body() can be non‑nil but empty; in that case sonic.Unmarshal will just fail and you fall back, which is safe but noisy. You could avoid unnecessary log noise with a length check:
-			if ctx.Request.Body() == nil {
+			if len(ctx.Request.Body()) == 0 {
 				next(ctx)
 				return
 			}
  1. Unused error from addMCPIncludeTools
    addMCPIncludeTools never returns a non‑nil error, so the err handling here is effectively dead. Either drop the error from the helper’s signature (if you control all call sites) or add a brief comment noting that it currently can’t fail to avoid confusion for future readers.

Both are non‑blocking polish items; functionally this middleware looks correct.

Also applies to: 158-181, 183-235


70-79: Update Init doc comment to reference HTTPTransportMiddleware instead of TransportInterceptor

The Init comment still mentions TransportInterceptor for how inMemoryStore is used, but the implementation has moved that responsibility into HTTPTransportMiddleware. Updating the comment will prevent confusion for future maintainers.

For example:

-//   - `inMemoryStore` is used by TransportInterceptor to validate configured providers
+//   - `inMemoryStore` is used by HTTPTransportMiddleware to validate configured 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 9be42ea and c08c82f.

⛔ Files ignored due to path filters (1)
  • examples/plugins/hello-world/go.sum is excluded by !**/*.sum
📒 Files selected for processing (32)
  • core/schemas/plugin.go (3 hunks)
  • examples/plugins/hello-world/go.mod (1 hunks)
  • examples/plugins/hello-world/main.go (2 hunks)
  • framework/plugins/dynamicplugin.go (3 hunks)
  • framework/plugins/dynamicplugin_test.go (2 hunks)
  • plugins/governance/go.mod (1 hunks)
  • plugins/governance/main.go (3 hunks)
  • plugins/jsonparser/main.go (1 hunks)
  • plugins/logging/main.go (1 hunks)
  • plugins/maxim/main.go (1 hunks)
  • plugins/mocker/main.go (1 hunks)
  • plugins/otel/main.go (1 hunks)
  • plugins/semanticcache/main.go (1 hunks)
  • plugins/telemetry/main.go (1 hunks)
  • transports/bifrost-http/handlers/cache.go (1 hunks)
  • transports/bifrost-http/handlers/config.go (2 hunks)
  • transports/bifrost-http/handlers/governance.go (2 hunks)
  • transports/bifrost-http/handlers/health.go (2 hunks)
  • transports/bifrost-http/handlers/inference.go (1 hunks)
  • transports/bifrost-http/handlers/integrations.go (2 hunks)
  • transports/bifrost-http/handlers/logging.go (1 hunks)
  • transports/bifrost-http/handlers/mcp.go (1 hunks)
  • transports/bifrost-http/handlers/middlewares.go (3 hunks)
  • transports/bifrost-http/handlers/middlewares_test.go (8 hunks)
  • transports/bifrost-http/handlers/plugins.go (1 hunks)
  • transports/bifrost-http/handlers/providers.go (1 hunks)
  • transports/bifrost-http/handlers/session.go (2 hunks)
  • transports/bifrost-http/handlers/ui.go (2 hunks)
  • transports/bifrost-http/handlers/websocket.go (2 hunks)
  • transports/bifrost-http/integrations/router.go (2 hunks)
  • transports/bifrost-http/lib/middleware.go (1 hunks)
  • transports/bifrost-http/server/server.go (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (13)
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/handlers/config.go
  • plugins/telemetry/main.go
  • transports/bifrost-http/handlers/session.go
  • framework/plugins/dynamicplugin.go
  • transports/bifrost-http/server/server.go
  • transports/bifrost-http/handlers/cache.go
  • transports/bifrost-http/handlers/health.go
  • plugins/semanticcache/main.go
  • plugins/otel/main.go
  • plugins/governance/go.mod
🧰 Additional context used
🧬 Code graph analysis (18)
transports/bifrost-http/handlers/websocket.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/integrations.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/logging.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
plugins/governance/main.go (3)
core/utils.go (1)
  • Ptr (51-53)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (20-27)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/providers.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
plugins/maxim/main.go (2)
core/schemas/plugin.go (2)
  • Plugin (71-97)
  • BifrostHTTPMiddleware (38-38)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (20-27)
transports/bifrost-http/handlers/plugins.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
examples/plugins/hello-world/main.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/middlewares_test.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
plugins/jsonparser/main.go (2)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (20-27)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/lib/middleware.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
core/schemas/plugin.go (1)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (20-27)
framework/plugins/dynamicplugin_test.go (1)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (20-27)
transports/bifrost-http/handlers/mcp.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
plugins/logging/main.go (2)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (20-27)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/middlewares.go (3)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (20-27)
transports/bifrost-http/lib/middleware.go (1)
  • ChainMiddlewares (11-23)
plugins/mocker/main.go (2)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (20-27)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/ui.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
🔇 Additional comments (20)
examples/plugins/hello-world/go.mod (2)

7-7: fasthttp addition aligns with middleware pattern transition.

The addition of github.com/valyala/fasthttp v1.67.0 as a direct dependency is an expected change that aligns with the PR's transition to an HTTP middleware pattern. The transitive indirect dependencies (brotli, compress, bytebufferpool) are standard fasthttp dependencies and are appropriate.


6-6: Unable to access repository files for verification.

The review comment references examples/plugins/hello-world/go.mod, but I cannot access the codebase files in the current sandbox environment to independently verify the core dependency downgrade claim or check version consistency across other modules in the repository.

The original analysis flagged a downgrade of github.com/maximhq/bifrost/core from v1.2.29 to v1.2.27 as potentially problematic. Without access to the actual files, I cannot confirm:

  • Whether this downgrade is intentional
  • If version consistency exists across other modules
  • What functionality or security implications might be affected
transports/bifrost-http/handlers/providers.go (1)

78-78: LGTM! Middleware type migration is consistent.

The update from lib.BifrostHTTPMiddleware to schemas.BifrostHTTPMiddleware aligns with the architectural shift to centralize the middleware type definition in the core schemas package.

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

11-11: LGTM! Clean type migration with proper import.

The import addition and middleware type update are consistent with the broader migration to schemas.BifrostHTTPMiddleware.

Also applies to: 29-29

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

42-42: LGTM! Middleware type updated consistently.

The RegisterRoutes signature update to schemas.BifrostHTTPMiddleware is consistent with the architectural refactor across the codebase.

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

53-53: LGTM! Type migration applied consistently.

The middleware type update maintains consistency with the broader architectural shift to centralize middleware types in the schemas package.

plugins/jsonparser/main.go (1)

87-90: LGTM! Plugin correctly implements new middleware interface.

The implementation of HTTPTransportMiddleware() returning nil is appropriate for this plugin, as indicated by the comment. This aligns with the migration from TransportInterceptor to the new middleware-based approach.

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

42-42: LGTM! Middleware type migration is consistent.

The RegisterRoutes signature update follows the standard pattern for migrating to schemas.BifrostHTTPMiddleware.

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

6-6: LGTM! Tests updated consistently with type migration.

All test middleware references have been properly updated from lib.BifrostHTTPMiddleware to schemas.BifrostHTTPMiddleware. The test logic remains unchanged, ensuring the migration is purely structural.

Also applies to: 295-295, 322-322, 329-329, 336-336, 368-368, 395-395, 405-405, 413-413, 458-458, 467-467, 477-477

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

8-8: LGTM! Integration handler properly migrated.

The import addition and RegisterRoutes signature update are consistent with the architectural refactor. The variadic middleware delegation to extension routers remains functionally identical.

Also applies to: 35-35

framework/plugins/dynamicplugin_test.go (1)

53-83: Dynamic plugin middleware test correctly validates chaining behavior

The new HTTPTransportMiddleware subtest exercises the middleware constructor, wrapped handler, and ensures next is invoked with a realistic fasthttp.RequestCtx setup; this is sufficient to validate the new dynamic-plugin hook wiring.

plugins/logging/main.go (1)

186-189: No-op HTTPTransportMiddleware is a valid opt-out for this plugin

Returning nil here is a clean way for the logging plugin to signal “no HTTP-level middleware”, and the transport layer already guards against nil middlewares before chaining.

examples/plugins/hello-world/main.go (1)

8-27: Example plugin’s HTTPTransportMiddleware correctly implements the new contract

The exported HTTPTransportMiddleware returns a schemas.BifrostHTTPMiddleware that logs then calls next, which is exactly what the dynamic plugin loader expects for the new HTTP transport hook.

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

14-53: WebSocket route registration now aligned with shared middleware type

Using ...schemas.BifrostHTTPMiddleware in RegisterRoutes and feeding them through lib.ChainMiddlewares aligns WebSocket routes with the unified middleware abstraction without changing behavior.

core/schemas/plugin.go (1)

4-39: BifrostHTTPMiddleware type cleanly defines the shared HTTP middleware contract

Defining BifrostHTTPMiddleware in schemas gives a single, clear function type for all HTTP middleware, and using fasthttp.RequestHandler directly matches the transport layer’s expectations.

plugins/mocker/main.go (1)

479-486: Mocker plugin cleanly opts out of HTTP transport middleware

Returning nil from MockerPlugin.HTTPTransportMiddleware is consistent with the interface and works with the interceptor middleware’s nil filtering to indicate this plugin has no HTTP-layer behavior.

transports/bifrost-http/handlers/middlewares.go (2)

18-44: CORS middleware correctly updated to use schemas.BifrostHTTPMiddleware

The CORS logic is unchanged and wrapping it in schemas.BifrostHTTPMiddleware matches the new shared middleware abstraction with no behavioral impact.


86-187: Auth middleware’s type change cleanly adopts the shared middleware abstraction

Changing AuthMiddleware to return schemas.BifrostHTTPMiddleware while keeping the behavior (including the identity middleware when auth is disabled or store is nil) preserves existing auth semantics and fits naturally into the new middleware chain.

plugins/maxim/main.go (1)

125-128: Nil HTTPTransportMiddleware is an acceptable no-op implementation

Returning nil here is a clear way to indicate this plugin does not participate in HTTP middleware and cleanly satisfies the updated Plugin interface. As long as the transport layer skips nil middlewares (as it does for other plugins in this PR), this is fine.

plugins/governance/main.go (1)

370-377: Updated PreHook error message better matches behavior

Changing the PreHook error message to “virtual key is missing or invalid” aligns with the actual semantics (empty/missing or rejected VK) and improves clarity for clients without altering logic.

Comment thread transports/bifrost-http/handlers/middlewares.go Outdated
@akshaydeo akshaydeo changed the base branch from main to graphite-base/888 December 22, 2025 09:12
@akshaydeo akshaydeo force-pushed the 11-19-plugins_v2_architecture branch from c08c82f to 13f8738 Compare December 22, 2025 09:12
@akshaydeo akshaydeo changed the base branch from graphite-base/888 to v1.4.0 December 22, 2025 09:12
Copy link
Copy Markdown
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/schemas/plugin.go (1)

46-46: Update documentation to reflect new method name.

Line 46 still refers to "TransportInterceptor" in the execution order documentation, but the method has been renamed to "HTTPTransportMiddleware". Update the comment for consistency.

🔎 Proposed fix
-// 1. TransportInterceptor (HTTP transport only, modifies raw headers/body before entering Bifrost core)
+// 1. HTTPTransportMiddleware (HTTP transport only, modifies raw headers/body before entering Bifrost core)
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c08c82f and 13f8738.

📒 Files selected for processing (1)
  • core/schemas/plugin.go
🧰 Additional context used
📓 Path-based instructions (1)
**

⚙️ CodeRabbit configuration file

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

Files:

  • core/schemas/plugin.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/schemas/plugin.go
🧬 Code graph analysis (1)
core/schemas/plugin.go (1)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-26)
🔇 Additional comments (3)
core/schemas/plugin.go (3)

4-6: LGTM! Import correctly added for middleware pattern.

The fasthttp import is necessary for the BifrostHTTPMiddleware type definition and follows the standard middleware pattern.


34-36: LGTM! Clean middleware type definition.

The BifrostHTTPMiddleware type correctly follows the standard middleware constructor pattern and is well-documented.


73-77: LGTM! Clean migration to middleware pattern.

The HTTPTransportMiddleware() method properly replaces the old TransportInterceptor approach with a standard middleware constructor pattern. The documentation clearly explains the new behavior.

Note: The hello-world example issue (missing method receiver) has already been flagged in previous review comments.

@akshaydeo akshaydeo force-pushed the 11-19-plugins_v2_architecture branch from 13f8738 to fa4b8d4 Compare December 22, 2025 09:33
Copy link
Copy Markdown
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 (1)
core/schemas/plugin.go (1)

46-46: Update comment to reference HTTPTransportMiddleware.

Line 46 still references the old method name "TransportInterceptor" in the execution order documentation.

🔎 Proposed fix
-// 1. TransportInterceptor (HTTP transport only, modifies raw headers/body before entering Bifrost core)
+// 1. HTTPTransportMiddleware (HTTP transport only, modifies raw headers/body before entering Bifrost core)
 // 2. PreHook (executed in registration order)
 // 3. Provider call
 // 4. PostHook (executed in reverse order of PreHooks)
♻️ Duplicate comments (3)
examples/plugins/hello-world/main.go (1)

19-26: Fix the hello-world example: HTTPTransportMiddleware must be a method with a receiver.

The HTTPTransportMiddleware function is defined as a free function without a receiver, which does not properly implement the Plugin interface. To correctly implement the interface, it should be a method on a plugin struct type (e.g., func (p *HelloWorldPlugin) HTTPTransportMiddleware() schemas.BifrostHTTPMiddleware).

🔎 Proposed fix

First, define a plugin struct type near the top of the file:

+type HelloWorldPlugin struct{}
+
 func Init(config any) error {
 	fmt.Println("Init called")
-	return nil
+	return nil
 }

Then update the function to be a method on that struct:

-func HTTPTransportMiddleware() schemas.BifrostHTTPMiddleware {
+func (p *HelloWorldPlugin) HTTPTransportMiddleware() schemas.BifrostHTTPMiddleware {
 	return func(next fasthttp.RequestHandler) fasthttp.RequestHandler {
 		return func(ctx *fasthttp.RequestCtx) {
 			fmt.Println("HTTPTransportMiddleware called")
 			next(ctx)
 		}
 	}
 }

Similarly, update all other exported functions to be methods on the plugin struct (PreHook, PostHook, GetName, Cleanup).

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

47-68: Update comments to reflect the new middleware terminology.

The function and inline comments still reference "interceptors" (lines 47, 60) despite the code now using HTTPTransportMiddleware(). Update the comments to reference "HTTP transport middleware" for consistency with the new API.

plugins/governance/main.go (1)

212-217: Fix error message accuracy.

Line 214: The error message says "failed to marshal" but the operation is sonic.Unmarshal. This was flagged in a previous review.

🔎 Proposed fix
-			p.logger.Error("failed to marshal request body to check for virtual key: %v", err)
+			p.logger.Error("failed to unmarshal request body: %v", err)
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 13f8738 and fa4b8d4.

⛔ Files ignored due to path filters (1)
  • examples/plugins/hello-world/go.sum is excluded by !**/*.sum
📒 Files selected for processing (36)
  • core/mcp.go
  • core/schemas/plugin.go
  • examples/plugins/hello-world/go.mod
  • examples/plugins/hello-world/main.go
  • framework/configstore/tables/mcp.go
  • framework/plugins/dynamicplugin.go
  • framework/plugins/dynamicplugin_test.go
  • plugins/governance/go.mod
  • plugins/governance/main.go
  • plugins/jsonparser/main.go
  • plugins/logging/main.go
  • plugins/maxim/main.go
  • plugins/mocker/main.go
  • plugins/otel/main.go
  • plugins/semanticcache/main.go
  • plugins/telemetry/main.go
  • transports/bifrost-http/handlers/cache.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/handlers/health.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/handlers/logging.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/handlers/mcpServer.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • transports/bifrost-http/handlers/plugins.go
  • transports/bifrost-http/handlers/providers.go
  • transports/bifrost-http/handlers/session.go
  • transports/bifrost-http/handlers/ui.go
  • transports/bifrost-http/handlers/websocket.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/lib/middleware.go
  • transports/bifrost-http/server/server.go
  • transports/go.mod
💤 Files with no reviewable changes (1)
  • framework/configstore/tables/mcp.go
🚧 Files skipped from review as they are similar to previous changes (12)
  • transports/bifrost-http/handlers/logging.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • transports/bifrost-http/handlers/session.go
  • plugins/telemetry/main.go
  • transports/bifrost-http/lib/middleware.go
  • framework/plugins/dynamicplugin_test.go
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/handlers/cache.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/handlers/ui.go
  • plugins/semanticcache/main.go
  • plugins/governance/go.mod
🧰 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/plugins.go
  • transports/bifrost-http/handlers/inference.go
  • core/mcp.go
  • transports/bifrost-http/handlers/websocket.go
  • plugins/otel/main.go
  • framework/plugins/dynamicplugin.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/handlers/providers.go
  • core/schemas/plugin.go
  • examples/plugins/hello-world/go.mod
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/handlers/health.go
  • plugins/logging/main.go
  • transports/go.mod
  • plugins/mocker/main.go
  • transports/bifrost-http/handlers/mcpServer.go
  • transports/bifrost-http/server/server.go
  • plugins/maxim/main.go
  • examples/plugins/hello-world/main.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/mcp.go
  • plugins/jsonparser/main.go
  • plugins/governance/main.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/handlers/plugins.go
  • transports/bifrost-http/handlers/inference.go
  • core/mcp.go
  • transports/bifrost-http/handlers/websocket.go
  • plugins/otel/main.go
  • framework/plugins/dynamicplugin.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/handlers/providers.go
  • core/schemas/plugin.go
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/handlers/health.go
  • plugins/logging/main.go
  • plugins/mocker/main.go
  • transports/bifrost-http/handlers/mcpServer.go
  • transports/bifrost-http/server/server.go
  • plugins/maxim/main.go
  • examples/plugins/hello-world/main.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/mcp.go
  • plugins/jsonparser/main.go
  • plugins/governance/main.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/plugins.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/handlers/websocket.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/handlers/providers.go
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/handlers/health.go
  • transports/bifrost-http/handlers/mcpServer.go
  • transports/bifrost-http/server/server.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/mcp.go
🧬 Code graph analysis (20)
transports/bifrost-http/handlers/plugins.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
transports/bifrost-http/handlers/inference.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
transports/bifrost-http/handlers/websocket.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
plugins/otel/main.go (2)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-26)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
framework/plugins/dynamicplugin.go (2)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-26)
transports/bifrost-http/handlers/middlewares.go (3)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-26)
transports/bifrost-http/lib/middleware.go (1)
  • ChainMiddlewares (11-23)
transports/bifrost-http/handlers/providers.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
core/schemas/plugin.go (1)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-26)
transports/bifrost-http/handlers/integrations.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
transports/bifrost-http/handlers/health.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
plugins/logging/main.go (2)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
transports/bifrost-http/handlers/inference.go (1)
  • TranscriptionRequest (285-289)
plugins/mocker/main.go (2)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-26)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
transports/bifrost-http/handlers/mcpServer.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
transports/bifrost-http/server/server.go (2)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
transports/bifrost-http/handlers/middlewares.go (1)
  • TransportInterceptorMiddleware (48-69)
plugins/maxim/main.go (1)
core/schemas/plugin.go (2)
  • Plugin (69-95)
  • BifrostHTTPMiddleware (36-36)
examples/plugins/hello-world/main.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
transports/bifrost-http/handlers/config.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
transports/bifrost-http/handlers/mcp.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
plugins/jsonparser/main.go (2)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-26)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
plugins/governance/main.go (3)
core/utils.go (1)
  • Ptr (56-58)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-26)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
🔇 Additional comments (29)
core/mcp.go (1)

1135-1150: In-process client now uses strongly typed InProcessServer

Passing config.InProcessServer directly into client.NewInProcessClient is fine and removes redundant runtime type assertions, assuming schemas.MCPClientConfig.InProcessServer is already declared as *server.MCPServer across the codebase.

Please confirm that InProcessServer is declared as *server.MCPServer (or a compatible concrete type) in schemas.MCPClientConfig so that this remains type-safe at compile time.

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

79-89: Provider routes now consume schemas.BifrostHTTPMiddleware

Updating RegisterRoutes to take ...schemas.BifrostHTTPMiddleware while still chaining via lib.ChainMiddlewares matches the new global middleware type and keeps the handler behavior unchanged.

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

15-62: Config routes aligned to schemas.BifrostHTTPMiddleware

Importing core/schemas and updating RegisterRoutes to accept ...schemas.BifrostHTTPMiddleware (still passed through lib.ChainMiddlewares) keeps the runtime behavior but unifies the middleware type with the rest of the HTTP stack.

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

52-59: Plugins handler now uses schema-level middleware type

Switching PluginsHandler.RegisterRoutes to ...schemas.BifrostHTTPMiddleware brings this handler in line with the new plugin middleware contract and with other HTTP handlers using the shared type.

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

364-390: Inference routes switched to schemas.BifrostHTTPMiddleware

Updating CompletionHandler.RegisterRoutes to take ...schemas.BifrostHTTPMiddleware while still delegating to lib.ChainMiddlewares cleanly adopts the new shared middleware type for all inference endpoints without changing routing behavior.

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

71-76: MCP server routes migrated to schemas.BifrostHTTPMiddleware

MCPServerHandler.RegisterRoutes now accepts ...schemas.BifrostHTTPMiddleware, which is consistent with the rest of the HTTP stack and the centralized Bifrost middleware type; the POST/GET /mcp behavior remains the same.

examples/plugins/hello-world/go.mod (1)

6-23: go.mod: fasthttp dependency aligns with existing modules

Adding github.com/valyala/fasthttp v1.67.0 to the hello-world example is correct and already consistent across all modules in the codebase (core, framework, and other plugins). No version misalignment issues.

transports/go.mod (1)

5-25: mcp-go v0.41.1 dependency is consistent across all modules

The github.com/mark3labs/mcp-go v0.41.1 dependency in transports/go.mod matches all other modules in the repository. Verified that core, framework, all plugins, and test modules consistently use the same version.

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

14-14: LGTM! Consistent middleware type migration.

The import addition and signature update align with the repository-wide migration to schemas.BifrostHTTPMiddleware. No functional changes to routing logic.

Also applies to: 51-51

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

9-9: LGTM! Middleware type migration is consistent.

The import and signature changes align with the new schemas.BifrostHTTPMiddleware type used across the codebase.

Also applies to: 27-27

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

43-51: LGTM! MCP handler aligned with new middleware pattern.

The signature update and route registrations properly use the new schemas.BifrostHTTPMiddleware type, maintaining existing MCP functionality.

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

8-8: LGTM! Integration handler properly delegates new middleware type.

The import and signature changes align with the migration, and the method correctly passes the new middleware type to extension routers.

Also applies to: 37-41

plugins/logging/main.go (1)

196-199: LGTM! Logging plugin correctly implements new middleware interface.

The method properly replaces TransportInterceptor with HTTPTransportMiddleware using the correct receiver signature and returns nil (appropriate for plugins that don't need transport-layer interception).

core/schemas/plugin.go (1)

34-36: LGTM! Core middleware type and interface properly defined.

The BifrostHTTPMiddleware type follows standard middleware patterns, and the Plugin.HTTPTransportMiddleware() method signature is correctly defined.

Also applies to: 73-77

plugins/otel/main.go (1)

167-170: LGTM! OTEL plugin correctly migrated to new middleware interface.

The method signature with receiver properly implements the Plugin interface, and returning nil is appropriate for plugins that don't require transport-layer middleware.

plugins/mocker/main.go (1)

481-484: LGTM!

The HTTPTransportMiddleware() correctly returns nil since this plugin uses PreHook for its mocking logic rather than HTTP transport-level interception. This aligns with the new middleware pattern introduced in this PR.

transports/bifrost-http/handlers/middlewares.go (2)

19-19: LGTM!

Return type correctly updated to schemas.BifrostHTTPMiddleware, aligning with the new centralized middleware type definition.


87-87: LGTM!

Return type correctly updated to schemas.BifrostHTTPMiddleware.

plugins/maxim/main.go (1)

124-127: LGTM!

The HTTPTransportMiddleware() correctly returns nil since this plugin uses PreHook/PostHook for Maxim tracing integration rather than HTTP transport-level interception.

plugins/jsonparser/main.go (1)

86-89: LGTM!

The HTTPTransportMiddleware() correctly returns nil since this plugin uses PostHook for streaming JSON parsing rather than HTTP transport-level interception.

plugins/governance/main.go (3)

13-13: LGTM!

Using sonic for JSON marshaling in the hot path is a good choice for performance.


158-181: LGTM!

The parseVirtualKey helper correctly extracts virtual keys from multiple header sources (x-bf-vk, Authorization: Bearer, x-api-key) with proper prefix validation and priority ordering.


183-234: HTTPTransportMiddleware implementation follows the new middleware pattern correctly.

The middleware properly:

  • Extracts and validates virtual keys
  • Adds MCP include tools headers
  • Performs provider load balancing
  • Gracefully continues on errors (avoiding request failures for governance issues)

The implementation aligns with the PR's architecture shift from TransportInterceptor to HTTPTransportMiddleware.

framework/plugins/dynamicplugin.go (1)

24-39: Struct and method look good, pending the loader fix.

The field and accessor method are correctly structured. Once the symbol lookup is fixed to call the factory function, this will work as intended.

transports/bifrost-http/server/server.go (5)

958-965: LGTM!

Signature correctly updated to use the new schemas.BifrostHTTPMiddleware type. The route registration logic is unchanged.


968-968: LGTM!

Consistent type migration for the API routes registration.


1054-1057: LGTM!

Type migration applied consistently.


1086-1097: LGTM!

Return type and slice initialization correctly updated to use the new middleware type.


1230-1231: LGTM!

The TransportInterceptorMiddleware is correctly prepended to the inference middleware chain, ensuring plugin middlewares wrap the entire request lifecycle.

@akshaydeo akshaydeo force-pushed the 11-19-plugins_v2_architecture branch from fa4b8d4 to ddfbaf4 Compare December 22, 2025 09:43
Copy link
Copy Markdown
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 (3)
transports/bifrost-http/handlers/middlewares.go (1)

47-68: Comments still reference old "interceptor" terminology.

As noted in a previous review, the function name and inline comments still use "TransportInterceptor" terminology even though the code now uses plugin.HTTPTransportMiddleware(). Update the comment on line 47 to reference "HTTP transport middleware" and line 59's comment to say "Collect plugin HTTP transport middleware" instead of "Call TransportInterceptor on all plugins".

🔎 Suggested comment updates
-// TransportInterceptorMiddleware collects all plugin interceptors and calls them one by one
+// TransportInterceptorMiddleware collects all plugin HTTP transport middleware and chains them
 func TransportInterceptorMiddleware(config *lib.Config) schemas.BifrostHTTPMiddleware {
 	return func(next fasthttp.RequestHandler) fasthttp.RequestHandler {
 		return func(ctx *fasthttp.RequestCtx) {
 			plugins := config.GetLoadedPlugins()
 			if len(plugins) == 0 {
 				next(ctx)
 				return
 			}
 			pluginsMiddlewareChain := []schemas.BifrostHTTPMiddleware{}
 			for _, plugin := range plugins {
 				middleware := plugin.HTTPTransportMiddleware()
-				// Call TransportInterceptor on all plugins
+				// Collect plugin HTTP transport middleware
 				if middleware == nil {
 					continue
 				}
plugins/governance/main.go (1)

212-217: Error message still incorrect after previous fix.

Line 214: The error message says "failed to marshal" but the operation on line 212 is sonic.Unmarshal. This was flagged in a previous review and marked as addressed in commit fa4b8d4, but the incorrect message remains in the current code.

🔎 Proposed fix
-				p.logger.Error("failed to marshal request body to check for virtual key: %v", err)
+				p.logger.Error("failed to unmarshal request body: %v", err)
framework/plugins/dynamicplugin.go (1)

140-147: Critical: Type mismatch will cause dynamic plugin loading to fail.

The example plugin at examples/plugins/hello-world/main.go:18-25 exports HTTPTransportMiddleware as a factory function:

func HTTPTransportMiddleware() schemas.BifrostHTTPMiddleware { ... }

However, line 145 attempts to cast the symbol directly to schemas.BifrostHTTPMiddleware:

if dp.httpTransportMiddleware, ok = httpTransportMiddlewareSym.(schemas.BifrostHTTPMiddleware); !ok {

This type mismatch will cause the assertion to always fail, preventing any dynamic plugins from loading successfully. The symbol is a function that returns the middleware, not the middleware itself.

Compare with the GetName pattern on line 137, which correctly handles factory functions.

🔎 Proposed fix
 	// Looking up for HTTPTransportMiddleware method
 	httpTransportMiddlewareSym, err := plugin.Lookup("HTTPTransportMiddleware")
 	if err != nil {
 		return nil, err
 	}
-	if dp.httpTransportMiddleware, ok = httpTransportMiddlewareSym.(schemas.BifrostHTTPMiddleware); !ok {
-		return nil, fmt.Errorf("failed to cast HTTPTransportMiddleware to func(next fasthttp.RequestHandler) fasthttp.RequestHandler")
+	httpTransportMiddlewareFunc, ok := httpTransportMiddlewareSym.(func() schemas.BifrostHTTPMiddleware)
+	if !ok {
+		return nil, fmt.Errorf("failed to cast HTTPTransportMiddleware to func() schemas.BifrostHTTPMiddleware")
 	}
+	dp.httpTransportMiddleware = httpTransportMiddlewareFunc()
🧹 Nitpick comments (1)
core/schemas/plugin.go (1)

45-46: Update outdated comment terminology.

Line 46 still references "TransportInterceptor" in the execution order documentation, but the interface now uses HTTPTransportMiddleware. This should be updated for consistency.

🔎 Proposed fix
 // Execution order:
-// 1. TransportInterceptor (HTTP transport only, modifies raw headers/body before entering Bifrost core)
+// 1. HTTPTransportMiddleware (HTTP transport only, wraps request handling before entering Bifrost core)
 // 2. PreHook (executed in registration order)
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fa4b8d4 and ddfbaf4.

⛔ Files ignored due to path filters (1)
  • examples/plugins/hello-world/go.sum is excluded by !**/*.sum
📒 Files selected for processing (37)
  • core/mcp.go
  • core/schemas/plugin.go
  • examples/plugins/hello-world/go.mod
  • examples/plugins/hello-world/main.go
  • framework/configstore/tables/mcp.go
  • framework/plugins/dynamicplugin.go
  • framework/plugins/dynamicplugin_test.go
  • plugins/governance/go.mod
  • plugins/governance/main.go
  • plugins/jsonparser/main.go
  • plugins/logging/main.go
  • plugins/maxim/main.go
  • plugins/mocker/main.go
  • plugins/otel/main.go
  • plugins/semanticcache/main.go
  • plugins/telemetry/main.go
  • transports/bifrost-http/handlers/cache.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/handlers/health.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/handlers/logging.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/handlers/mcpServer.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • transports/bifrost-http/handlers/plugins.go
  • transports/bifrost-http/handlers/providers.go
  • transports/bifrost-http/handlers/session.go
  • transports/bifrost-http/handlers/ui.go
  • transports/bifrost-http/handlers/websocket.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/lib/middleware.go
  • transports/bifrost-http/server/server.go
  • transports/go.mod
  • ui/components/sidebar.tsx
💤 Files with no reviewable changes (1)
  • framework/configstore/tables/mcp.go
✅ Files skipped from review due to trivial changes (1)
  • ui/components/sidebar.tsx
🚧 Files skipped from review as they are similar to previous changes (17)
  • transports/bifrost-http/handlers/session.go
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/handlers/cache.go
  • transports/go.mod
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/handlers/providers.go
  • plugins/governance/go.mod
  • core/mcp.go
  • plugins/maxim/main.go
  • examples/plugins/hello-world/go.mod
  • plugins/otel/main.go
  • plugins/semanticcache/main.go
  • examples/plugins/hello-world/main.go
  • transports/bifrost-http/handlers/websocket.go
  • transports/bifrost-http/handlers/plugins.go
  • transports/bifrost-http/handlers/config.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:

  • framework/plugins/dynamicplugin_test.go
  • transports/bifrost-http/handlers/logging.go
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/handlers/mcpServer.go
  • core/schemas/plugin.go
  • plugins/mocker/main.go
  • plugins/telemetry/main.go
  • transports/bifrost-http/lib/middleware.go
  • transports/bifrost-http/handlers/ui.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • transports/bifrost-http/server/server.go
  • plugins/governance/main.go
  • transports/bifrost-http/handlers/health.go
  • framework/plugins/dynamicplugin.go
  • transports/bifrost-http/handlers/mcp.go
  • plugins/logging/main.go
  • transports/bifrost-http/handlers/middlewares.go
  • plugins/jsonparser/main.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:

  • framework/plugins/dynamicplugin_test.go
  • transports/bifrost-http/handlers/logging.go
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/handlers/mcpServer.go
  • core/schemas/plugin.go
  • plugins/mocker/main.go
  • plugins/telemetry/main.go
  • transports/bifrost-http/lib/middleware.go
  • transports/bifrost-http/handlers/ui.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • transports/bifrost-http/server/server.go
  • plugins/governance/main.go
  • transports/bifrost-http/handlers/health.go
  • framework/plugins/dynamicplugin.go
  • transports/bifrost-http/handlers/mcp.go
  • plugins/logging/main.go
  • transports/bifrost-http/handlers/middlewares.go
  • plugins/jsonparser/main.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/logging.go
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/handlers/mcpServer.go
  • transports/bifrost-http/lib/middleware.go
  • transports/bifrost-http/handlers/ui.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • transports/bifrost-http/server/server.go
  • transports/bifrost-http/handlers/health.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/handlers/middlewares.go
🧬 Code graph analysis (18)
framework/plugins/dynamicplugin_test.go (1)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-26)
transports/bifrost-http/handlers/logging.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
transports/bifrost-http/handlers/integrations.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
transports/bifrost-http/handlers/mcpServer.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
core/schemas/plugin.go (1)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-26)
plugins/mocker/main.go (2)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-26)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
plugins/telemetry/main.go (2)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-26)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
transports/bifrost-http/lib/middleware.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
transports/bifrost-http/handlers/ui.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
transports/bifrost-http/handlers/middlewares_test.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
transports/bifrost-http/server/server.go (3)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
transports/bifrost-http/handlers/middlewares.go (1)
  • TransportInterceptorMiddleware (48-68)
transports/bifrost-http/lib/config.go (1)
  • Config (184-215)
plugins/governance/main.go (3)
core/utils.go (1)
  • Ptr (56-58)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-26)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
transports/bifrost-http/handlers/health.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
framework/plugins/dynamicplugin.go (2)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
examples/plugins/hello-world/main.go (2)
  • GetName (15-17)
  • HTTPTransportMiddleware (19-26)
transports/bifrost-http/handlers/mcp.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
plugins/logging/main.go (4)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-26)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
ui/lib/types/logs.ts (1)
  • TranscriptionInput (15-17)
transports/bifrost-http/handlers/inference.go (1)
  • TranscriptionRequest (285-289)
transports/bifrost-http/handlers/middlewares.go (2)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
transports/bifrost-http/lib/middleware.go (1)
  • ChainMiddlewares (11-23)
plugins/jsonparser/main.go (2)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-26)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
🔇 Additional comments (19)
transports/bifrost-http/handlers/middlewares_test.go (1)

308-308: LGTM! Consistent middleware type migration.

All test middleware instantiations have been correctly updated from lib.BifrostHTTPMiddleware to schemas.BifrostHTTPMiddleware. The test logic remains unchanged and continues to validate middleware chaining, execution order, context modification, and short-circuiting behavior.

Also applies to: 335-335, 342-342, 349-349, 381-381, 408-408, 418-418, 426-426, 471-471, 480-480, 490-490

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

42-42: LGTM! RegisterRoutes signature updated consistently.

The method signature has been updated to accept schemas.BifrostHTTPMiddleware instead of lib.BifrostHTTPMiddleware, aligning with the broader middleware type migration. The route registration logic remains unchanged.

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

11-11: LGTM! Middleware type migration completed.

The import and RegisterRoutes signature have been updated to use schemas.BifrostHTTPMiddleware, consistent with the codebase-wide migration. The UI serving logic remains unchanged.

Also applies to: 29-29

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

43-43: LGTM! Signature updated for consistency.

The RegisterRoutes method now accepts schemas.BifrostHTTPMiddleware, aligning with the middleware type migration. All MCP route registrations continue to apply middlewares via ChainMiddlewares.

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

72-72: LGTM! MCP server routes updated consistently.

The RegisterRoutes signature has been updated to accept schemas.BifrostHTTPMiddleware, maintaining consistency with the broader middleware type migration. The MCP server route handling remains unchanged.

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

8-8: LGTM! Integration handler updated consistently.

The import and RegisterRoutes signature have been updated to use schemas.BifrostHTTPMiddleware. The method continues to delegate route registration to each integration extension with the provided middlewares.

Also applies to: 37-37

plugins/telemetry/main.go (1)

279-282: LGTM! Correct no-op middleware implementation.

The HTTPTransportMiddleware method correctly returns nil since this plugin doesn't require HTTP transport middleware. The telemetry plugin collects metrics through PreHook and PostHook methods instead, which is the appropriate pattern for request/response lifecycle tracking.

framework/plugins/dynamicplugin_test.go (1)

16-16: LGTM! Test properly validates new middleware API.

The test has been correctly updated to validate HTTPTransportMiddleware instead of the deprecated TransportInterceptor. The test:

  • Creates a mock next handler to track invocation
  • Obtains the middleware function from the plugin
  • Wraps the next handler with the middleware
  • Sets up a realistic request context with URI, method, and headers
  • Verifies the next handler is called through the middleware chain

This properly tests the new middleware-based invocation pattern.

Also applies to: 54-84

transports/bifrost-http/lib/middleware.go (1)

3-22: LGTM! Clean migration to schemas-based middleware type.

The transition from a local BifrostHTTPMiddleware type to schemas.BifrostHTTPMiddleware is straightforward and maintains the existing chaining logic correctly.

plugins/logging/main.go (1)

196-199: LGTM! Correct no-op middleware implementation.

The logging plugin appropriately returns nil for HTTP transport middleware since it operates via PreHook/PostHook rather than at the transport layer.

core/schemas/plugin.go (1)

73-77: LGTM! Clean middleware-based API design.

The new HTTPTransportMiddleware() method returning a BifrostHTTPMiddleware constructor follows standard middleware patterns and provides better composability than the previous interception approach.

plugins/jsonparser/main.go (1)

86-89: LGTM! Appropriate no-op middleware for this plugin.

The JSON parser plugin correctly returns nil since it operates on responses in PostHook rather than at the HTTP transport layer.

plugins/mocker/main.go (1)

481-484: LGTM! Correct no-op implementation.

The mocker plugin appropriately returns nil since it operates via PreHook to short-circuit requests, not at the HTTP transport layer.

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

27-29: LGTM! Clean signature migration.

The health handler correctly adopts the new schemas.BifrostHTTPMiddleware type without any functional changes.

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

56-65: LGTM! Correct middleware collection and chaining.

The logic properly collects non-nil middleware from each plugin and chains them using lib.ChainMiddlewares, maintaining plugin ordering and allowing short-circuiting.

transports/bifrost-http/server/server.go (2)

958-958: LGTM! Consistent signature updates across server API.

All route registration and middleware preparation methods have been correctly updated to use schemas.BifrostHTTPMiddleware, maintaining consistency throughout the server initialization flow.

Also applies to: 968-968, 1054-1054, 1086-1087


1230-1230: LGTM! Correct middleware prepending.

The inference middleware chain correctly prepends TransportInterceptorMiddleware using the new schemas.BifrostHTTPMiddleware type.

plugins/governance/main.go (2)

409-409: LGTM!

The error message accurately reflects the validation failure condition.


183-234: HTTPTransportMiddleware operates at HTTP transport layer for request transformation, not governance enforcement.

The middleware correctly fails open when virtual keys are missing or errors occur in transformations (MCP tools, JSON parsing, load balancing). Governance enforcement is handled at a separate layer via PreHook, which respects the IsVkMandatory configuration setting:

  • When IsVkMandatory=false (default): missing virtual keys bypass HTTPTransportMiddleware transformations intentionally
  • When IsVkMandatory=true: PreHook rejects missing/invalid virtual keys with a 400 error before request processing

Errors in HTTPTransportMiddleware operations (JSON unmarshaling, load balancing) should not block requests since this layer focuses on request transformation for availability. However, to improve observability, add metrics/alerts for these error paths to ensure visibility when governance transformations fail silently.

Comment thread plugins/governance/main.go
@akshaydeo akshaydeo force-pushed the 11-19-plugins_v2_architecture branch from ddfbaf4 to d632297 Compare December 22, 2025 10:26
Copy link
Copy Markdown
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 (2)
plugins/governance/main.go (1)

158-181: Missing prefix validation for x-bf-vk header (previously flagged).

The x-bf-vk header on line 162 is returned without validating that it starts with VirtualKeyPrefix (sk-bf-), while the Authorization Bearer (line 168) and x-api-key (line 178) headers correctly validate the prefix. This inconsistency could allow invalid virtual keys to pass when provided via the x-bf-vk header.

🔎 Proposed fix
 	vkHeader := ctx.Request.Header.Peek("x-bf-vk")
 	if string(vkHeader) != "" {
-		return bifrost.Ptr(string(vkHeader))
+		vkValue := string(vkHeader)
+		if strings.HasPrefix(strings.ToLower(vkValue), VirtualKeyPrefix) {
+			return bifrost.Ptr(vkValue)
+		}
 	}

Based on past review comments from commit history; this issue remains unaddressed.

framework/plugins/dynamicplugin.go (1)

140-147: Critical: Type mismatch in symbol lookup - this issue appears unresolved.

This is the same critical issue flagged in the previous review comment (marked as "✅ Addressed in commit ddfbaf4"), but the code still shows the incorrect pattern:

Line 145 attempts to cast httpTransportMiddlewareSym directly to schemas.BifrostHTTPMiddleware, but the example plugin at examples/plugins/hello-world/main.go exports HTTPTransportMiddleware as a factory function with signature func() schemas.BifrostHTTPMiddleware.

This cast will always fail because the symbol is a function that returns the middleware, not the middleware itself. Compare with the correct pattern used for GetName on line 137.

🔎 Required fix (same as previous review)
 	// Looking up for HTTPTransportMiddleware method
 	httpTransportMiddlewareSym, err := plugin.Lookup("HTTPTransportMiddleware")
 	if err != nil {
 		return nil, err
 	}
-	if dp.httpTransportMiddleware, ok = httpTransportMiddlewareSym.(schemas.BifrostHTTPMiddleware); !ok {
-		return nil, fmt.Errorf("failed to cast HTTPTransportMiddleware to func(next fasthttp.RequestHandler) fasthttp.RequestHandler")
+	httpTransportMiddlewareFunc, ok := httpTransportMiddlewareSym.(func() schemas.BifrostHTTPMiddleware)
+	if !ok {
+		return nil, fmt.Errorf("failed to cast HTTPTransportMiddleware to func() schemas.BifrostHTTPMiddleware")
 	}
+	dp.httpTransportMiddleware = httpTransportMiddlewareFunc()

Note: The previous review indicated this was addressed in commit ddfbaf4, but the issue persists in the current code. Please verify whether the fix was reverted or if this PR is based on an older commit.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ddfbaf4 and d632297.

⛔ Files ignored due to path filters (1)
  • examples/plugins/hello-world/go.sum is excluded by !**/*.sum
📒 Files selected for processing (41)
  • core/mcp.go
  • core/schemas/plugin.go
  • docs/docs.json
  • docs/plugins/getting-started.mdx
  • docs/plugins/migration-guide.mdx
  • docs/plugins/writing-plugin.mdx
  • examples/plugins/hello-world/go.mod
  • examples/plugins/hello-world/main.go
  • framework/configstore/tables/mcp.go
  • framework/plugins/dynamicplugin.go
  • framework/plugins/dynamicplugin_test.go
  • plugins/governance/go.mod
  • plugins/governance/main.go
  • plugins/jsonparser/main.go
  • plugins/logging/main.go
  • plugins/maxim/main.go
  • plugins/mocker/main.go
  • plugins/otel/main.go
  • plugins/semanticcache/main.go
  • plugins/telemetry/main.go
  • transports/bifrost-http/handlers/cache.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/handlers/health.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/handlers/logging.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/handlers/mcpServer.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • transports/bifrost-http/handlers/plugins.go
  • transports/bifrost-http/handlers/providers.go
  • transports/bifrost-http/handlers/session.go
  • transports/bifrost-http/handlers/ui.go
  • transports/bifrost-http/handlers/websocket.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/lib/middleware.go
  • transports/bifrost-http/server/server.go
  • transports/go.mod
  • ui/components/sidebar.tsx
💤 Files with no reviewable changes (1)
  • framework/configstore/tables/mcp.go
✅ Files skipped from review due to trivial changes (1)
  • docs/plugins/getting-started.mdx
🚧 Files skipped from review as they are similar to previous changes (14)
  • transports/bifrost-http/handlers/providers.go
  • core/mcp.go
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/handlers/logging.go
  • examples/plugins/hello-world/go.mod
  • transports/bifrost-http/handlers/session.go
  • plugins/otel/main.go
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • core/schemas/plugin.go
  • transports/go.mod
  • transports/bifrost-http/handlers/cache.go
  • plugins/semanticcache/main.go
  • plugins/maxim/main.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/docs.json
  • transports/bifrost-http/handlers/mcpServer.go
  • transports/bifrost-http/handlers/ui.go
  • transports/bifrost-http/handlers/websocket.go
  • transports/bifrost-http/handlers/plugins.go
  • framework/plugins/dynamicplugin_test.go
  • transports/bifrost-http/handlers/inference.go
  • docs/plugins/writing-plugin.mdx
  • plugins/logging/main.go
  • ui/components/sidebar.tsx
  • plugins/telemetry/main.go
  • plugins/governance/go.mod
  • docs/plugins/migration-guide.mdx
  • plugins/governance/main.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/lib/middleware.go
  • plugins/mocker/main.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/server/server.go
  • framework/plugins/dynamicplugin.go
  • transports/bifrost-http/handlers/config.go
  • plugins/jsonparser/main.go
  • transports/bifrost-http/handlers/health.go
  • examples/plugins/hello-world/main.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/handlers/mcpServer.go
  • transports/bifrost-http/handlers/ui.go
  • transports/bifrost-http/handlers/websocket.go
  • transports/bifrost-http/handlers/plugins.go
  • framework/plugins/dynamicplugin_test.go
  • transports/bifrost-http/handlers/inference.go
  • plugins/logging/main.go
  • plugins/telemetry/main.go
  • plugins/governance/main.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/lib/middleware.go
  • plugins/mocker/main.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/server/server.go
  • framework/plugins/dynamicplugin.go
  • transports/bifrost-http/handlers/config.go
  • plugins/jsonparser/main.go
  • transports/bifrost-http/handlers/health.go
  • examples/plugins/hello-world/main.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/mcpServer.go
  • transports/bifrost-http/handlers/ui.go
  • transports/bifrost-http/handlers/websocket.go
  • transports/bifrost-http/handlers/plugins.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/lib/middleware.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/server/server.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/health.go
🧬 Code graph analysis (19)
transports/bifrost-http/handlers/mcpServer.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
transports/bifrost-http/handlers/ui.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
transports/bifrost-http/handlers/websocket.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
transports/bifrost-http/handlers/plugins.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
framework/plugins/dynamicplugin_test.go (1)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-26)
transports/bifrost-http/handlers/inference.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
plugins/telemetry/main.go (2)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-26)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
plugins/governance/main.go (3)
core/utils.go (1)
  • Ptr (56-58)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-26)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
transports/bifrost-http/handlers/mcp.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
transports/bifrost-http/integrations/router.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
transports/bifrost-http/lib/middleware.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
plugins/mocker/main.go (2)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-26)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
transports/bifrost-http/handlers/middlewares.go (2)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
transports/bifrost-http/lib/middleware.go (1)
  • ChainMiddlewares (11-23)
transports/bifrost-http/server/server.go (3)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
transports/bifrost-http/handlers/middlewares.go (1)
  • TransportInterceptorMiddleware (48-68)
transports/bifrost-http/lib/config.go (1)
  • Config (184-215)
framework/plugins/dynamicplugin.go (2)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
examples/plugins/hello-world/main.go (2)
  • GetName (15-17)
  • HTTPTransportMiddleware (19-26)
transports/bifrost-http/handlers/config.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
plugins/jsonparser/main.go (2)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-26)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
transports/bifrost-http/handlers/health.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
examples/plugins/hello-world/main.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (36-36)
🔇 Additional comments (25)
ui/components/sidebar.tsx (2)

208-208: Verify whether this UI change belongs in this PR.

This styling change (reducing border radius from rounded-md to rounded-sm) appears unrelated to the PR's stated objectives, which focus on plugins v2 architecture and replacing TransportInterceptor with HTTPTransportMiddleware in the Go backend.

Consider whether this visual adjustment should be:

  • Moved to a separate UI-focused PR for clearer change tracking
  • Documented in the PR description if intentionally bundled

The change itself is technically correct.

Based on coding guidelines, which emphasize reviewing changes in light of the whole PR stack context.


243-243: Same styling adjustment as line 208.

This change maintains visual consistency with the main menu button adjustment, but the same concern about PR scope applies.

plugins/governance/go.mod (1)

8-11: Pinned dependency versions are current and secure.

The promotion of bytedance/sonic v1.14.1 and valyala/fasthttp v1.67.0 from indirect to direct dependencies aligns with the middleware refactor. v1.67.0 includes security fixes from multiple security researchers, with improvements to URL validation, IPv6 validation, scheme validation, and host validation. bytedance/sonic shows 0 unresolved vulnerabilities. These versions are consistent across all plugins in the PR stack.

docs/plugins/migration-guide.mdx (2)

1-307: Comprehensive and well-structured migration guide.

The documentation thoroughly covers the migration from TransportInterceptor to HTTPTransportMiddleware, including:

  • Clear comparison table of differences
  • Step-by-step migration instructions
  • Common migration patterns with before/after examples
  • Troubleshooting section for common issues

The code examples are consistent with the actual implementation in the codebase (e.g., examples/plugins/hello-world/main.go shows the same middleware pattern).


259-264: The documented execution order in the migration guide is accurate. No changes needed.

Likely an incorrect or invalid review comment.

docs/docs.json (1)

135-136: LGTM!

The migration guide is appropriately placed after the "writing-plugin" documentation in the navigation structure, providing a logical flow for plugin developers.

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

365-390: LGTM!

The RegisterRoutes signature update to accept schemas.BifrostHTTPMiddleware aligns with the project-wide migration to the new middleware pattern. The change is consistent with the type definition in core/schemas/plugin.go and other handler files in this PR.

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

53-59: LGTM!

The RegisterRoutes signature update is consistent with the middleware type migration across all handler files in this PR.

plugins/telemetry/main.go (1)

279-282: LGTM - No-op implementation is appropriate.

The telemetry plugin correctly implements HTTPTransportMiddleware returning nil since it doesn't need to intercept HTTP transport requests. The framework safely filters out nil middlewares before chaining them. The plugin's metrics collection is handled via PreHook/PostHook for Bifrost-level metrics and the separate HTTPMiddleware method for HTTP request metrics.

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

15-15: LGTM: Consistent type migration to schemas.BifrostHTTPMiddleware.

The import addition and signature change align with the broader architectural migration from lib.BifrostHTTPMiddleware to schemas.BifrostHTTPMiddleware. The routing logic and middleware chaining remain unchanged.

Also applies to: 55-62

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

14-14: LGTM: Consistent type migration.

The import and signature change align with the project-wide migration to schemas.BifrostHTTPMiddleware. WebSocket route registration logic remains unchanged.

Also applies to: 51-53

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

11-11: LGTM: Consistent type migration.

The import and signature change align with the architectural migration to schemas.BifrostHTTPMiddleware. UI route registration logic remains unchanged.

Also applies to: 29-32

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

72-76: LGTM: Consistent type migration.

The signature change aligns with the project-wide migration to schemas.BifrostHTTPMiddleware. MCP server route registration logic remains unchanged.

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

43-51: LGTM: Consistent type migration.

The signature change aligns with the project-wide migration to schemas.BifrostHTTPMiddleware. MCP tool route registration logic remains unchanged.

plugins/logging/main.go (1)

196-199: LGTM: Appropriate no-op middleware implementation.

The logging plugin correctly returns nil from HTTPTransportMiddleware() since it performs its functionality through PreHook/PostHook rather than HTTP transport interception. This aligns with the new plugin architecture where middleware is optional.

framework/plugins/dynamicplugin_test.go (1)

54-84: LGTM: Comprehensive test coverage for new middleware API.

The test properly exercises the new HTTPTransportMiddleware() API by:

  1. Obtaining the middleware factory from the plugin
  2. Wrapping a mock next handler
  3. Invoking the wrapped handler
  4. Verifying the next handler is called

This validates the middleware chain pattern introduced by the plugins v2 architecture.

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

72-73: LGTM: Consistent interface and implementation updates.

The ExtensionRouter interface and GenericRouter implementation both correctly update their RegisterRoutes signatures to accept schemas.BifrostHTTPMiddleware. The route registration logic and middleware chaining remain unchanged.

Also applies to: 320-368

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

27-29: LGTM: Clean type migration.

The middleware parameter type correctly migrated from lib.BifrostHTTPMiddleware to schemas.BifrostHTTPMiddleware, aligning with the PR's centralization goal.

plugins/mocker/main.go (1)

481-484: LGTM: Appropriate no-op implementation.

Returning nil is correct for the mocker plugin since it operates at the PreHook/PostHook level rather than HTTP transport layer. The middleware chain correctly handles nil values (as seen in handlers/middlewares.go line 60).

transports/bifrost-http/lib/middleware.go (1)

11-23: LGTM: Type migration maintains correct chaining logic.

The signature update to use schemas.BifrostHTTPMiddleware is clean and maintains the correct right-to-left construction order for middleware chaining.

docs/plugins/writing-plugin.mdx (1)

65-241: LGTM: Comprehensive documentation for breaking change.

The versioned tabs clearly distinguish v1.3.x from v1.4.x+ patterns, and the HTTPTransportMiddleware section provides appropriate warnings about runtime scope and invocation context. The migration guide is thorough and will help plugin developers adapt to the new middleware pattern.

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

47-68: LGTM: Correct plugin middleware collection and chaining.

The middleware now properly collects non-nil HTTPTransportMiddleware() results from all plugins and chains them via lib.ChainMiddlewares. The logic correctly handles nil returns (line 60-62) and maintains proper execution order.

plugins/governance/main.go (1)

184-234: LGTM: HTTPTransportMiddleware correctly implements governance logic.

The middleware properly:

  • Parses virtual keys from headers
  • Validates virtual key existence and active status
  • Adds MCP include-tools headers
  • Performs load balancing and provider selection
  • Marshals/unmarshals request body using Sonic

The error handling appropriately continues with next(ctx) on errors, allowing requests to proceed without governance when issues occur.

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

958-965: LGTM: Consistent middleware type migration across route registration.

All route registration methods (RegisterInferenceRoutes, RegisterAPIRoutes, RegisterUIRoutes) and PrepareCommonMiddlewares correctly migrated from lib.BifrostHTTPMiddleware to schemas.BifrostHTTPMiddleware. The middleware ordering at line 1230 appropriately places TransportInterceptorMiddleware first to ensure plugin HTTP transport middleware executes before other inference middlewares.

Also applies to: 968-1051, 1054-1057, 1086-1097

plugins/jsonparser/main.go (1)

86-89: The nil return value is safely handled—nil middleware is filtered out before invocation.

Looking at the middleware chain construction in transports/bifrost-http/handlers/middlewares.go, the code checks each plugin's middleware before using it:

middleware := plugin.HTTPTransportMiddleware()
if middleware == nil {
    continue  // Nil values are skipped, not added to the chain
}
pluginsMiddlewareChain = append(pluginsMiddlewareChain, middleware)

Only non-nil middlewares are added to pluginsMiddlewareChain, so nil function pointers never reach invocation. This is a well-established pattern used by 7 other plugins (telemetry, logging, maxim, mocker, otel, semanticcache, governance) with the same "is not used for this plugin" comment. The implementation is safe and requires no changes.

Likely an incorrect or invalid review comment.

Comment thread examples/plugins/hello-world/main.go
@akshaydeo akshaydeo force-pushed the 11-19-plugins_v2_architecture branch from d632297 to bfb02bb Compare December 23, 2025 02:14
Copy link
Copy Markdown
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: 5

♻️ Duplicate comments (4)
examples/plugins/hello-world/main.go (1)

19-26: Note: Existing issue flagged in previous review.

The middleware doesn't set the context value that PreHook/PostHook expect to read (line 29, 38-39). This has already been flagged in a previous review.

plugins/governance/main.go (2)

158-181: Missing prefix validation for x-bf-vk header.

The x-bf-vk header (lines 161-163) is returned without validating the VirtualKeyPrefix, while the Authorization (line 168) and x-api-key (line 177) headers have this validation. This inconsistency was flagged in a previous review.

🔎 Proposed fix
 	vkHeader := ctx.Request.Header.Peek("x-bf-vk")
 	if string(vkHeader) != "" {
-		return bifrost.Ptr(string(vkHeader))
+		vkValue := string(vkHeader)
+		if strings.HasPrefix(strings.ToLower(vkValue), VirtualKeyPrefix) {
+			return bifrost.Ptr(vkValue)
+		}
 	}

212-217: Fix error messages to reflect unmarshal operation.

Lines 214 and 226 both log "failed to marshal" but the operations are sonic.Unmarshal (line 212) and sonic.Marshal (line 224) respectively. Line 214's message should say "unmarshal".

🔎 Proposed fix
-			p.logger.Error("failed to marshal request body to check for virtual key: %v", err)
+			p.logger.Error("failed to unmarshal request body: %v", err)
framework/plugins/dynamicplugin.go (1)

140-147: Type mismatch between dynamic plugin loading and example plugin export still present.

The example plugin at examples/plugins/hello-world/main.go:18-25 exports HTTPTransportMiddleware as a factory function func() schemas.BifrostHTTPMiddleware, but line 145 casts directly to schemas.BifrostHTTPMiddleware. This type assertion will fail at runtime.

Either:

  1. Update this code to cast to func() schemas.BifrostHTTPMiddleware and invoke it (consistent with the GetName pattern on line 137), or
  2. Update the example plugin to export the middleware directly instead of a factory function.
🔎 Proposed fix (Option 1: align with factory pattern)
 	// Looking up for HTTPTransportMiddleware method
 	httpTransportMiddlewareSym, err := plugin.Lookup("HTTPTransportMiddleware")
 	if err != nil {
 		return nil, err
 	}
-	if dp.httpTransportMiddleware, ok = httpTransportMiddlewareSym.(schemas.BifrostHTTPMiddleware); !ok {
-		return nil, fmt.Errorf("failed to cast HTTPTransportMiddleware to func(next fasthttp.RequestHandler) fasthttp.RequestHandler")
+	httpTransportMiddlewareFunc, ok := httpTransportMiddlewareSym.(func() schemas.BifrostHTTPMiddleware)
+	if !ok {
+		return nil, fmt.Errorf("failed to cast HTTPTransportMiddleware to func() schemas.BifrostHTTPMiddleware")
 	}
+	dp.httpTransportMiddleware = httpTransportMiddlewareFunc()
🧹 Nitpick comments (6)
core/bifrost.go (2)

70-71: Tracer initialization and retry span wiring are sound

The new tracer field on Bifrost and initialization in Init (using config.Tracer or schemas.DefaultTracer()) guarantee a non‑nil tracer for downstream use, and passing it into executeRequestWithRetries centralizes span creation for all provider calls. The generic retry helper correctly:

  • Records retry count in context and attributes,
  • Wraps each attempt in its own span (llm.call for first, retry.attempt.N for retries),
  • Attaches provider/model/requestType metadata and error/status details.

One behavioral nuance: because *ctx is updated with the span context each time, later attempts become children of the previous attempt’s span (nested retries). If you’d prefer all attempts as siblings under the same parent span, you could preserve the original parent span ID and avoid chaining through the previous attempt’s span, but the current behavior is still trace‑valid.

Also applies to: 108-127, 2729-2837


2283-2311: Fallback, key-selection, and plugin hook tracing are coherent; only minor hierarchy nits

The added spans around:

  • Fallback attempts (fallback.<provider>.<model> in both unary and streaming paths),
  • Key selection (key.selection in requestWorker), and
  • Per‑plugin PreHook/PostHook executions,

are all well-scoped and tagged (provider, model, fallback index, plugin name, errors). They also correctly propagate the active span ID via BifrostContextKeySpanID so downstream operations become children of the right span.

Two minor, optional refinements you could consider:

  • For key selection, you currently end the key.selection span before updating req.Context to keySpanCtx, so subsequent LLM spans become children of a finished span. That’s valid for tracing, but if you want a cleaner hierarchy you could:

    • Set req.Context = keySpanCtx before calling selectKeyFromProviderForModel, and
    • Keep the parent span as the outer request or root span instead of making the LLM call a child of the key-selection span.
  • In plugin hooks, you set a new span ID in the plugin context but only sync back to the parent context after all hooks via GetParentCtxWithUserValues(). If you ever need per-plugin child spans to be visible immediately to nested operations, you might want to push span IDs into the parent context earlier, though the current approach is consistent and safe.

No functional bugs here; these are purely trace-structure considerations.

Also applies to: 2381-2408, 2896-2924, 3157-3212, 3221-3247, 3267-3271

core/schemas/plugin.go (1)

4-8: Update Plugin execution-order docs to match HTTPTransportMiddleware

The interface and BifrostHTTPMiddleware type correctly move transport interception to a middleware model, and ObservabilityPlugin is well-documented. The comment block above Plugin still describes step 1 as TransportInterceptor though the method is now HTTPTransportMiddleware() returning BifrostHTTPMiddleware. To avoid confusion for plugin authors, consider updating that execution-order description to reference HTTPTransportMiddleware and the HTTP middleware chain rather than the old TransportInterceptor terminology. No runtime issues here.

Also applies to: 36-39, 47-79, 109-137

framework/tracing/propagation.go (1)

10-22: W3C trace-context helpers look correct; consider clarifying ExtractParentID naming

The W3C parsing/validation helpers (ParseTraceparent, FormatTraceparent, InjectTraceContext) are defensively implemented and match the standard version-traceid-parentid-traceflags format.

One minor naming nit: ExtractParentID currently returns ctx.TraceID (the upstream trace ID) rather than ctx.ParentID (the parent span ID). The comment says “parent trace ID”, and you pass this into CreateTrace(parentID) in TracingMiddleware, so the behavior is consistent, but the function name and struct field names can be confusing. Consider either:

  • Renaming it to something like ExtractUpstreamTraceID, or
  • Returning both trace ID and parent span ID via ExtractTraceContext where you already expose both.

Functionally this is fine; this is just about avoiding future misunderstandings.

Also applies to: 24-37, 38-95, 97-117, 119-127

plugins/otel/converter.go (2)

165-170: Potential overflow when converting large uint64 values to int64.

Lines 169-170 convert uint64 to int64 which will overflow for values greater than math.MaxInt64 (~9.2 quintillion). While this is unlikely for typical use cases, it could silently produce incorrect negative values for very large unsigned integers stored in span attributes.

🔎 Suggested handling for large uint64
 	case uint64:
+		if v > uint64(^uint(0)>>1) { // v > MaxInt64
+			return kvStr(key, fmt.Sprintf("%d", v))
+		}
 		return kvInt(key, int64(v))

68-69: Silently ignored error from hex.DecodeString.

While the input is sanitized and padded, explicitly handling the error would be more defensive. The error is discarded with bytes, _ := hex.DecodeString(cleaned).

🔎 Optional: log or return empty on error
-	bytes, _ := hex.DecodeString(cleaned)
-	return bytes
+	bytes, err := hex.DecodeString(cleaned)
+	if err != nil {
+		return make([]byte, length)
+	}
+	return bytes
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d632297 and bfb02bb.

⛔ Files ignored due to path filters (2)
  • core/go.sum is excluded by !**/*.sum
  • examples/plugins/hello-world/go.sum is excluded by !**/*.sum
📒 Files selected for processing (55)
  • core/bifrost.go
  • core/bifrost_test.go
  • core/go.mod
  • core/mcp.go
  • core/schemas/bifrost.go
  • core/schemas/plugin.go
  • core/schemas/trace.go
  • core/schemas/tracer.go
  • docs/docs.json
  • docs/plugins/getting-started.mdx
  • docs/plugins/migration-guide.mdx
  • docs/plugins/writing-plugin.mdx
  • examples/plugins/hello-world/go.mod
  • examples/plugins/hello-world/main.go
  • framework/configstore/tables/mcp.go
  • framework/plugins/dynamicplugin.go
  • framework/plugins/dynamicplugin_test.go
  • framework/tracing/helpers.go
  • framework/tracing/llmspan.go
  • framework/tracing/propagation.go
  • framework/tracing/store.go
  • framework/tracing/tracer.go
  • plugins/governance/go.mod
  • plugins/governance/main.go
  • plugins/jsonparser/main.go
  • plugins/logging/main.go
  • plugins/maxim/main.go
  • plugins/mocker/main.go
  • plugins/otel/converter.go
  • plugins/otel/main.go
  • plugins/otel/ttlsyncmap.go
  • plugins/semanticcache/main.go
  • plugins/telemetry/main.go
  • transports/bifrost-http/handlers/cache.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/handlers/health.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/handlers/logging.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/handlers/mcpServer.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • transports/bifrost-http/handlers/plugins.go
  • transports/bifrost-http/handlers/providers.go
  • transports/bifrost-http/handlers/session.go
  • transports/bifrost-http/handlers/ui.go
  • transports/bifrost-http/handlers/websocket.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/lib/middleware.go
  • transports/bifrost-http/server/server.go
  • transports/go.mod
  • ui/components/sidebar.tsx
  • ui/components/ui/switch.tsx
💤 Files with no reviewable changes (2)
  • framework/configstore/tables/mcp.go
  • plugins/otel/ttlsyncmap.go
🚧 Files skipped from review as they are similar to previous changes (16)
  • core/mcp.go
  • transports/bifrost-http/handlers/websocket.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/handlers/plugins.go
  • transports/bifrost-http/handlers/ui.go
  • docs/plugins/migration-guide.mdx
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/handlers/integrations.go
  • docs/plugins/writing-plugin.mdx
  • transports/go.mod
  • plugins/governance/go.mod
  • transports/bifrost-http/handlers/health.go
  • ui/components/sidebar.tsx
  • transports/bifrost-http/handlers/mcp.go
  • plugins/mocker/main.go
  • plugins/logging/main.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:

  • plugins/semanticcache/main.go
  • plugins/telemetry/main.go
  • core/go.mod
  • transports/bifrost-http/handlers/logging.go
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/handlers/mcpServer.go
  • transports/bifrost-http/handlers/session.go
  • core/schemas/bifrost.go
  • plugins/jsonparser/main.go
  • framework/tracing/propagation.go
  • plugins/maxim/main.go
  • framework/plugins/dynamicplugin.go
  • transports/bifrost-http/handlers/providers.go
  • transports/bifrost-http/server/server.go
  • transports/bifrost-http/handlers/middlewares.go
  • framework/plugins/dynamicplugin_test.go
  • transports/bifrost-http/lib/middleware.go
  • framework/tracing/store.go
  • transports/bifrost-http/handlers/cache.go
  • framework/tracing/helpers.go
  • examples/plugins/hello-world/go.mod
  • framework/tracing/tracer.go
  • core/schemas/plugin.go
  • core/schemas/tracer.go
  • plugins/otel/main.go
  • transports/bifrost-http/handlers/config.go
  • ui/components/ui/switch.tsx
  • examples/plugins/hello-world/main.go
  • core/bifrost_test.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • core/schemas/trace.go
  • plugins/otel/converter.go
  • docs/docs.json
  • framework/tracing/llmspan.go
  • plugins/governance/main.go
  • core/bifrost.go
  • docs/plugins/getting-started.mdx
🧠 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:

  • plugins/semanticcache/main.go
  • plugins/telemetry/main.go
  • transports/bifrost-http/handlers/logging.go
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/handlers/mcpServer.go
  • transports/bifrost-http/handlers/session.go
  • core/schemas/bifrost.go
  • plugins/jsonparser/main.go
  • framework/tracing/propagation.go
  • plugins/maxim/main.go
  • framework/plugins/dynamicplugin.go
  • transports/bifrost-http/handlers/providers.go
  • transports/bifrost-http/server/server.go
  • transports/bifrost-http/handlers/middlewares.go
  • framework/plugins/dynamicplugin_test.go
  • transports/bifrost-http/lib/middleware.go
  • framework/tracing/store.go
  • transports/bifrost-http/handlers/cache.go
  • framework/tracing/helpers.go
  • framework/tracing/tracer.go
  • core/schemas/plugin.go
  • core/schemas/tracer.go
  • plugins/otel/main.go
  • transports/bifrost-http/handlers/config.go
  • examples/plugins/hello-world/main.go
  • core/bifrost_test.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • core/schemas/trace.go
  • plugins/otel/converter.go
  • framework/tracing/llmspan.go
  • plugins/governance/main.go
  • core/bifrost.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/logging.go
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/handlers/mcpServer.go
  • transports/bifrost-http/handlers/session.go
  • transports/bifrost-http/handlers/providers.go
  • transports/bifrost-http/server/server.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/lib/middleware.go
  • transports/bifrost-http/handlers/cache.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/middlewares_test.go
📚 Learning: 2025-12-22T10:50:40.990Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1154
File: plugins/governance/store.go:1165-1186
Timestamp: 2025-12-22T10:50:40.990Z
Learning: In the Bifrost governance plugin, budgets and rate limits have 1:1 relationships with their parent entities (virtual keys, teams, customers). Do not assume sharing; ensure cascade deletion logic only deletes budgets/rate limits when there are no shared references. Enforce invariants in code and add tests to verify no cross-entity sharing and that cascade deletes only remove the specific child of the parent. If a counterexample arises, adjust data model or add guards.

Applied to files:

  • plugins/governance/main.go
🧬 Code graph analysis (27)
plugins/semanticcache/main.go (2)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-26)
plugins/telemetry/main.go (2)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-26)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/logging.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/governance.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/mcpServer.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/session.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
core/schemas/bifrost.go (1)
core/schemas/tracer.go (1)
  • Tracer (13-30)
plugins/jsonparser/main.go (2)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-26)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
plugins/maxim/main.go (2)
core/schemas/plugin.go (2)
  • Plugin (71-97)
  • BifrostHTTPMiddleware (38-38)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-26)
framework/plugins/dynamicplugin.go (2)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
examples/plugins/hello-world/main.go (2)
  • GetName (15-17)
  • HTTPTransportMiddleware (19-26)
transports/bifrost-http/handlers/providers.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/server/server.go (2)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/middlewares.go (1)
  • TransportInterceptorMiddleware (49-69)
framework/plugins/dynamicplugin_test.go (1)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-26)
transports/bifrost-http/lib/middleware.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
framework/tracing/store.go (2)
core/schemas/trace.go (6)
  • Trace (10-19)
  • Span (52-65)
  • SpanEvent (107-111)
  • SpanKind (115-115)
  • SpanStatusUnset (147-147)
  • SpanStatus (143-143)
framework/tracing/helpers.go (3)
  • GetTrace (23-29)
  • AddSpan (32-38)
  • EndSpan (50-56)
transports/bifrost-http/handlers/cache.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
framework/tracing/tracer.go (5)
framework/tracing/store.go (1)
  • TraceStore (13-23)
core/schemas/trace.go (4)
  • SpanKind (115-115)
  • Span (52-65)
  • SpanStatus (143-143)
  • SpanEvent (107-111)
core/schemas/tracer.go (2)
  • SpanHandle (8-8)
  • Tracer (13-30)
framework/tracing/helpers.go (3)
  • GetTraceID (11-20)
  • EndSpan (50-56)
  • GetTrace (23-29)
core/schemas/bifrost.go (1)
  • BifrostContextKeySpanID (144-144)
core/schemas/plugin.go (1)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-26)
core/schemas/tracer.go (2)
core/schemas/trace.go (2)
  • SpanKind (115-115)
  • SpanStatus (143-143)
framework/tracing/helpers.go (1)
  • EndSpan (50-56)
plugins/otel/main.go (3)
core/schemas/plugin.go (2)
  • BifrostHTTPMiddleware (38-38)
  • ObservabilityPlugin (123-137)
core/schemas/context.go (1)
  • BifrostContext (31-41)
core/schemas/trace.go (1)
  • Trace (10-19)
transports/bifrost-http/handlers/config.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
examples/plugins/hello-world/main.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
core/bifrost_test.go (1)
core/schemas/tracer.go (1)
  • DefaultTracer (51-53)
transports/bifrost-http/handlers/middlewares_test.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
core/schemas/trace.go (2)
framework/tracing/helpers.go (1)
  • AddSpan (32-38)
ui/lib/constants/logs.ts (1)
  • Status (163-163)
plugins/otel/converter.go (2)
plugins/otel/main.go (1)
  • OtelPlugin (58-75)
core/schemas/trace.go (16)
  • Trace (10-19)
  • Span (52-65)
  • SpanKind (115-115)
  • SpanKindLLMCall (121-121)
  • SpanKindPlugin (123-123)
  • SpanKindInternal (139-139)
  • SpanKindRetry (127-127)
  • SpanKindFallback (129-129)
  • SpanKindMCPTool (125-125)
  • SpanKindEmbedding (133-133)
  • SpanKindSpeech (135-135)
  • SpanKindTranscription (137-137)
  • SpanStatus (143-143)
  • SpanStatusOk (149-149)
  • SpanStatusError (151-151)
  • SpanEvent (107-111)
core/bifrost.go (1)
framework/tracing/helpers.go (1)
  • EndSpan (50-56)
🔇 Additional comments (39)
ui/components/ui/switch.tsx (1)

16-16: Confirm the switch design change from pill-shaped to slightly rounded corners.

Standard implementations like Radix UI use rounded-full for both the Root and Thumb of switch components, creating the characteristic pill-shaped appearance. Changing to rounded-sm (2px border-radius) introduces a visual breaking change that deviates from established UI patterns. Ensure this design direction aligns with the broader UI system redesign and document this change in migration notes so users understand the visual impact.

core/go.mod (1)

27-27: LGTM! Routine dependency updates.

The addition of semver/v3 and update to pprof are indirect dependencies, likely pulled in transitively by the new plugin architecture changes. These updates appear routine and appropriate for the PR's scope.

Also applies to: 50-50

docs/plugins/getting-started.mdx (1)

51-103: LGTM! Excellent version-specific documentation.

The use of tabs to document both v1.4.x+ (with HTTPTransportMiddleware) and v1.3.x (with TransportInterceptor) provides a clear migration path for users. The execution order is properly documented for each version, making the breaking change easy to understand.

docs/docs.json (1)

135-136: LGTM! Essential migration guide added.

Adding the migration guide to the documentation navigation is appropriate for a breaking change. The placement after "writing-plugin" makes logical sense for users exploring plugin development.

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

15-15: LGTM! Consistent middleware type migration.

The change from lib.BifrostHTTPMiddleware to schemas.BifrostHTTPMiddleware aligns with the PR's goal of consolidating middleware types in the core schemas package. This is part of the broader migration to the new plugin architecture.

Also applies to: 55-55

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

80-80: LGTM! Consistent middleware type migration.

The middleware type change matches the pattern applied across all handler files, maintaining consistency with the new plugin architecture.

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

42-42: LGTM! Consistent middleware type migration.

The change maintains consistency with the middleware type migration applied across all handler files in this PR.

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

308-308: LGTM! Comprehensive test coverage maintained.

All test cases have been consistently updated to use schemas.BifrostHTTPMiddleware instead of lib.BifrostHTTPMiddleware. The test logic remains unchanged, ensuring that middleware chaining, execution order, context modification, and short-circuit behavior continue to be properly verified.

Also applies to: 335-335, 342-342, 349-349, 381-381, 408-408, 418-418, 426-426, 471-471, 480-480, 490-490

core/bifrost_test.go (1)

71-71: LGTM! Tests updated for tracer parameter.

All test cases have been consistently updated to pass schemas.DefaultTracer() to executeRequestWithRetries, aligning with the new signature that supports tracing. The use of DefaultTracer() (a no-op tracer) is appropriate for unit tests, maintaining test isolation while exercising the new tracing parameter.

Also applies to: 105-105, 139-139, 202-202, 275-275, 504-504

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

26-29: LGTM! Middleware type migration is correct.

The RegisterRoutes signature has been properly updated to use schemas.BifrostHTTPMiddleware instead of lib.BifrostHTTPMiddleware, aligning with the new centralized middleware type definition in core/schemas.

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

72-76: LGTM! Middleware type migration is consistent.

The RegisterRoutes signature correctly adopts schemas.BifrostHTTPMiddleware, maintaining consistency with the framework-wide middleware migration.

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

32-36: LGTM! Middleware type update is correct.

RegisterRoutes properly migrated to use schemas.BifrostHTTPMiddleware with no functional changes.

core/schemas/bifrost.go (2)

25-25: LGTM! Tracer integration is well-designed.

The optional Tracer field with documented nil-as-NoOp behavior allows distributed tracing to be integrated without breaking existing code. This follows good design patterns for optional observability features.


143-144: LGTM! Trace context keys follow established conventions.

The new BifrostContextKeyTraceID and BifrostContextKeySpanID constants are properly typed and follow the existing BifrostContextKey naming convention, enabling trace propagation through the Bifrost context.

framework/plugins/dynamicplugin_test.go (1)

54-84: LGTM! Test correctly validates new middleware pattern.

The updated test properly exercises the HTTPTransportMiddleware() method by:

  • Creating a mock next handler with a flag to track invocation
  • Obtaining and wrapping the middleware function
  • Verifying the wrapped handler correctly calls the next handler

This test implementation correctly validates the new middleware contract.

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

154-175: LGTM! Governance routes properly migrated to new middleware type.

RegisterRoutes correctly updated to use schemas.BifrostHTTPMiddleware, maintaining consistency with the repository-wide middleware migration.

examples/plugins/hello-world/go.mod (1)

6-23: fasthttp v1.67.0 is secure for use.

The version includes security improvements for URL validation and IPv6 address validation, and no known vulnerabilities were identified for this release. The addition is appropriate for the new HTTP middleware pattern.

plugins/telemetry/main.go (1)

279-282: No action needed. The telemetry plugin correctly returns nil from HTTPTransportMiddleware() since this plugin does not require HTTP transport-level interception. The middleware chain collection logic in transports/bifrost-http/handlers/middlewares.go explicitly filters out nil middleware functions before passing them to ChainMiddlewares, preventing any nil pointer dereferences. This is a safe and intentional pattern.

Likely an incorrect or invalid review comment.

plugins/semanticcache/main.go (1)

338-341: LGTM!

The no-op implementation is correct for this plugin, which doesn't require HTTP transport middleware.

transports/bifrost-http/lib/middleware.go (1)

3-6: LGTM!

The migration to the centralized schemas.BifrostHTTPMiddleware type is correct and maintains backward compatibility.

Also applies to: 11-11

plugins/maxim/main.go (1)

124-127: LGTM!

The no-op implementation is appropriate for this plugin, which doesn't require HTTP transport middleware.

core/schemas/tracer.go (1)

1-57: LGTM!

The Tracer interface is well-designed with a minimal surface area. The NoOpTracer provides a safe default, and the compile-time interface check ensures correctness.

plugins/jsonparser/main.go (1)

86-89: LGTM!

The no-op implementation is correct for this plugin, which processes responses in PostHook rather than at the transport layer.

framework/tracing/store.go (1)

1-302: LGTM!

The TraceStore implementation is well-designed with proper:

  • Object pooling to reduce allocations
  • Thread-safe concurrent access via sync.Map
  • TTL-based cleanup with graceful shutdown
  • Pre-allocated capacities for slices to minimize reallocations
transports/bifrost-http/server/server.go (1)

958-965: Middleware type migration to schemas.BifrostHTTPMiddleware looks consistent

All server route registration and middleware preparation now consistently use schemas.BifrostHTTPMiddleware, and inference middlewares correctly prepend TransportInterceptorMiddleware(s.Config) while preserving existing behavior. No issues spotted here.

Also applies to: 968-1057, 1086-1097, 1220-1234

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

20-46: HTTP middleware type changes and new tracing middleware integrate cleanly

  • CorsMiddleware, AuthMiddleware, and TransportInterceptorMiddleware now correctly expose schemas.BifrostHTTPMiddleware, matching the new Plugin contract. TransportInterceptorMiddleware safely skips plugins that return nil middleware and chains the rest via lib.ChainMiddlewares without altering ordering semantics.
  • TracingMiddleware cleanly wires request‑scoped traces:
    • Derives a parent ID from W3C trace headers and calls store.CreateTrace(parentID).
    • Stores the resulting trace ID and root span ID in RequestCtx user values for downstream components.
    • Ends the root span with an OK or error status based on the HTTP response code.
    • Asynchronously completes the trace, forwards it to all non‑nil observability plugins via Inject, and then releases it back to the store.

This is a solid foundation for HTTP‑level tracing; any further enhancements (e.g., sampling or additional HTTP attributes) can be layered on later without changing the API.

Also applies to: 48-69, 87-188, 190-264, 266-281

framework/tracing/tracer.go (1)

1-21: StoreTracer correctly bridges schemas.Tracer to TraceStore

StoreTracer cleanly implements schemas.Tracer: it:

  • Reads the current trace ID and optional parent span ID from context,
  • Starts either root or child spans and propagates the new span ID back into context,
  • Safely no‑ops when there’s no trace ID or when the handle is nil/invalid,
  • Updates attributes and events via TraceStore and Span methods.

As long as callers only construct StoreTracer with a non‑nil *TraceStore, this provides a solid bridge between the core tracer interface and the in‑memory trace store.

Also applies to: 23-29, 30-57, 59-67, 69-82, 84-102, 104-106

framework/tracing/helpers.go (1)

1-21: Tracing helper functions are straightforward and defensive

The helpers (GetTraceID, GetTrace, AddSpan, AddChildSpan, EndSpan, SetSpanAttribute, AddSpanEvent) provide a clean, minimal API over TraceStore, with appropriate nil/empty checks for context, trace, and span. They’re safe to use across the codebase and complement the higher-level StoreTracer nicely.

Also applies to: 22-29, 31-38, 40-47, 49-56, 58-69, 71-82

plugins/otel/main.go (1)

55-75: No action required. The code correctly uses strings.SplitSeq, a standard library function added in Go 1.24 that returns an iterator over substrings. This pattern is valid and preferred for iterating over split results without allocating a slice.

Likely an incorrect or invalid review comment.

framework/plugins/dynamicplugin.go (1)

36-39: LGTM for the middleware accessor method.

The HTTPTransportMiddleware() method correctly returns the stored middleware. Since loadDynamicPlugin requires all symbols to be present and properly cast before returning, the field will never be nil when accessed.

plugins/otel/converter.go (2)

72-88: Well-structured trace-to-OTEL conversion.

The convertTraceToResourceSpan function cleanly iterates through spans and assembles a properly structured ResourceSpan with resource attributes and instrumentation scope. The implementation follows OTEL conventions.


231-269: Span kind and status mappings are comprehensive.

Both convertSpanKind and convertSpanStatus cover all defined SpanKind and SpanStatus constants from core/schemas/trace.go, with appropriate defaults for unrecognized values.

core/schemas/trace.go (3)

21-38: Thread-safe span operations look correct.

AddSpan and GetSpan properly acquire the mutex before accessing the shared Spans slice. The linear search in GetSpan is acceptable for typical trace sizes.


67-82: Thread-safe attribute and event operations look correct.

SetAttribute and AddEvent properly acquire the mutex and handle nil map initialization. These follow the expected patterns for concurrent access.


154-325: Comprehensive attribute key definitions following OpenTelemetry conventions.

The extensive set of gen_ai.* namespace attribute keys is well-organized by category and follows OpenTelemetry semantic conventions. Good documentation comments explain the purpose.

framework/tracing/llmspan.go (4)

1118-1132: Message extraction helpers are well-implemented.

extractChatMessages properly handles nil content by skipping such messages. The helper uses a pre-allocated slice with appropriate capacity.


1207-1228: Efficient content extraction with strings.Builder.

extractMessageContent correctly uses strings.Builder for efficient concatenation when processing multiple content blocks.


12-57: Clean dispatcher pattern for request attribute population.

PopulateRequestAttributes provides a well-organized entry point that dispatches to type-specific handlers based on RequestType. The nil check at the start prevents panics.


59-100: Response attribute dispatcher uses switch on pointer fields.

PopulateResponseAttributes uses switch { case resp.X != nil: } pattern which works but differs from the request dispatcher's switch req.RequestType pattern. This is acceptable since BifrostResponse may not have an explicit type field.

Comment thread core/schemas/trace.go
Comment thread core/schemas/trace.go
Comment thread core/schemas/trace.go
Comment thread framework/tracing/llmspan.go
@akshaydeo akshaydeo force-pushed the 11-19-plugins_v2_architecture branch from bfb02bb to b8f95ce Compare December 23, 2025 06:38
Copy link
Copy Markdown
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: 5

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/integrations/router.go (1)

513-518: Context parameter change breaks downstream context value propagation and requires fixing.

The code sets Bifrost-specific context values in bifrostCtx using custom-typed keys (e.g., schemas.BifrostContextKeySendBackRawResponse, schemas.BifrostContextKeyDirectKey) via context.WithValue. However, client methods now receive ctx (fasthttp.RequestCtx) instead of bifrostCtx. Since fasthttp.RequestCtx.Value() only supports string keys, these custom-typed context values set in bifrostCtx will not be accessible to the client methods, breaking features that depend on them (such as direct key selection, raw response handling, and integration-type routing). Pass bifrostCtx to client methods instead of ctx to preserve context value propagation.

core/bifrost.go (1)

2731-2743: Clarify PopulateLLMResponseAttributes’s contract when resp is nil

In executeRequestWithRetries, you always end the span and, for non-streaming calls, invoke:

if resp, ok := any(result).(*schemas.BifrostResponse); ok {
    tracer.PopulateLLMResponseAttributes(handle, resp, bifrostError)
}

When the handler returns an error, result can be a typed (*schemas.BifrostResponse)(nil), so ok is true and resp is nil. Tracer implementations that assume resp is non-nil could panic here.

Either:

  • Guard the call with resp != nil, or
  • Explicitly document in Tracer.PopulateLLMResponseAttributes that resp may be nil while err is non-nil and must be handled defensively.

This keeps tracing backends robust under error paths.

Also applies to: 2789-2817

♻️ Duplicate comments (5)
examples/plugins/hello-world/main.go (1)

19-26: HTTPTransportMiddleware implementation is correct for dynamic plugins.

The free function signature is appropriate for Go's plugin system, where exported symbols are looked up by name and wrapped by the DynamicPlugin adapter.

However, note that PreHook (line 29) and PostHook (lines 38-39) still attempt to read the context value "hello-world-plugin-transport-interceptor", which is no longer set by this middleware. This results in value1 always being nil, breaking the example's context propagation demonstration. This issue was flagged in previous reviews.

plugins/governance/main.go (2)

158-181: Missing prefix validation for x-bf-vk header.

The parseVirtualKey function validates that values from the Authorization (Bearer) and x-api-key headers start with VirtualKeyPrefix (lines 168, 177), but the x-bf-vk header on lines 160-163 is returned without prefix validation. This inconsistency could allow invalid virtual keys to pass through when provided via the x-bf-vk header.

🔎 Proposed fix
 	vkHeader := ctx.Request.Header.Peek("x-bf-vk")
 	if string(vkHeader) != "" {
+		vkValue := string(vkHeader)
+		if strings.HasPrefix(strings.ToLower(vkValue), VirtualKeyPrefix) {
-		return bifrost.Ptr(string(vkHeader))
+			return bifrost.Ptr(vkValue)
+		}
 	}

211-217: Fix error message accuracy.

Line 214: The error message says "failed to marshal" but the operation is unmarshaling (sonic.Unmarshal).

🔎 Proposed fix
-			p.logger.Error("failed to marshal request body to check for virtual key: %v", err)
+			p.logger.Error("failed to unmarshal request body: %v", err)
core/schemas/trace.go (1)

40-49: Guard Trace.Reset, Span.End, and Span.Reset with the existing mutex to avoid races

Trace and Span both embed a mu sync.Mutex, and mutating methods like AddSpan, GetSpan, SetAttribute, and AddEvent use it. However:

  • Trace.Reset clears IDs, spans, and attributes without locking.
  • Span.End updates EndTime, Status, and StatusMsg without locking.
  • Span.Reset clears all fields and slices without locking.

If these methods are ever called while other goroutines still hold references obtained via GetTrace/GetSpan (or via the store), this can lead to data races despite the presence of mu.

Given you already rely on the mutex for other mutations, it would be safer and more consistent to wrap these methods with the same lock, e.g.:

func (t *Trace) Reset() {
-   t.TraceID = ""
-   t.ParentID = ""
-   t.RootSpan = nil
-   t.Spans = t.Spans[:0]
-   t.StartTime = time.Time{}
-   t.EndTime = time.Time{}
-   t.Attributes = nil
+   t.mu.Lock()
+   defer t.mu.Unlock()
+   t.TraceID = ""
+   t.ParentID = ""
+   t.RootSpan = nil
+   t.Spans = t.Spans[:0]
+   t.StartTime = time.Time{}
+   t.EndTime = time.Time{}
+   t.Attributes = nil
}

and similarly for Span.End and Span.Reset.

This keeps span/trace lifecycle mutations consistent with the rest of the API and avoids subtle races when pooling is combined with concurrent readers.

Also applies to: 84-104

framework/tracing/llmspan.go (1)

190-193: ExtraParams written directly to attrs map may cause attribute key collisions.

This issue was previously flagged: user-provided keys from ExtraParams are written directly to the attributes map without namespacing. If a user sets a key like "gen_ai.request.model" in ExtraParams, it would overwrite the standard attribute set on line 22.

This pattern repeats throughout the file at lines 298-300, 365-367, 575-577, 708-710, 738-740, 753-755, 768-770, 783-785, 958-960, 982-984, 997-999, 1012-1014, 1027-1029.

🔎 Suggested approach: namespace ExtraParams
 	// ExtraParams
 	for k, v := range req.Params.ExtraParams {
-		attrs[k] = fmt.Sprintf("%v", v)
+		attrs["gen_ai.extra."+k] = fmt.Sprintf("%v", v)
 	}

Apply this pattern consistently at all ExtraParams locations throughout the file.

🧹 Nitpick comments (3)
ui/components/ui/sheet.tsx (1)

103-103: Simplify redundant mb-6 application.

The base className already includes mb-6, and the conditional also applies mb-6 when expandable is false, resulting in duplicate class application. While Tailwind handles this gracefully, the code can be simplified for clarity.

🔎 Suggested simplification
-<div data-slot="sheet-header" className={cn("mb-6 flex items-center", sheetContext?.expandable ? "p-0" : "mb-6")} {...props}>
+<div data-slot="sheet-header" className={cn("flex items-center", sheetContext?.expandable ? "p-0" : "mb-6")} {...props}>
ui/app/workspace/logs/views/logDetailsSheet.tsx (1)

181-201: Add aria-label to the action menu trigger for better accessibility.

The dropdown menu implementation follows best practices by consolidating actions into an overflow menu. However, the icon-only trigger button should include an aria-label to improve screen reader accessibility.

🔎 Suggested improvement
 <DropdownMenuTrigger asChild>
-  <Button variant="ghost" size="icon">
+  <Button variant="ghost" size="icon" aria-label="Log actions">
     <MoreVertical className="h-3 w-3" />
   </Button>
 </DropdownMenuTrigger>
framework/tracing/helpers.go (1)

10-29: Helpers are sound; optionally guard against nil TraceStore

The helper layer around TraceStore (GetTraceID, GetTrace, AddSpan*, EndSpan, SetSpanAttribute, AddSpanEvent) is clean and includes good nil/empty-traceID checks, so they safely no-op when there is no active trace.

If you ever expect these to be called before a tracer is fully wired, you might want an extra defensive if store == nil { return } in the helpers that dereference store to avoid panics from accidental nil stores. Right now, they assume the tracer always passes a non-nil store, which is OK but a bit brittle.

Also applies to: 49-82

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bfb02bb and b8f95ce.

⛔ Files ignored due to path filters (2)
  • core/go.sum is excluded by !**/*.sum
  • examples/plugins/hello-world/go.sum is excluded by !**/*.sum
📒 Files selected for processing (57)
  • core/bifrost.go
  • core/bifrost_test.go
  • core/go.mod
  • core/mcp.go
  • core/schemas/bifrost.go
  • core/schemas/plugin.go
  • core/schemas/trace.go
  • core/schemas/tracer.go
  • docs/docs.json
  • docs/plugins/getting-started.mdx
  • docs/plugins/migration-guide.mdx
  • docs/plugins/writing-plugin.mdx
  • examples/plugins/hello-world/go.mod
  • examples/plugins/hello-world/main.go
  • framework/configstore/tables/mcp.go
  • framework/plugins/dynamicplugin.go
  • framework/plugins/dynamicplugin_test.go
  • framework/tracing/helpers.go
  • framework/tracing/llmspan.go
  • framework/tracing/propagation.go
  • framework/tracing/store.go
  • framework/tracing/tracer.go
  • plugins/governance/go.mod
  • plugins/governance/main.go
  • plugins/jsonparser/main.go
  • plugins/logging/main.go
  • plugins/maxim/main.go
  • plugins/mocker/main.go
  • plugins/otel/converter.go
  • plugins/otel/main.go
  • plugins/otel/ttlsyncmap.go
  • plugins/semanticcache/main.go
  • plugins/telemetry/main.go
  • transports/bifrost-http/handlers/cache.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/handlers/health.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/handlers/logging.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/handlers/mcpServer.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • transports/bifrost-http/handlers/plugins.go
  • transports/bifrost-http/handlers/providers.go
  • transports/bifrost-http/handlers/session.go
  • transports/bifrost-http/handlers/ui.go
  • transports/bifrost-http/handlers/websocket.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/lib/middleware.go
  • transports/bifrost-http/server/server.go
  • transports/go.mod
  • ui/app/workspace/logs/views/logDetailsSheet.tsx
  • ui/components/sidebar.tsx
  • ui/components/ui/sheet.tsx
  • ui/components/ui/switch.tsx
💤 Files with no reviewable changes (2)
  • framework/configstore/tables/mcp.go
  • plugins/otel/ttlsyncmap.go
✅ Files skipped from review due to trivial changes (1)
  • docs/plugins/migration-guide.mdx
🚧 Files skipped from review as they are similar to previous changes (17)
  • core/mcp.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/handlers/mcpServer.go
  • plugins/mocker/main.go
  • plugins/telemetry/main.go
  • plugins/governance/go.mod
  • plugins/maxim/main.go
  • transports/bifrost-http/handlers/cache.go
  • transports/bifrost-http/handlers/logging.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/go.mod
  • core/bifrost_test.go
  • plugins/semanticcache/main.go
  • ui/components/sidebar.tsx
  • transports/bifrost-http/handlers/middlewares_test.go
  • transports/bifrost-http/handlers/websocket.go
  • ui/components/ui/switch.tsx
🧰 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/governance.go
  • transports/bifrost-http/handlers/plugins.go
  • framework/plugins/dynamicplugin_test.go
  • transports/bifrost-http/handlers/config.go
  • core/go.mod
  • transports/bifrost-http/handlers/session.go
  • ui/app/workspace/logs/views/logDetailsSheet.tsx
  • core/schemas/bifrost.go
  • docs/plugins/getting-started.mdx
  • core/schemas/plugin.go
  • ui/components/ui/sheet.tsx
  • plugins/governance/main.go
  • transports/bifrost-http/handlers/providers.go
  • docs/plugins/writing-plugin.mdx
  • core/schemas/tracer.go
  • framework/tracing/store.go
  • transports/bifrost-http/server/server.go
  • plugins/otel/main.go
  • transports/bifrost-http/handlers/health.go
  • plugins/logging/main.go
  • transports/bifrost-http/handlers/middlewares.go
  • framework/plugins/dynamicplugin.go
  • core/bifrost.go
  • plugins/otel/converter.go
  • transports/bifrost-http/handlers/ui.go
  • transports/bifrost-http/lib/middleware.go
  • examples/plugins/hello-world/main.go
  • plugins/jsonparser/main.go
  • framework/tracing/tracer.go
  • core/schemas/trace.go
  • framework/tracing/propagation.go
  • framework/tracing/helpers.go
  • examples/plugins/hello-world/go.mod
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/handlers/integrations.go
  • docs/docs.json
  • framework/tracing/llmspan.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/governance.go
  • transports/bifrost-http/handlers/plugins.go
  • framework/plugins/dynamicplugin_test.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/session.go
  • core/schemas/bifrost.go
  • core/schemas/plugin.go
  • plugins/governance/main.go
  • transports/bifrost-http/handlers/providers.go
  • core/schemas/tracer.go
  • framework/tracing/store.go
  • transports/bifrost-http/server/server.go
  • plugins/otel/main.go
  • transports/bifrost-http/handlers/health.go
  • plugins/logging/main.go
  • transports/bifrost-http/handlers/middlewares.go
  • framework/plugins/dynamicplugin.go
  • core/bifrost.go
  • plugins/otel/converter.go
  • transports/bifrost-http/handlers/ui.go
  • transports/bifrost-http/lib/middleware.go
  • examples/plugins/hello-world/main.go
  • plugins/jsonparser/main.go
  • framework/tracing/tracer.go
  • core/schemas/trace.go
  • framework/tracing/propagation.go
  • framework/tracing/helpers.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/handlers/integrations.go
  • framework/tracing/llmspan.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/governance.go
  • transports/bifrost-http/handlers/plugins.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/session.go
  • transports/bifrost-http/handlers/providers.go
  • transports/bifrost-http/server/server.go
  • transports/bifrost-http/handlers/health.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/handlers/ui.go
  • transports/bifrost-http/lib/middleware.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/handlers/integrations.go
📚 Learning: 2025-12-22T10:50:40.990Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1154
File: plugins/governance/store.go:1165-1186
Timestamp: 2025-12-22T10:50:40.990Z
Learning: In the Bifrost governance plugin, budgets and rate limits have 1:1 relationships with their parent entities (virtual keys, teams, customers). Do not assume sharing; ensure cascade deletion logic only deletes budgets/rate limits when there are no shared references. Enforce invariants in code and add tests to verify no cross-entity sharing and that cascade deletes only remove the specific child of the parent. If a counterexample arises, adjust data model or add guards.

Applied to files:

  • plugins/governance/main.go
🧬 Code graph analysis (27)
transports/bifrost-http/handlers/governance.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/plugins.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
framework/plugins/dynamicplugin_test.go (1)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-26)
transports/bifrost-http/handlers/config.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/session.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
ui/app/workspace/logs/views/logDetailsSheet.tsx (3)
ui/components/ui/sheet.tsx (1)
  • SheetContent (137-137)
ui/components/ui/alertDialog.tsx (9)
  • AlertDialog (83-83)
  • AlertDialogTrigger (93-93)
  • AlertDialogContent (86-86)
  • AlertDialogHeader (89-89)
  • AlertDialogTitle (92-92)
  • AlertDialogDescription (87-87)
  • AlertDialogFooter (88-88)
  • AlertDialogCancel (85-85)
  • AlertDialogAction (84-84)
ui/components/ui/dropdownMenu.tsx (5)
  • DropdownMenu (193-193)
  • DropdownMenuTrigger (207-207)
  • DropdownMenuContent (195-195)
  • DropdownMenuItem (197-197)
  • DropdownMenuSeparator (202-202)
core/schemas/bifrost.go (2)
core/schemas/tracer.go (1)
  • Tracer (13-47)
framework/tracing/tracer.go (1)
  • Tracer (14-16)
core/schemas/plugin.go (2)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-26)
core/schemas/trace.go (1)
  • Trace (10-19)
transports/bifrost-http/handlers/providers.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
core/schemas/tracer.go (4)
core/schemas/trace.go (3)
  • Trace (10-19)
  • SpanKind (115-115)
  • SpanStatus (143-143)
framework/tracing/helpers.go (1)
  • EndSpan (50-56)
core/schemas/bifrost.go (3)
  • BifrostRequest (168-188)
  • BifrostResponse (330-350)
  • BifrostError (469-478)
framework/tracing/tracer.go (1)
  • Tracer (14-16)
transports/bifrost-http/server/server.go (4)
core/schemas/plugin.go (2)
  • BifrostHTTPMiddleware (38-38)
  • ObservabilityPlugin (123-137)
transports/bifrost-http/handlers/middlewares.go (2)
  • TransportInterceptorMiddleware (50-70)
  • NewTracingMiddleware (207-214)
framework/tracing/store.go (1)
  • NewTraceStore (26-53)
framework/tracing/tracer.go (1)
  • NewTracer (19-21)
plugins/otel/main.go (3)
core/schemas/plugin.go (1)
  • ObservabilityPlugin (123-137)
core/schemas/context.go (1)
  • BifrostContext (31-41)
core/schemas/trace.go (1)
  • Trace (10-19)
transports/bifrost-http/handlers/health.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
plugins/logging/main.go (5)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-26)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
ui/lib/types/logs.ts (1)
  • TranscriptionInput (15-17)
core/schemas/bifrost.go (1)
  • TranscriptionRequest (102-102)
transports/bifrost-http/handlers/inference.go (1)
  • TranscriptionRequest (285-289)
transports/bifrost-http/handlers/middlewares.go (6)
core/schemas/plugin.go (3)
  • BifrostHTTPMiddleware (38-38)
  • ObservabilityPlugin (123-137)
  • Plugin (71-97)
transports/bifrost-http/lib/middleware.go (1)
  • ChainMiddlewares (11-23)
core/schemas/tracer.go (1)
  • Tracer (13-47)
framework/tracing/propagation.go (1)
  • ExtractParentID (26-36)
core/schemas/trace.go (3)
  • SpanKindHTTPRequest (131-131)
  • SpanStatusError (151-151)
  • SpanStatusOk (149-149)
framework/tracing/helpers.go (1)
  • EndSpan (50-56)
core/bifrost.go (4)
core/schemas/tracer.go (2)
  • Tracer (13-47)
  • DefaultTracer (80-82)
framework/tracing/tracer.go (1)
  • Tracer (14-16)
core/schemas/trace.go (3)
  • AttrProviderName (159-159)
  • AttrRequestModel (160-160)
  • SpanKind (115-115)
framework/tracing/helpers.go (1)
  • EndSpan (50-56)
plugins/otel/converter.go (2)
plugins/otel/main.go (1)
  • OtelPlugin (58-75)
core/schemas/trace.go (17)
  • Trace (10-19)
  • Span (52-65)
  • SpanKind (115-115)
  • SpanKindLLMCall (121-121)
  • SpanKindHTTPRequest (131-131)
  • SpanKindPlugin (123-123)
  • SpanKindInternal (139-139)
  • SpanKindRetry (127-127)
  • SpanKindFallback (129-129)
  • SpanKindMCPTool (125-125)
  • SpanKindEmbedding (133-133)
  • SpanKindSpeech (135-135)
  • SpanKindTranscription (137-137)
  • SpanStatus (143-143)
  • SpanStatusOk (149-149)
  • SpanStatusError (151-151)
  • SpanEvent (107-111)
transports/bifrost-http/handlers/ui.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/lib/middleware.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
examples/plugins/hello-world/main.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
plugins/jsonparser/main.go (2)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-26)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
framework/tracing/tracer.go (6)
framework/tracing/store.go (1)
  • TraceStore (13-23)
core/schemas/tracer.go (2)
  • Tracer (13-47)
  • SpanHandle (8-8)
core/schemas/trace.go (5)
  • Trace (10-19)
  • SpanKind (115-115)
  • Span (52-65)
  • SpanStatus (143-143)
  • SpanEvent (107-111)
framework/tracing/helpers.go (3)
  • GetTraceID (11-20)
  • EndSpan (50-56)
  • GetTrace (23-29)
core/schemas/bifrost.go (4)
  • BifrostContextKeySpanID (144-144)
  • BifrostRequest (168-188)
  • BifrostResponse (330-350)
  • BifrostError (469-478)
framework/tracing/llmspan.go (3)
  • PopulateRequestAttributes (14-60)
  • PopulateResponseAttributes (64-106)
  • PopulateErrorAttributes (109-124)
core/schemas/trace.go (2)
framework/tracing/helpers.go (1)
  • AddSpan (32-38)
ui/lib/constants/logs.ts (1)
  • Status (163-163)
framework/tracing/helpers.go (3)
core/schemas/bifrost.go (1)
  • BifrostContextKeyTraceID (143-143)
framework/tracing/store.go (1)
  • TraceStore (13-23)
core/schemas/trace.go (5)
  • Trace (10-19)
  • SpanKind (115-115)
  • Span (52-65)
  • SpanStatus (143-143)
  • SpanEvent (107-111)
transports/bifrost-http/integrations/router.go (2)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
core/schemas/bifrost.go (7)
  • ListModelsRequest (92-92)
  • TextCompletionRequest (93-93)
  • ChatCompletionRequest (95-95)
  • ResponsesRequest (97-97)
  • EmbeddingRequest (99-99)
  • SpeechRequest (100-100)
  • TranscriptionRequest (102-102)
transports/bifrost-http/handlers/integrations.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
framework/tracing/llmspan.go (6)
core/schemas/bifrost.go (18)
  • BifrostRequest (168-188)
  • RequestType (89-89)
  • TextCompletionRequest (93-93)
  • EmbeddingRequest (99-99)
  • TranscriptionRequest (102-102)
  • SpeechRequest (100-100)
  • ResponsesRequest (97-97)
  • BatchCreateRequest (104-104)
  • BatchListRequest (105-105)
  • BatchRetrieveRequest (106-106)
  • BatchCancelRequest (107-107)
  • BatchResultsRequest (108-108)
  • FileUploadRequest (109-109)
  • FileListRequest (110-110)
  • FileRetrieveRequest (111-111)
  • FileDeleteRequest (112-112)
  • FileContentRequest (113-113)
  • BifrostResponse (330-350)
core/schemas/trace.go (24)
  • AttrProviderName (159-159)
  • AttrRequestModel (160-160)
  • AttrError (208-208)
  • AttrErrorType (209-209)
  • AttrErrorCode (210-210)
  • AttrVirtualKeyID (220-220)
  • AttrVirtualKeyName (221-221)
  • AttrSelectedKeyID (222-222)
  • AttrSelectedKeyName (223-223)
  • AttrTeamID (224-224)
  • AttrTeamName (225-225)
  • AttrCustomerID (226-226)
  • AttrCustomerName (227-227)
  • AttrNumberOfRetries (228-228)
  • AttrFallbackIndex (229-229)
  • AttrMaxTokens (163-163)
  • AttrTemperature (164-164)
  • AttrTopP (165-165)
  • AttrStopSequences (166-166)
  • AttrPresencePenalty (167-167)
  • AttrFrequencyPenalty (168-168)
  • AttrParallelToolCall (169-169)
  • AttrRequestUser (170-170)
  • AttrN (175-175)
core/schemas/chatcompletions.go (1)
  • BifrostChatRequest (12-19)
core/schemas/responses.go (1)
  • BifrostResponsesRequest (32-39)
core/schemas/files.go (6)
  • BifrostFileUploadRequest (53-67)
  • BifrostFileListRequest (109-128)
  • BifrostFileRetrieveRequest (146-159)
  • BifrostFileDeleteRequest (186-198)
  • BifrostFileContentRequest (215-227)
  • BifrostFileUploadResponse (90-106)
framework/modelcatalog/main.go (1)
  • ModelCatalog (23-44)
🔇 Additional comments (53)
examples/plugins/hello-world/go.mod (2)

7-7: Verify core dependency version reflects breaking changes.

The example plugin depends on github.com/maximhq/bifrost/core v1.2.40. Given the breaking change (replacing TransportInterceptor with HTTPTransportMiddleware), confirm this core version includes the new interface and that the hello-world plugin's implementation aligns with it.

Also verify that the reorganization of direct and indirect dependencies is intentional and consistent with go mod tidy output (rather than manually maintained).

Also applies to: 11-23


8-8: No changes needed. The fasthttp v1.67.0 version in hello-world/go.mod is correct and consistent with the core module v1.2.40. The HTTPTransportMiddleware implementation in main.go properly uses the fasthttp.RequestHandler pattern, and all transitive dependencies (brotli, compress, bytebufferpool) are expected from fasthttp. Versions are aligned across the plugin stack.

ui/app/workspace/logs/views/logDetailsSheet.tsx (4)

16-16: LGTM!

The new imports for dropdown menu components and the MoreVertical icon are appropriate for the refactored action menu pattern.

Also applies to: 22-22


202-218: LGTM!

The AlertDialog implementation for delete confirmation is well-structured. The dialog properly handles the destructive action with clear messaging, cancel option, and correctly closes the sheet after deletion.


171-171: Remove the expandable prop verification concern.

The expandable prop is properly implemented as a custom feature in the SheetContent component. It has a default value of false, is integrated into the component's context, and controls responsive width and styling behavior. No changes needed.

Likely an incorrect or invalid review comment.


195-195: The destructive variant on DropdownMenuItem is already properly implemented in the wrapper component with full type safety and appropriate styling for light/dark modes and focus states. No changes needed.

core/go.mod (1)

27-27: LGTM: Routine indirect dependency updates.

The addition of semver/v3 and the update to google/pprof appear to be indirect dependency changes, likely pulled in by the new tracing infrastructure or other updated dependencies in this PR.

Also applies to: 50-50

core/schemas/bifrost.go (2)

25-25: LGTM: Well-documented tracing support.

The addition of the Tracer field to BifrostConfig with clear documentation about the nil = NoOpTracer behavior is excellent. This provides a clean opt-in mechanism for distributed tracing.


143-144: LGTM: Tracing context keys follow established patterns.

The new context keys for trace ID and span ID follow the existing BifrostContextKey naming convention and enable proper trace propagation through the request lifecycle. The comments clearly indicate these are set by the tracing middleware and tracer respectively.

docs/docs.json (1)

135-136: LGTM: Essential documentation for breaking changes.

Adding the migration guide to the Custom plugins section is crucial for this breaking change. The placement after "writing-plugin" makes logical sense for developers who need to update their plugins from v1.3.x to v1.4.x.

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

80-80: LGTM: Middleware type migration aligns with plugin v2 architecture.

The migration from lib.BifrostHTTPMiddleware to schemas.BifrostHTTPMiddleware centralizes the middleware type definition in the core schemas package. This change is consistent with the broader plugin v2 architecture migration and maintains the same functional signature.

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

53-53: LGTM: Consistent middleware type migration.

This change mirrors the migration pattern seen across all handler files, updating the middleware type to use the centralized schemas.BifrostHTTPMiddleware definition.

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

8-8: LGTM: Integration handler updated for middleware type migration.

The addition of the schemas import and the update to use schemas.BifrostHTTPMiddleware maintains consistency with the rest of the HTTP transport layer. The middleware forwarding logic to extensions remains unchanged.

Also applies to: 37-37

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

11-11: LGTM: UI handler completes the middleware type migration.

This change completes the systematic migration across all handler files. The UI serving logic remains unchanged while the middleware type is updated to the centralized schemas.BifrostHTTPMiddleware.

Also applies to: 29-29

plugins/logging/main.go (1)

196-199: Returning nil for HTTPTransportMiddleware() is correct since the logging plugin only uses PreHook and PostHook for request/response logging.

The framework properly handles nil middleware returns. The plugin loader explicitly checks for nil middleware at line 62 in transports/bifrost-http/handlers/middlewares.go and skips them, ensuring only active middlewares are passed to the chaining logic.

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

11-11: LGTM! Clean migration to schemas.BifrostHTTPMiddleware.

The import and signature change correctly align with the new middleware type defined in core/schemas/plugin.go. The routing logic remains unchanged, maintaining backward compatibility at the handler level.

Also applies to: 32-32

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

15-15: LGTM! Consistent middleware type migration.

The signature update follows the same pattern as other handlers in this PR, correctly using the new schemas.BifrostHTTPMiddleware type.

Also applies to: 55-55

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

9-9: LGTM! Handler signature properly updated.

The middleware type migration is consistent with the broader refactor across all HTTP handlers.

Also applies to: 27-27

framework/plugins/dynamicplugin_test.go (1)

16-16: LGTM! Test properly validates the new middleware pattern.

The test correctly:

  • Creates a mock next handler to track invocation
  • Obtains the middleware function from the plugin
  • Wraps the mock handler with the middleware
  • Verifies the middleware calls the next handler in the chain

This validates the middleware chaining semantics introduced by the new architecture.

Also applies to: 54-84

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

15-15: LGTM! Governance handler signature updated consistently.

The middleware type migration aligns with the unified schemas.BifrostHTTPMiddleware pattern used across all HTTP handlers.

Also applies to: 154-154

docs/plugins/getting-started.mdx (1)

51-68: LGTM! Excellent documentation of the breaking change.

The versioned tabs clearly distinguish the v1.4.x+ API (with HTTPTransportMiddleware) from the v1.3.x API (with TransportInterceptor), helping plugin developers understand the migration path. The execution order is properly documented for both versions.

Also applies to: 90-103

core/schemas/plugin.go (3)

36-38: LGTM! Clean middleware type definition.

The BifrostHTTPMiddleware type follows the standard HTTP middleware pattern: a function that takes the next handler and returns a new handler. This is more idiomatic than the previous TransportInterceptor approach.


75-79: LGTM! Plugin interface properly migrated to middleware pattern.

The replacement of TransportInterceptor with HTTPTransportMiddleware() BifrostHTTPMiddleware provides a cleaner, more composable API for HTTP transport interception. The method signature is consistent with standard middleware patterns used in Go HTTP frameworks.


109-137: LGTM! ObservabilityPlugin interface enables async trace forwarding.

The new interface properly extends Plugin and adds the Inject method for receiving completed traces. The documentation clearly explains:

  • Async invocation after response is written (no client latency impact)
  • Usage via Go type assertion
  • Expected backend implementations (OTEL, Datadog, Jaeger, etc.)

This is a well-designed extension point for observability integrations.

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

72-72: Middleware type migration looks correct.

The interface and method signatures are updated to use schemas.BifrostHTTPMiddleware instead of lib.BifrostHTTPMiddleware, aligning with the centralized type definition in core/schemas/plugin.go. This is consistent across the codebase migration in this PR.

Also applies to: 320-320

docs/plugins/writing-plugin.mdx (1)

65-126: Documentation for v1.4.x+ HTTPTransportMiddleware is clear and comprehensive.

The new tab structure with side-by-side v1.3.x and v1.4.x+ examples provides excellent migration guidance. The HTTPTransportMiddleware example correctly demonstrates the middleware pattern with next(ctx) invocation.

One minor observation: The example shows request modification before next(ctx) and response modification after, which is good practice to document.

plugins/jsonparser/main.go (1)

86-89: HTTPTransportMiddleware returning nil is appropriate for this plugin.

Since the JSON parser plugin only processes responses (in PostHook), returning nil from HTTPTransportMiddleware is correct. The middleware chain in lib.ChainMiddlewares properly skips nil middlewares.

plugins/governance/main.go (1)

183-234: HTTPTransportMiddleware implementation looks solid.

The middleware correctly:

  • Parses virtual keys from multiple header sources
  • Gracefully degrades on errors by calling next(ctx) rather than blocking requests
  • Modifies headers and body appropriately using fasthttp APIs
  • Follows the middleware pattern with next(ctx) invocation
transports/bifrost-http/lib/middleware.go (1)

3-23: Clean migration to centralized middleware type.

The ChainMiddlewares function now uses schemas.BifrostHTTPMiddleware from the core schemas package instead of a local type definition. The chaining logic (right-to-left composition for left-to-right execution) remains unchanged and correct.

framework/tracing/store.go (2)

56-82: Object pooling implementation looks correct.

The CreateTrace method properly resets pooled objects before reuse:

  • Generates new TraceID
  • Resets time fields
  • Clears RootSpan
  • Resets slices while preserving capacity (efficient)
  • Clears attributes map

This prevents data leakage between pooled objects.


120-165: Span lifecycle management is well implemented.

The StartSpan method properly:

  • Gets span from pool
  • Resets all fields (including events and attributes)
  • Sets parent relationship based on root span
  • Adds span to trace

The parent tracking logic (lines 154-159) correctly handles both root spans and child spans.

plugins/otel/main.go (2)

201-224: Inject method implementation is clean and correct.

The Inject method properly:

  • Handles nil trace gracefully
  • Checks client initialization
  • Converts trace to OTEL ResourceSpan format
  • Emits to collector with error handling
  • Logs errors but returns them for upstream handling

This aligns well with the ObservabilityPlugin interface contract described in core/schemas/plugin.go.


237-238: Good use of compile-time interface check.

The var _ schemas.ObservabilityPlugin = (*OtelPlugin)(nil) pattern ensures the plugin correctly implements the interface at compile time, catching signature mismatches early.

transports/bifrost-http/handlers/middlewares.go (4)

49-70: TransportInterceptorMiddleware correctly collects and chains plugin middlewares.

The implementation:

  • Collects non-nil middlewares from all plugins
  • Uses lib.ChainMiddlewares to compose them
  • Gracefully handles empty plugin lists

This is a clean migration from the old per-request interception pattern to middleware composition.


201-214: TracingMiddleware design with atomic plugin updates is well thought out.

Using atomic.Pointer[[]schemas.ObservabilityPlugin] allows thread-safe updates to observability plugins at runtime (e.g., during hot-reload) without requiring locks on the hot path. The constructor properly initializes and stores the initial plugin list.


284-299: GetObservabilityPlugins helper is clean and correct.

The type assertion pattern correctly filters plugins implementing ObservabilityPlugin interface. Early return for empty input is a good optimization.


247-277: Async trace forwarding pattern is sound, but lacks graceful shutdown coordination.

The defer with goroutine pattern appropriately ensures trace completion and async execution, using context.Background() since request context is done. However, the trace forwarding goroutines spawned here are untracked and will not be coordinated during server shutdown—traces may be lost if the server terminates while these goroutines are in flight.

For production deployments, consider tracking in-flight traces with sync.WaitGroup if guaranteed delivery is required.

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

958-1037: HTTP middleware type migration and tracing bootstrap look consistent

The migration to schemas.BifrostHTTPMiddleware across RegisterInferenceRoutes, RegisterAPIRoutes, RegisterUIRoutes, and PrepareCommonMiddlewares, plus the new bootstrap logic that:

  • Prepends handlers.TransportInterceptorMiddleware to inference middlewares, and
  • When any schemas.ObservabilityPlugin is loaded, creates a TraceStore/Tracer, injects it into s.Client via SetTracer, and prepends the tracing middleware

is internally consistent and preserves the intended middleware ordering for inference traffic.

This neatly centralizes tracing/bootstrap concerns in the server without leaking HTTP details into core.

Also applies to: 1087-1098, 1205-1246

plugins/otel/converter.go (1)

49-70: OTEL conversion logic for traces/spans/events looks solid

The OTEL converter:

  • Normalizes IDs with hexToBytes,
  • Maps SpanKind/SpanStatus appropriately,
  • Translates span and event attributes via convertAttributesToKeyValues/anyToKeyValue, and
  • Builds ResourceSpan with reasonable resource and instrumentation scope metadata,

all in a nil-safe way for attributes and events. As long as callers never pass a nil *schemas.Trace, this should behave correctly and produce well-formed OTLP payloads.

Also applies to: 72-87, 90-107, 112-147, 148-229, 231-257, 259-269, 271-285

core/schemas/tracer.go (1)

6-47: Tracer abstraction and NoOpTracer default are well-structured

The Tracer interface cleanly captures the operations Bifrost needs (trace lifecycle, span lifecycle, attributes, LLM request/response enrichment), and NoOpTracer provides a safe default that keeps all call sites simple (no nil checks) while avoiding overhead when tracing is disabled.

The compile-time assertion and DefaultTracer() helper round out the contract cleanly.

Also applies to: 49-86

framework/plugins/dynamicplugin.go (1)

24-29: Dynamic plugin HTTP middleware wiring matches the new plugin contract

The DynamicPlugin now stores an httpTransportMiddleware of type schemas.BifrostHTTPMiddleware, exposes it via HTTPTransportMiddleware(), and loadDynamicPlugin looks up the HTTPTransportMiddleware symbol and asserts it to that function type. This matches the updated plugin interface (exporting a func HTTPTransportMiddleware(next fasthttp.RequestHandler) fasthttp.RequestHandler) and integrates cleanly with the HTTP transport middleware chain.

Dynamic plugins that don’t need HTTP interception can simply implement a pass-through middleware.

Also applies to: 36-39, 140-147

framework/tracing/tracer.go (6)

23-44: LGTM!

The trace lifecycle methods correctly delegate to the underlying store with appropriate nil checks. The comment on line 35 properly documents caller responsibility for releasing traces.


46-75: LGTM!

The StartSpan implementation correctly handles parent-child span relationships and maintains context propagation. The defensive nil checks ensure robustness when trace or span creation fails.


77-84: LGTM!

EndSpan correctly delegates to the store with defensive type checking. Passing nil for attributes is appropriate since attributes should be set via SetAttribute during the span's lifetime.


86-100: LGTM!

SetAttribute is properly defensive with nil checks at each step. The delegation pattern to the underlying trace and span is clean and correct.


102-120: LGTM!

AddEvent follows the same defensive pattern as SetAttribute. Using time.Now() for the event timestamp is appropriate.


122-162: LGTM!

The LLM attribute population methods correctly integrate with the helper functions from llmspan.go. The pattern of retrieving attribute maps and applying them to spans is clean, and all nil checks are in place.

framework/tracing/llmspan.go (6)

12-60: LGTM!

PopulateRequestAttributes provides a clean entry point with proper nil checking and comprehensive request type coverage. The delegation pattern to type-specific populators is well-structured.


62-124: LGTM!

Both response and error attribute population functions are properly nil-safe and provide comprehensive coverage. The conditional checks for optional error fields are appropriate.


126-153: LGTM!

PopulateContextAttributes correctly populates optional context metadata with appropriate conditional checks. Setting retry and fallback counts unconditionally is appropriate since zero is a meaningful value.


206-936: LGTM!

The type-specific attribute population functions (Chat, TextCompletion, Embedding, Transcription, Speech, Responses, Batch, and File operations) all follow consistent patterns with proper nil checks and conditional handling of optional fields. The implementation is comprehensive and correct, aside from the ExtraParams collision issue already noted.


1121-1237: LGTM!

The message extraction helpers are well-structured with appropriate nil checks throughout. The MessageSummary and ResponsesMessageSummary types provide clean abstractions for tracing, and the content extraction logic correctly handles both string and block-based content formats.


1243-1255: LGTM!

PopulateCostAttribute correctly handles optional pricing integration with appropriate nil checks. The integration with ModelCatalog.CalculateCostWithCacheDebug provides cost tracking when a pricing manager is available.

Comment thread core/bifrost.go Outdated
Comment thread framework/tracing/propagation.go
Comment thread framework/tracing/store.go
Comment thread framework/tracing/tracer.go Outdated
Comment thread framework/tracing/tracer.go Outdated
@akshaydeo akshaydeo force-pushed the 11-19-plugins_v2_architecture branch from b8f95ce to fb945b9 Compare December 23, 2025 10:37
Copy link
Copy Markdown
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

Caution

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

⚠️ Outside diff range comments (5)
ui/components/ui/switch.tsx (1)

13-32: UI changes appear unrelated to the PR's stated objective.

This PR is described as "plugins v2 architecture" focusing on Go backend changes (HTTP middleware, plugin interfaces, transport layer). However, this file contains UI component modifications (default size and border radius changes) that seem unrelated to the plugin architecture refactor.

Consider moving these UI changes to a separate PR for better separation of concerns and clearer change tracking.

docs/plugins/writing-plugin.mdx (1)

54-59: Update go.mod example version to support HTTPTransportMiddleware

The v1.4.x+ example plugin code uses HTTPTransportMiddleware() and schemas.BifrostHTTPMiddleware (lines 66–102), but the go.mod snippet pins github.com/maximhq/bifrost/core v1.2.38. The hello-world example in the repository uses v1.2.40 with this API. Update the pinned version to at least v1.2.40 to avoid compilation errors.

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

503-693: Use the bifrost context for all non-streaming client calls

handleNonStreamingRequest passes ctx (a *fasthttp.RequestCtx) to client methods, but it should use a context.Context derived from bifrostCtx, matching the pattern in handleBatchRequest (line 698) and handleFileRequest.

Add requestCtx := *bifrostCtx after the comments and use requestCtx for all client method calls:

 func (g *GenericRouter) handleNonStreamingRequest(ctx *fasthttp.RequestCtx, config RouteConfig, req interface{}, bifrostReq *schemas.BifrostRequest, bifrostCtx *context.Context) {
 	// Use the cancellable context from ConvertToBifrostContext
 	// While we can't detect client disconnects until we try to write, having a cancellable context
 	// allows providers that check ctx.Done() to cancel early if needed. This is less critical than
 	// streaming requests (where we actively detect write errors), but still provides a mechanism
 	// for providers to respect cancellation.
+	requestCtx := *bifrostCtx
 	
 	var response interface{}
 	var err error
 
 	switch {
 	case bifrostReq.ListModelsRequest != nil:
-		listModelsResponse, bifrostErr := g.client.ListModelsRequest(ctx, bifrostReq.ListModelsRequest)
+		listModelsResponse, bifrostErr := g.client.ListModelsRequest(requestCtx, bifrostReq.ListModelsRequest)

Replace ctx with requestCtx in all client method calls: ListModelsRequest, TextCompletionRequest, ChatCompletionRequest, ResponsesRequest, EmbeddingRequest, SpeechRequest, and TranscriptionRequest.

core/bifrost.go (2)

2743-2850: Streaming detection via BifrostContextKeyStreamStartTime breaks non-stream plugin spans

executeRequestWithRetries now unconditionally sets:

streamStartTime := time.Now()
*ctx = context.WithValue(*ctx, schemas.BifrostContextKeyStreamStartTime, streamStartTime)
*ctx = context.WithValue(*ctx, schemas.BifrostContextKeyTracer, tracer)

for all request types (streaming and non-streaming). PluginPipeline.RunPostHooks detects “streaming mode” via:

isStreaming := (*ctx).Value(schemas.BifrostContextKeyStreamStartTime) != nil

and, when isStreaming is true, it:

  • Skips per-plugin posthook spans and
  • Only accumulates timing into postHookTimings/chunkCount.

Because BifrostContextKeyStreamStartTime is now always set in the worker context, every non-stream request is treated as streaming in RunPostHooks, which:

  • Silently removes the existing per-plugin post-hook spans for all synchronous calls.
  • Accumulates timing that is never flushed for non-stream flows (no finalizer is invoked).

You likely want one of:

  • Only setting BifrostContextKeyStreamStartTime for stream requests (e.g., pass an isStreaming flag into executeRequestWithRetries and gate the WithValue calls), or
  • Changing RunPostHooks to key off a dedicated streaming flag (e.g., BifrostContextKeyDeferTraceCompletion or similar) instead of the presence of a timestamp used for TTFT.

As-is, this is a functional regression for plugin observability on non-streaming requests.

Also applies to: 3261-3323


2978-2996: Streaming PluginPipeline lifetime vs pooling can race and drop metrics

For streaming requests, requestWorker does:

pipeline := bifrost.getPluginPipeline()
postHookRunner := func(...) { return pipeline.RunPostHooks(...) }
// stores postHookSpanFinalizer capturing `pipeline` into req.Context
...
stream, bifrostError = executeRequestWithRetries(..., postHookRunner, ...)
...
if pipeline != nil {
    bifrost.releasePluginPipeline(pipeline) // calls reset and returns to pool
}

Meanwhile, providers receive postHookRunner and (by design) call it from their streaming goroutines as chunks arrive. Additionally, HTTP/transport code is expected to call the postHookSpanFinalizer closure (which also closes spans on pipeline) at the end of streaming, using the reference stored in the context.

This creates two problems:

  1. pipeline is reset and returned to the pool while streaming goroutines and the finalizer may still be using it → data races and incorrect postHookTimings/chunkCount.
  2. Because resetPluginPipeline clears postHookTimings and postHookPluginOrder, FinalizeStreamingPostHookSpans will frequently see an empty map and produce no aggregated spans, even if RunPostHooks was called many times during streaming.

You probably want to:

  • Keep the streaming PluginPipeline alive until the stream truly ends (e.g., release it from the place that closes the stream / calls the finalizer), or
  • Avoid pooling PluginPipeline for streaming at all, and only reuse it for non-streaming flows.

As implemented, the combination of pooling + streaming callbacks is unsafe and undermines the new aggregated post-hook span metrics.

Also applies to: 3370-3423

♻️ Duplicate comments (9)
ui/components/ui/switch.tsx (1)

13-13: Counterintuitive naming: "md" is smaller than "default" and is now the default.

The past review correctly identified that "md" renders smaller (h-5 w-9) than "default" (h-6 w-11), which is confusing. Setting size = "md" as the default parameter exacerbates this—users now get the smaller variant by default, and "medium" being smaller than "default" remains counterintuitive.

Consider renaming to make the size relationship clear (e.g., "sm" and "md", or "compact" and "default"), and set the default parameter to represent the actual baseline size.

examples/plugins/hello-world/main.go (1)

7-7: Hello-world example still reads a context value that HTTPTransportMiddleware never sets.

PreHook/PostHook log hello-world-plugin-transport-interceptor, but the new HTTPTransportMiddleware only prints and forwards, so that key is always nil and the data‑flow demo is broken.

Consider either:

  • Updating HTTPTransportMiddleware to extract the *schemas.BifrostContext from fasthttp.RequestCtx (e.g., via ctx.UserValue("bifrost-context")) and set the expected key, or
  • Removing the reads of "hello-world-plugin-transport-interceptor" from PreHook/PostHook if you no longer want to demonstrate that flow.

Also applies to: 19-43

framework/tracing/store.go (1)

339-367: Harden TraceStore.Stop against double calls and clean up deferred spans

Stop() currently stops the ticker and unconditionally closes s.stopCleanup:

func (s *TraceStore) Stop() {
	if s.cleanupTicker != nil {
		s.cleanupTicker.Stop()
	}
	close(s.stopCleanup)
	s.cleanupWg.Wait()
}

If Stop() is ever called more than once (or called after a zero-TTL store that never started cleanup), this will panic with “close of closed channel”. A prior review already flagged this pattern.

Separately, cleanupOldTraces only evicts entries from s.traces. Any matching entries in deferredSpans for those trace IDs will remain, which is a small but real leak over time for abandoned traces.

Proposed fix: guard Stop() with sync.Once and clear deferred spans for evicted traces
 type TraceStore struct {
@@
-	ttl           time.Duration
-	cleanupTicker *time.Ticker
-	stopCleanup   chan struct{}
-	cleanupWg     sync.WaitGroup
+	ttl           time.Duration
+	cleanupTicker *time.Ticker
+	stopCleanup   chan struct{}
+	cleanupWg     sync.WaitGroup
+	stopOnce      sync.Once
 }
@@
 func (s *TraceStore) cleanupOldTraces() {
 	cutoff := time.Now().Add(-s.ttl)
 	count := 0
 
-	s.traces.Range(func(key, value any) bool {
-		trace := value.(*schemas.Trace)
-		if trace.StartTime.Before(cutoff) {
-			if deleted, ok := s.traces.LoadAndDelete(key); ok {
-				s.ReleaseTrace(deleted.(*schemas.Trace))
-				count++
-			}
-		}
-		return true
-	})
+	s.traces.Range(func(key, value any) bool {
+		traceID := key.(string)
+		trace := value.(*schemas.Trace)
+		if trace.StartTime.Before(cutoff) {
+			if deleted, ok := s.traces.LoadAndDelete(key); ok {
+				s.ReleaseTrace(deleted.(*schemas.Trace))
+				// also clear any deferred span for this trace
+				s.deferredSpans.Delete(traceID)
+				count++
+			}
+		}
+		return true
+	})
@@
 func (s *TraceStore) Stop() {
-	if s.cleanupTicker != nil {
-		s.cleanupTicker.Stop()
-	}
-	close(s.stopCleanup)
-	s.cleanupWg.Wait()
+	s.stopOnce.Do(func() {
+		if s.cleanupTicker != nil {
+			s.cleanupTicker.Stop()
+		}
+		close(s.stopCleanup)
+		s.cleanupWg.Wait()
+	})
 }
plugins/governance/main.go (1)

158-181: Tighten virtual key handling and error messages in HTTP transport middleware

  1. Missing prefix validation for x-bf-vk (duplicate of earlier review)
    parseVirtualKey returns the x-bf-vk header as-is:

    vkHeader := ctx.Request.Header.Peek("x-bf-vk")
    if string(vkHeader) != "" {
        return bifrost.Ptr(string(vkHeader))
    }

    but both Authorization: Bearer ... and x-api-key paths enforce VirtualKeyPrefix. This inconsistency allows arbitrary values via x-bf-vk that would be rejected on other headers.

  2. Misleading log messages for JSON (un)marshaling (duplicate of earlier review)
    In HTTPTransportMiddleware, you log:

    err = sonic.Unmarshal(ctx.Request.Body(), &payload)
    if err != nil {
        p.logger.Error("failed to marshal request body to check for virtual key: %v", err)
    }
    ...
    body, err := sonic.Marshal(payload)
    if err != nil {
        p.logger.Error("failed to marshal request body to check for virtual key: %v", err)
    }

    The first message should say “unmarshal”, and both messages should describe what the code is actually doing (parsing request JSON / rebuilding body after load-balancing), not “check for virtual key”.

  3. Body presence check is fragile
    ctx.Request.Body() returns a []byte; comparing it to nil might still treat an empty body as “present” on some paths. That leads to unnecessary JSON parsing attempts and noisy error logs for routes that legitimately have no body.

    Safer is to check len(body) == 0 and skip parsing in that case.

Proposed fixes
 func parseVirtualKey(ctx *fasthttp.RequestCtx) *string {
 	var virtualKeyValue string
 	vkHeader := ctx.Request.Header.Peek("x-bf-vk")
-	if string(vkHeader) != "" {
-		return bifrost.Ptr(string(vkHeader))
-	}
+	if vk := string(vkHeader); vk != "" && strings.HasPrefix(strings.ToLower(vk), VirtualKeyPrefix) {
+		return bifrost.Ptr(vk)
+	}
@@
 func (p *GovernancePlugin) HTTPTransportMiddleware() schemas.BifrostHTTPMiddleware {
 	return func(next fasthttp.RequestHandler) fasthttp.RequestHandler {
 		return func(ctx *fasthttp.RequestCtx) {
@@
-			if ctx.Request.Body() == nil {
+			bodyBytes := ctx.Request.Body()
+			if len(bodyBytes) == 0 {
 				next(ctx)
 				return
 			}
 			var payload map[string]any
-			err = sonic.Unmarshal(ctx.Request.Body(), &payload)
+			err = sonic.Unmarshal(bodyBytes, &payload)
 			if err != nil {
-				p.logger.Error("failed to marshal request body to check for virtual key: %v", err)
+				p.logger.Error("failed to unmarshal request body for governance HTTP middleware: %v", err)
 				next(ctx)
 				return
 			}
@@
 			payload, err = p.loadBalanceProvider(payload, virtualKey)
 			if err != nil {
-				p.logger.Error("failed to load balance provider: %v", err)
+				p.logger.Error("failed to load balance provider in governance HTTP middleware: %v", err)
 				next(ctx)
 				return
 			}
 			body, err := sonic.Marshal(payload)
 			if err != nil {
-				p.logger.Error("failed to marshal request body to check for virtual key: %v", err)
+				p.logger.Error("failed to marshal updated request body in governance HTTP middleware: %v", err)
 				next(ctx)
 				return
 			}

Also applies to: 183-234

core/bifrost.go (1)

70-75: SetTracer is unsafe for concurrent use and allows nil (risking panics)

Bifrost.tracer is read from hot paths (workers, fallback spans, plugin pipeline) without synchronization, while SetTracer mutates it directly and is public. In addition:

  • SetTracer allows tracer to be set to nil, but all call sites assume a non-nil tracer (bifrost.tracer.StartSpan(...) etc.), so a runtime call to SetTracer(nil) would lead to panics.

Given this is a global cross-cutting dependency:

  • Either treat SetTracer as init-only (document it clearly and possibly guard with a once/“started” flag), or
  • Make tracer concurrency-safe (e.g., atomic.Value with Load/Store) and normalize nil to schemas.DefaultTracer() so it’s never nil at use sites.

This repeats an earlier concern and is still present in the current diff.

Also applies to: 246-250

framework/tracing/propagation.go (1)

24-36: ExtractParentID should return ParentID, not TraceID

The function is documented to extract the parent ID from the traceparent header but currently returns ctx.TraceID. Per W3C format, the parent/span ID is the third field and should map to ctx.ParentID.

Proposed fix
 func ExtractParentID(header *fasthttp.RequestHeader) string {
@@
 	ctx := ParseTraceparent(traceParent)
 	if ctx == nil {
 		return ""
 	}
-	return ctx.TraceID
+	return ctx.ParentID
 }
core/schemas/trace.go (1)

40-49: Trace.Reset, Span.End, and Span.Reset mutate state without locking

Trace.AddSpan / Trace.GetSpan and Span.SetAttribute / Span.AddEvent are mutex-protected, but:

  • Trace.Reset clears fields and slices with no lock.
  • Span.End sets EndTime, Status, StatusMsg without locking.
  • Span.Reset clears all fields and the Events slice without locking.

Given these types are shared through TraceStore and may be read/updated concurrently, calling these methods while another goroutine accesses the same trace/span can produce data races.

Either:

  • Guard these methods with the same mutex (mu.Lock / defer mu.Unlock()), or
  • Very clearly document and enforce that they may only be called when the trace/span is no longer reachable by any other goroutine (e.g., after removal from maps and before returning to pools).

Example for Span.End:

func (s *Span) End(status SpanStatus, statusMsg string) {
-    s.EndTime = time.Now()
-    s.Status = status
-    s.StatusMsg = statusMsg
+    s.mu.Lock()
+    defer s.mu.Unlock()
+    s.EndTime = time.Now()
+    s.Status = status
+    s.StatusMsg = statusMsg
}

Same pattern applies to Trace.Reset and Span.Reset.

Also applies to: 84-104

framework/tracing/tracer.go (1)

18-21: Comments still refer to “StoreTracer” instead of Tracer

There are several references to “StoreTracer” in comments:

  • Constructor comment: // NewTracer creates a new StoreTracer...
  • Type comment for spanHandle.
  • Compile-time assertion comment: // Ensure StoreTracer implements schemas.Tracer...

The actual exported type is Tracer, so these comments should be updated for consistency and to avoid confusion.

Example fixes:

-// NewTracer creates a new StoreTracer wrapping the given TraceStore.
+// NewTracer creates a new Tracer wrapping the given TraceStore.
@@
-// spanHandle is the concrete implementation of schemas.SpanHandle for StoreTracer.
+// spanHandle is the concrete implementation of schemas.SpanHandle for Tracer.
@@
-// Ensure StoreTracer implements schemas.Tracer at compile time
+// Ensure Tracer implements schemas.Tracer at compile time

Also applies to: 46-51, 447-448

framework/tracing/llmspan.go (1)

190-193: ExtraParams collision issue remains unaddressed.

User-provided keys from ExtraParams are written directly to the attributes map without namespacing, which can overwrite standard attributes (e.g., if a user sets a key like "gen_ai.request.model").

This issue was previously flagged and affects multiple locations in this file (lines 190-193, 298-300, 365-367, 575-577, 708-710, 738-740, 753-755, 768-770, 783-785, 958-960, 982-984, 997-999, 1012-1014, 1027-1029).

🔎 Suggested approach: namespace ExtraParams consistently
 	// ExtraParams
 	for k, v := range req.Params.ExtraParams {
-		attrs[k] = fmt.Sprintf("%v", v)
+		attrs["gen_ai.extra."+k] = fmt.Sprintf("%v", v)
 	}

Apply this pattern consistently across all 14 occurrences in the file.

🧹 Nitpick comments (6)
core/providers/utils/utils.go (1)

880-925: Deferred LLM span completion for streaming looks correct, but check error/ordering edge cases.

The new tracing hooks in ProcessAndSendResponse/ProcessAndSendBifrostError plus completeDeferredSpan are defensively implemented and should safely no-op when tracing isn’t configured. Chunk accumulation + GetAccumulatedChunks gives you a solid basis to populate LLM response attributes, TTFT, and total chunks before ending the deferred span.

Two edge cases are worth double‑checking:

  1. Ordering relative to post hooks (final chunk).
    completeDeferredSpan is called before postHookRunner on the final chunk, while it uses BifrostContextKeyPostHookSpanFinalizer to synthesize aggregated post‑hook spans. If that finalizer is intended to account for post‑hook execution on the last chunk as well, you may want to move the completeDeferredSpan call after postHookRunner for the final chunk (or adjust the finalizer’s expectations).

  2. Scanner / transport errors via ProcessAndSendError.
    Only ProcessAndSendResponse and ProcessAndSendBifrostError trigger completeDeferredSpan when BifrostContextKeyStreamEndIndicator is true. For stream failures that go through ProcessAndSendError (e.g., low‑level read/parse errors), there currently isn’t a path here to close the deferred span, which could leave traces open until some external timeout.

Also applies to: 932-969, 1412-1501

core/schemas/plugin.go (1)

4-8: Plugin API evolution is coherent, but docs still mention TransportInterceptor.

Adding BifrostHTTPMiddleware and switching the Plugin interface to HTTPTransportMiddleware() BifrostHTTPMiddleware, plus the new ObservabilityPlugin interface, aligns with the rest of the middleware and tracing changes.

One minor doc mismatch remains: the “Execution order” comment block still refers to TransportInterceptor as step 1. It should be updated to describe HTTPTransportMiddleware as the first stage in the pipeline to avoid confusing plugin authors migrating to the new API.

Also applies to: 36-39, 71-80, 109-137

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

1231-1246: Consider managing the tracing store’s lifecycle on server shutdown

The Bootstrap logic:

  • Prepends handlers.TransportInterceptorMiddleware to inferenceMiddlewares.
  • When any ObservabilityPlugin is present, allocates a TraceStore with a 60-minute TTL, builds a tracing.Tracer, calls s.Client.SetTracer(tracer), and prepends a tracing middleware.

This is a good integration of HTTP middleware, client tracing, and observability plugins. One improvement would be to keep a reference to the TraceStore (or a closer on the tracer) on BifrostHTTPServer and stop it during shutdown, so the cleanup goroutine and its ticker are explicitly torn down rather than relying on process exit.

plugins/otel/converter.go (1)

49-70: Consider stricter validation in hexToBytes for trace/span IDs

hexToBytes strips non-hex chars, pads/truncates, and ignores hex.DecodeString errors. For trace IDs/span IDs this can silently turn malformed inputs into “valid” OTEL IDs, which hurts debuggability and cross-system correlation.

If feasible, consider:

  • Failing fast (returning nil and skipping span export) when DecodeString errors, or
  • Validating length/charset at call sites (e.g., before convertSpanToOTELSpan) and logging when IDs are bad.

This is an optional hardening improvement; current behavior is functional but very permissive.

framework/tracing/helpers.go (1)

22-82: Defensively guard against nil *TraceStore in helpers

GetTrace, AddSpan, AddChildSpan, EndSpan, SetSpanAttribute, and AddSpanEvent all dereference store without checking for nil. If callers ever pass a nil *TraceStore (e.g., during partial initialization or tests), these will panic.

Consider early returns like:

if store == nil {
    return // or nil for the getters
}

in these helpers to make them safer to use across the codebase.

framework/tracing/llmspan.go (1)

280-280: Consider preserving structured data instead of string conversion.

Maps and structured data are being converted to string representations using fmt.Sprintf("%v", ...) at lines 280 (LogitBias), 613 (Metadata), and 705 (Metadata).

This flattens the structure and reduces queryability in tracing backends that support nested attributes. Consider using structured attribute values if the tracing backend supports them, or use JSON serialization for better structure preservation.

💡 Alternative serialization approach

For better structure preservation, consider JSON serialization:

-		attrs[schemas.AttrLogitBias] = fmt.Sprintf("%v", req.Params.LogitBias)
+		if data, err := json.Marshal(req.Params.LogitBias); err == nil {
+			attrs[schemas.AttrLogitBias] = string(data)
+		}

Note: This requires importing encoding/json.

Also applies to: 613-613, 705-705

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b8f95ce and fb945b9.

⛔ Files ignored due to path filters (2)
  • core/go.sum is excluded by !**/*.sum
  • examples/plugins/hello-world/go.sum is excluded by !**/*.sum
📒 Files selected for processing (65)
  • core/bifrost.go
  • core/bifrost_test.go
  • core/go.mod
  • core/mcp.go
  • core/providers/utils/utils.go
  • core/schemas/bifrost.go
  • core/schemas/context.go
  • core/schemas/plugin.go
  • core/schemas/trace.go
  • core/schemas/tracer.go
  • docs/docs.json
  • docs/plugins/getting-started.mdx
  • docs/plugins/migration-guide.mdx
  • docs/plugins/writing-plugin.mdx
  • examples/plugins/hello-world/go.mod
  • examples/plugins/hello-world/main.go
  • framework/configstore/tables/mcp.go
  • framework/plugins/dynamicplugin.go
  • framework/plugins/dynamicplugin_test.go
  • framework/streaming/accumulator.go
  • framework/streaming/audio.go
  • framework/streaming/chat.go
  • framework/streaming/responses.go
  • framework/streaming/transcription.go
  • framework/streaming/types.go
  • framework/tracing/helpers.go
  • framework/tracing/llmspan.go
  • framework/tracing/propagation.go
  • framework/tracing/store.go
  • framework/tracing/tracer.go
  • plugins/governance/go.mod
  • plugins/governance/main.go
  • plugins/jsonparser/main.go
  • plugins/logging/main.go
  • plugins/maxim/main.go
  • plugins/mocker/main.go
  • plugins/otel/converter.go
  • plugins/otel/main.go
  • plugins/otel/ttlsyncmap.go
  • plugins/semanticcache/main.go
  • plugins/telemetry/main.go
  • transports/bifrost-http/handlers/cache.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/handlers/health.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/handlers/logging.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/handlers/mcpServer.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • transports/bifrost-http/handlers/plugins.go
  • transports/bifrost-http/handlers/providers.go
  • transports/bifrost-http/handlers/session.go
  • transports/bifrost-http/handlers/ui.go
  • transports/bifrost-http/handlers/websocket.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/lib/middleware.go
  • transports/bifrost-http/server/server.go
  • transports/go.mod
  • ui/app/workspace/logs/views/logDetailsSheet.tsx
  • ui/components/sidebar.tsx
  • ui/components/ui/sheet.tsx
  • ui/components/ui/switch.tsx
💤 Files with no reviewable changes (2)
  • framework/configstore/tables/mcp.go
  • plugins/otel/ttlsyncmap.go
🚧 Files skipped from review as they are similar to previous changes (22)
  • transports/bifrost-http/handlers/websocket.go
  • transports/go.mod
  • transports/bifrost-http/handlers/plugins.go
  • docs/plugins/getting-started.mdx
  • transports/bifrost-http/handlers/health.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • core/bifrost_test.go
  • plugins/maxim/main.go
  • transports/bifrost-http/handlers/cache.go
  • plugins/mocker/main.go
  • transports/bifrost-http/handlers/providers.go
  • ui/app/workspace/logs/views/logDetailsSheet.tsx
  • plugins/governance/go.mod
  • framework/plugins/dynamicplugin_test.go
  • ui/components/sidebar.tsx
  • core/mcp.go
  • framework/plugins/dynamicplugin.go
  • ui/components/ui/sheet.tsx
  • transports/bifrost-http/handlers/config.go
  • examples/plugins/hello-world/go.mod
  • plugins/logging/main.go
  • docs/plugins/migration-guide.mdx
🧰 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:

  • framework/streaming/types.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/handlers/logging.go
  • framework/streaming/responses.go
  • core/schemas/plugin.go
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/handlers/ui.go
  • framework/streaming/transcription.go
  • docs/docs.json
  • transports/bifrost-http/server/server.go
  • framework/tracing/store.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/lib/middleware.go
  • plugins/telemetry/main.go
  • ui/components/ui/switch.tsx
  • docs/plugins/writing-plugin.mdx
  • plugins/semanticcache/main.go
  • plugins/otel/converter.go
  • plugins/governance/main.go
  • framework/streaming/chat.go
  • core/schemas/context.go
  • framework/streaming/accumulator.go
  • core/providers/utils/utils.go
  • framework/tracing/propagation.go
  • plugins/otel/main.go
  • core/schemas/bifrost.go
  • core/go.mod
  • core/schemas/trace.go
  • transports/bifrost-http/handlers/mcpServer.go
  • framework/streaming/audio.go
  • transports/bifrost-http/handlers/integrations.go
  • examples/plugins/hello-world/main.go
  • core/schemas/tracer.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/handlers/session.go
  • plugins/jsonparser/main.go
  • core/bifrost.go
  • framework/tracing/helpers.go
  • framework/tracing/llmspan.go
  • framework/tracing/tracer.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:

  • framework/streaming/types.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/handlers/logging.go
  • framework/streaming/responses.go
  • core/schemas/plugin.go
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/handlers/ui.go
  • framework/streaming/transcription.go
  • transports/bifrost-http/server/server.go
  • framework/tracing/store.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/lib/middleware.go
  • plugins/telemetry/main.go
  • plugins/semanticcache/main.go
  • plugins/otel/converter.go
  • plugins/governance/main.go
  • framework/streaming/chat.go
  • core/schemas/context.go
  • framework/streaming/accumulator.go
  • core/providers/utils/utils.go
  • framework/tracing/propagation.go
  • plugins/otel/main.go
  • core/schemas/bifrost.go
  • core/schemas/trace.go
  • transports/bifrost-http/handlers/mcpServer.go
  • framework/streaming/audio.go
  • transports/bifrost-http/handlers/integrations.go
  • examples/plugins/hello-world/main.go
  • core/schemas/tracer.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/handlers/session.go
  • plugins/jsonparser/main.go
  • core/bifrost.go
  • framework/tracing/helpers.go
  • framework/tracing/llmspan.go
  • framework/tracing/tracer.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/mcp.go
  • transports/bifrost-http/handlers/logging.go
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/handlers/ui.go
  • transports/bifrost-http/server/server.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/lib/middleware.go
  • transports/bifrost-http/handlers/mcpServer.go
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/handlers/session.go
📚 Learning: 2025-12-22T10:50:40.990Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1154
File: plugins/governance/store.go:1165-1186
Timestamp: 2025-12-22T10:50:40.990Z
Learning: In the Bifrost governance plugin, budgets and rate limits have 1:1 relationships with their parent entities (virtual keys, teams, customers). Do not assume sharing; ensure cascade deletion logic only deletes budgets/rate limits when there are no shared references. Enforce invariants in code and add tests to verify no cross-entity sharing and that cascade deletes only remove the specific child of the parent. If a counterexample arises, adjust data model or add guards.

Applied to files:

  • plugins/governance/main.go
📚 Learning: 2025-12-19T09:26:54.961Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/providers/utils/utils.go:1050-1051
Timestamp: 2025-12-19T09:26:54.961Z
Learning: Update streaming end-marker handling so HuggingFace is treated as a non-[DONE] provider for backends that do not emit a DONE marker (e.g., meta llama on novita). In core/providers/utils/utils.go, adjust ProviderSendsDoneMarker() (or related logic) to detect providers that may not emit DONE and avoid relying on DONE as the sole end signal. Add tests to cover both DONE-emitting and non-DONE backends, with clear documentation in code comments explaining the rationale and any fallback behavior.

Applied to files:

  • core/providers/utils/utils.go
🧬 Code graph analysis (32)
transports/bifrost-http/handlers/mcp.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/logging.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
framework/streaming/responses.go (1)
framework/streaming/types.go (1)
  • AccumulatedData (28-48)
core/schemas/plugin.go (2)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-26)
core/schemas/trace.go (1)
  • Trace (10-19)
transports/bifrost-http/handlers/governance.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/ui.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
framework/streaming/transcription.go (1)
framework/streaming/types.go (1)
  • AccumulatedData (28-48)
transports/bifrost-http/server/server.go (4)
core/schemas/plugin.go (2)
  • BifrostHTTPMiddleware (38-38)
  • ObservabilityPlugin (123-137)
transports/bifrost-http/handlers/middlewares.go (2)
  • TransportInterceptorMiddleware (50-70)
  • NewTracingMiddleware (207-214)
framework/tracing/store.go (1)
  • NewTraceStore (38-65)
framework/tracing/tracer.go (1)
  • NewTracer (19-21)
framework/tracing/store.go (5)
core/schemas/tracer.go (1)
  • Tracer (13-73)
framework/tracing/tracer.go (1)
  • Tracer (14-16)
core/schemas/bifrost.go (1)
  • BifrostResponse (335-355)
core/schemas/trace.go (6)
  • Trace (10-19)
  • Span (52-65)
  • SpanEvent (107-111)
  • SpanKind (115-115)
  • SpanStatusUnset (147-147)
  • SpanStatus (143-143)
framework/tracing/helpers.go (3)
  • GetTrace (23-29)
  • AddSpan (32-38)
  • EndSpan (50-56)
transports/bifrost-http/handlers/middlewares.go (6)
core/schemas/plugin.go (2)
  • BifrostHTTPMiddleware (38-38)
  • Plugin (71-97)
transports/bifrost-http/lib/middleware.go (1)
  • ChainMiddlewares (11-23)
core/schemas/tracer.go (1)
  • Tracer (13-73)
framework/tracing/propagation.go (1)
  • ExtractParentID (26-36)
core/schemas/bifrost.go (4)
  • BifrostContextKeyTraceID (143-143)
  • BifrostContextKeyTraceCompleter (148-148)
  • BifrostContextKeySpanID (144-144)
  • BifrostContextKeyDeferTraceCompletion (147-147)
core/schemas/trace.go (3)
  • SpanKindHTTPRequest (131-131)
  • SpanStatusError (151-151)
  • SpanStatusOk (149-149)
transports/bifrost-http/lib/middleware.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
plugins/telemetry/main.go (2)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-26)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
plugins/semanticcache/main.go (2)
core/schemas/plugin.go (2)
  • Plugin (71-97)
  • BifrostHTTPMiddleware (38-38)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-26)
plugins/otel/converter.go (2)
plugins/otel/main.go (1)
  • OtelPlugin (58-75)
core/schemas/trace.go (17)
  • Trace (10-19)
  • Span (52-65)
  • SpanKind (115-115)
  • SpanKindLLMCall (121-121)
  • SpanKindHTTPRequest (131-131)
  • SpanKindPlugin (123-123)
  • SpanKindInternal (139-139)
  • SpanKindRetry (127-127)
  • SpanKindFallback (129-129)
  • SpanKindMCPTool (125-125)
  • SpanKindEmbedding (133-133)
  • SpanKindSpeech (135-135)
  • SpanKindTranscription (137-137)
  • SpanStatus (143-143)
  • SpanStatusOk (149-149)
  • SpanStatusError (151-151)
  • SpanEvent (107-111)
plugins/governance/main.go (3)
core/utils.go (1)
  • Ptr (56-58)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-26)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
framework/streaming/chat.go (1)
framework/streaming/types.go (1)
  • AccumulatedData (28-48)
core/schemas/context.go (1)
core/schemas/bifrost.go (1)
  • BifrostContextKeyDeferTraceCompletion (147-147)
plugins/otel/main.go (3)
core/schemas/plugin.go (3)
  • BifrostHTTPMiddleware (38-38)
  • PluginShortCircuit (12-16)
  • ObservabilityPlugin (123-137)
core/schemas/context.go (1)
  • BifrostContext (32-42)
core/schemas/trace.go (1)
  • Trace (10-19)
core/schemas/bifrost.go (2)
core/schemas/tracer.go (1)
  • Tracer (13-73)
framework/tracing/tracer.go (1)
  • Tracer (14-16)
core/schemas/trace.go (2)
framework/tracing/helpers.go (1)
  • AddSpan (32-38)
ui/lib/constants/logs.ts (1)
  • Status (163-163)
transports/bifrost-http/handlers/mcpServer.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
framework/streaming/audio.go (1)
framework/streaming/types.go (1)
  • AccumulatedData (28-48)
examples/plugins/hello-world/main.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
core/schemas/tracer.go (4)
core/schemas/trace.go (3)
  • Trace (10-19)
  • SpanKind (115-115)
  • SpanStatus (143-143)
framework/tracing/helpers.go (1)
  • EndSpan (50-56)
core/schemas/bifrost.go (3)
  • BifrostRequest (173-193)
  • BifrostResponse (335-355)
  • BifrostError (474-483)
framework/tracing/tracer.go (1)
  • Tracer (14-16)
transports/bifrost-http/integrations/router.go (3)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
core/schemas/bifrost.go (9)
  • ListModelsRequest (92-92)
  • TextCompletionRequest (93-93)
  • ChatCompletionRequest (95-95)
  • ResponsesRequest (97-97)
  • EmbeddingRequest (99-99)
  • SpeechRequest (100-100)
  • TranscriptionRequest (102-102)
  • BifrostContextKeyDeferTraceCompletion (147-147)
  • BifrostContextKeyTraceCompleter (148-148)
transports/bifrost-http/handlers/inference.go (5)
  • ChatRequest (172-176)
  • ResponsesRequest (266-270)
  • EmbeddingRequest (273-277)
  • SpeechRequest (279-283)
  • TranscriptionRequest (285-289)
transports/bifrost-http/handlers/inference.go (2)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
core/schemas/bifrost.go (2)
  • BifrostContextKeyDeferTraceCompletion (147-147)
  • BifrostContextKeyTraceCompleter (148-148)
transports/bifrost-http/handlers/session.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
plugins/jsonparser/main.go (2)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-26)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
core/bifrost.go (3)
core/schemas/tracer.go (3)
  • Tracer (13-73)
  • DefaultTracer (124-126)
  • SpanHandle (8-8)
core/schemas/bifrost.go (5)
  • BifrostRequest (173-193)
  • BifrostContextKeyStreamStartTime (145-145)
  • BifrostContextKeyTracer (146-146)
  • BifrostResponse (335-355)
  • BifrostError (474-483)
framework/tracing/helpers.go (1)
  • EndSpan (50-56)
framework/tracing/helpers.go (3)
core/schemas/bifrost.go (1)
  • BifrostContextKeyTraceID (143-143)
framework/tracing/store.go (1)
  • TraceStore (24-35)
core/schemas/trace.go (5)
  • Trace (10-19)
  • SpanKind (115-115)
  • Span (52-65)
  • SpanStatus (143-143)
  • SpanEvent (107-111)
framework/tracing/llmspan.go (5)
core/schemas/bifrost.go (18)
  • BifrostRequest (173-193)
  • RequestType (89-89)
  • TextCompletionRequest (93-93)
  • EmbeddingRequest (99-99)
  • TranscriptionRequest (102-102)
  • SpeechRequest (100-100)
  • ResponsesRequest (97-97)
  • BatchCreateRequest (104-104)
  • BatchListRequest (105-105)
  • BatchRetrieveRequest (106-106)
  • BatchCancelRequest (107-107)
  • BatchResultsRequest (108-108)
  • FileUploadRequest (109-109)
  • FileListRequest (110-110)
  • FileRetrieveRequest (111-111)
  • FileDeleteRequest (112-112)
  • FileContentRequest (113-113)
  • BifrostResponse (335-355)
core/schemas/trace.go (100)
  • AttrProviderName (159-159)
  • AttrRequestModel (160-160)
  • AttrError (216-216)
  • AttrErrorType (217-217)
  • AttrErrorCode (218-218)
  • AttrVirtualKeyID (228-228)
  • AttrVirtualKeyName (229-229)
  • AttrSelectedKeyID (230-230)
  • AttrSelectedKeyName (231-231)
  • AttrTeamID (232-232)
  • AttrTeamName (233-233)
  • AttrCustomerID (234-234)
  • AttrCustomerName (235-235)
  • AttrNumberOfRetries (236-236)
  • AttrFallbackIndex (237-237)
  • AttrMaxTokens (163-163)
  • AttrTemperature (164-164)
  • AttrTopP (165-165)
  • AttrStopSequences (166-166)
  • AttrPresencePenalty (167-167)
  • AttrFrequencyPenalty (168-168)
  • AttrParallelToolCall (169-169)
  • AttrRequestUser (170-170)
  • AttrMessageCount (188-188)
  • AttrInputMessages (222-222)
  • AttrResponseID (191-191)
  • AttrResponseModel (192-192)
  • AttrObject (197-197)
  • AttrSystemFprint (194-194)
  • AttrCreated (196-196)
  • AttrServiceTier (195-195)
  • AttrOutputMessages (225-225)
  • AttrFinishReason (193-193)
  • AttrTotalTokens (210-210)
  • AttrN (175-175)
  • AttrInputText (221-221)
  • AttrDimensions (178-178)
  • AttrEncodingFormat (179-179)
  • AttrInputEmbedding (224-224)
  • AttrLanguage (180-180)
  • AttrPrompt (181-181)
  • AttrResponseFormat (182-182)
  • AttrFormat (183-183)
  • AttrInputTokens (211-211)
  • AttrOutputTokens (212-212)
  • AttrInputSpeech (223-223)
  • AttrRespInclude (255-255)
  • AttrRespMaxOutputTokens (256-256)
  • AttrRespMaxToolCalls (257-257)
  • AttrRespMetadata (258-258)
  • AttrRespPreviousRespID (259-259)
  • AttrRespPromptCacheKey (260-260)
  • AttrRespReasoningText (261-261)
  • AttrRespReasoningEffort (262-262)
  • AttrRespReasoningGenSum (263-263)
  • AttrRespSafetyIdentifier (264-264)
  • AttrRespStore (265-265)
  • AttrRespTemperature (266-266)
  • AttrRespTextVerbosity (267-267)
  • AttrRespTextFormatType (268-268)
  • AttrRespTopLogProbs (269-269)
  • AttrRespTopP (270-270)
  • AttrRespToolChoiceType (271-271)
  • AttrRespToolChoiceName (272-272)
  • AttrRespTruncation (273-273)
  • AttrRespTools (274-274)
  • AttrBatchInputFileID (281-281)
  • AttrBatchEndpoint (280-280)
  • AttrBatchCompletionWin (284-284)
  • AttrBatchRequestsCount (287-287)
  • AttrBatchMetadata (291-291)
  • AttrBatchLimit (292-292)
  • AttrBatchAfter (293-293)
  • AttrBatchBeforeID (294-294)
  • AttrBatchAfterID (295-295)
  • AttrBatchPageToken (296-296)
  • AttrBatchPageSize (297-297)
  • AttrBatchID (277-277)
  • AttrBatchStatus (278-278)
  • AttrBatchObject (279-279)
  • AttrBatchCreatedAt (285-285)
  • AttrBatchExpiresAt (286-286)
  • AttrBatchOutputFileID (282-282)
  • AttrBatchErrorFileID (283-283)
  • AttrBatchCountTotal (298-298)
  • AttrBatchCountCompleted (299-299)
  • AttrBatchCountFailed (300-300)
  • AttrBatchDataCount (288-288)
  • AttrBatchHasMore (290-290)
  • AttrBatchFirstID (301-301)
  • AttrBatchLastID (302-302)
  • AttrBatchInProgressAt (303-303)
  • AttrBatchFinalizingAt (304-304)
  • AttrBatchCompletedAt (305-305)
  • AttrBatchFailedAt (306-306)
  • AttrBatchExpiredAt (307-307)
  • AttrBatchCancellingAt (308-308)
  • AttrBatchCancelledAt (309-309)
  • AttrBatchNextCursor (310-310)
  • AttrUsageCost (213-213)
core/schemas/chatcompletions.go (1)
  • BifrostChatRequest (12-19)
core/schemas/embedding.go (1)
  • BifrostEmbeddingRequest (9-16)
core/schemas/responses.go (1)
  • BifrostResponsesRequest (32-39)
framework/tracing/tracer.go (5)
framework/tracing/store.go (1)
  • TraceStore (24-35)
core/schemas/tracer.go (2)
  • Tracer (13-73)
  • SpanHandle (8-8)
core/schemas/trace.go (5)
  • Trace (10-19)
  • SpanKind (115-115)
  • Span (52-65)
  • SpanStatus (143-143)
  • SpanEvent (107-111)
framework/tracing/helpers.go (3)
  • GetTraceID (11-20)
  • EndSpan (50-56)
  • GetTrace (23-29)
core/schemas/bifrost.go (4)
  • BifrostContextKeySpanID (144-144)
  • BifrostRequest (173-193)
  • BifrostResponse (335-355)
  • BifrostError (474-483)

Comment thread ui/components/ui/switch.tsx
@akshaydeo akshaydeo force-pushed the 11-19-plugins_v2_architecture branch from fb945b9 to 1cecccb Compare December 24, 2025 07:24
Copy link
Copy Markdown
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

♻️ Duplicate comments (12)
ui/components/ui/switch.tsx (1)

13-13: Note: Size default change already flagged in previous review.

The change from size = "default" (implicit previous default) to size = "md" has already been identified as a breaking change in the past review comment. Existing Switch components without an explicit size prop will render smaller (h-6 w-11 → h-5 w-9).

examples/plugins/hello-world/main.go (1)

19-26: HTTPTransportMiddleware implementation looks correct but context value propagation is broken.

The middleware correctly implements the schemas.BifrostHTTPMiddleware signature with proper delegation to next(ctx). However, PreHook (line 29) and PostHook (lines 38-39) still read "hello-world-plugin-transport-interceptor" from context, which this middleware never sets—those reads will always return nil.

Either set the context value in the middleware to demonstrate data flow, or remove the stale context reads from the hooks.

plugins/governance/main.go (2)

30-36: Validate x-bf-vk header consistently with other virtual key sources

parseVirtualKey returns the raw x-bf-vk header without checking it starts with VirtualKeyPrefix ("sk-bf-"), while both Authorization: Bearer and x-api-key paths enforce that prefix. This inconsistency can allow malformed or unintended tokens via x-bf-vk to bypass the prefix check.

Align x-bf-vk handling with the other branches, e.g.:

Proposed adjustment
 func parseVirtualKey(ctx *fasthttp.RequestCtx) *string {
-	var virtualKeyValue string
-	vkHeader := ctx.Request.Header.Peek("x-bf-vk")
-	if string(vkHeader) != "" {
-		return bifrost.Ptr(string(vkHeader))
-	}
+	var virtualKeyValue string
+
+	vkHeader := ctx.Request.Header.Peek("x-bf-vk")
+	if len(vkHeader) > 0 {
+		vkValue := string(vkHeader)
+		if strings.HasPrefix(strings.ToLower(vkValue), VirtualKeyPrefix) {
+			return bifrost.Ptr(vkValue)
+		}
+	}

This keeps all header sources enforcing the same VK prefix semantics.

Also applies to: 158-181


183-231: Fix HTTP middleware log message and empty-body handling

Two small correctness/UX issues in HTTPTransportMiddleware:

  • Line 214 logs "failed to marshal" while calling sonic.Unmarshal. This should say "unmarshal" to reflect the actual operation.
  • The empty-body check only guards on ctx.Request.Body() == nil. In fasthttp, an empty body is often a zero-length slice, not nil, so you’ll still try to unmarshal and log an error for valid but empty requests.

Consider:

Proposed tweak
-	if ctx.Request.Body() == nil {
+	if body := ctx.Request.Body(); body == nil || len(body) == 0 {
 		next(ctx)
 		return
 	}
 	var payload map[string]any
-	err = sonic.Unmarshal(ctx.Request.Body(), &payload)
+	err = sonic.Unmarshal(ctx.Request.Body(), &payload)
 	if err != nil {
-		p.logger.Error("failed to marshal request body to check for virtual key: %v", err)
+		p.logger.Error("failed to unmarshal request body to check for virtual key: %v", err)
 		next(ctx)
 		return
 	}
core/bifrost.go (1)

55-75: Clarify SetTracer’s concurrency model or make it race-free

Bifrost.tracer is read on hot paths (handleRequest, handleStreamRequest, requestWorker, executeRequestWithRetries, plugin pipeline) without synchronization, while SetTracer mutates it directly:

type Bifrost struct {
    ...
    tracer schemas.Tracer
}

func (bifrost *Bifrost) SetTracer(tracer schemas.Tracer) {
    bifrost.tracer = tracer
}

In the current server flow SetTracer is only called during bootstrap (before requests start), which is safe. But as a public method it invites runtime reconfiguration, which would introduce a data race.

Consider either:

  • Treating SetTracer as init-only: document it as such and avoid calling it after workers start, or
  • Making it concurrency-safe (e.g., store the tracer in an atomic.Value / atomic.Pointer or a small RW-mutex-protected field, and load via that everywhere).
In Go, is writing to a struct field from one goroutine while other goroutines read it without synchronization considered a data race?

Also applies to: 120-139, 247-250

framework/tracing/store.go (1)

23-35: Guard TraceStore.Stop() against multiple invocations (double close panic)

Stop() unconditionally closes s.stopCleanup. If Stop() is called more than once (e.g., from tests or multiple shutdown paths), closing an already-closed channel will panic. This was previously flagged and the pattern is unchanged.

Consider adding a sync.Once field to TraceStore and wrapping the shutdown logic so it runs only once:

Proposed fix
 type TraceStore struct {
 	traces        sync.Map // map[traceID]*schemas.Trace - thread-safe concurrent access
 	deferredSpans sync.Map // map[traceID]*DeferredSpanInfo - deferred spans for streaming requests
 	tracePool     sync.Pool // Reuse Trace objects to reduce allocations
 	spanPool      sync.Pool // Reuse Span objects to reduce allocations
 	logger        schemas.Logger

 	ttl           time.Duration
 	cleanupTicker *time.Ticker
 	stopCleanup   chan struct{}
 	cleanupWg     sync.WaitGroup
+	stopOnce      sync.Once
 }

 // Stop stops the cleanup goroutine and releases resources
 func (s *TraceStore) Stop() {
-	if s.cleanupTicker != nil {
-		s.cleanupTicker.Stop()
-	}
-	close(s.stopCleanup)
-	s.cleanupWg.Wait()
+	s.stopOnce.Do(func() {
+		if s.cleanupTicker != nil {
+			s.cleanupTicker.Stop()
+		}
+		close(s.stopCleanup)
+		s.cleanupWg.Wait()
+	})
 }
In Go, what happens if you call close() on a channel more than once?

Also applies to: 360-367

framework/tracing/propagation.go (1)

24-36: ExtractParentID should return ParentID, not TraceID

The function is documented as extracting the parent trace/span ID from the traceparent header, but after parsing it returns ctx.TraceID:

ctx := ParseTraceparent(traceParent)
...
return ctx.TraceID

Per the W3C format (version-traceid-parentid-traceflags), the parent/span ID is the third field and should be surfaced via ctx.ParentID. Returning the trace ID here will break proper parent-child linking for incoming traces.

Recommend:

 func ExtractParentID(header *fasthttp.RequestHeader) string {
   traceParent := string(header.Peek(TraceParentHeader))
   if traceParent == "" {
     return ""
   }
   ctx := ParseTraceparent(traceParent)
   if ctx == nil {
     return ""
   }
-  return ctx.TraceID
+  return ctx.ParentID
 }
In the W3C Trace Context `traceparent` header, which field represents the parent/span ID?
framework/tracing/llmspan.go (1)

190-193: ExtraParams keys written directly may cause attribute key collisions.

User-provided keys from ExtraParams are written directly to the attributes map without namespacing. If a user sets a key like "gen_ai.request.model" in ExtraParams, it would overwrite the standard attribute.

This pattern repeats throughout the file (lines 298-300, 365-367, 575-577, 708-710, 738-740, 753-755, 768-770, 783-785, 958-960, 982-984, 997-999, 1012-1014, 1027-1029).

🔎 Suggested approach: namespace ExtraParams
 // ExtraParams
 for k, v := range req.Params.ExtraParams {
-    attrs[k] = fmt.Sprintf("%v", v)
+    attrs["gen_ai.extra."+k] = fmt.Sprintf("%v", v)
 }
core/schemas/trace.go (3)

40-49: Trace.Reset() lacks mutex protection.

This method modifies all fields including Spans without holding the mutex, creating a potential data race if Reset is called while another goroutine is accessing the trace.


84-89: Span.End() lacks mutex protection.

This method modifies EndTime, Status, and StatusMsg without holding the mutex, which can cause data races if called concurrently with reads or writes to these fields.


91-104: Span.Reset() lacks mutex protection.

Similar to Trace.Reset(), this method modifies fields without holding the mutex. If reused from a pool while another goroutine still holds a reference, a data race could occur.

framework/tracing/tracer.go (1)

393-398: Missing sort for deterministic output in buildCompleteResponseFromChunks.

The choiceIndices (line 395-398) and tcIndices (line 434-437) slices are built from map iteration but are not sorted before use. This results in non-deterministic ordering of choices and tool calls in the reconstructed response.

🔎 Proposed fix
+import "sort"

 // Build final choices from accumulated data
 // Sort choice indices for deterministic output
 choiceIndices := make([]int, 0, len(choiceMap))
 for idx := range choiceMap {
     choiceIndices = append(choiceIndices, idx)
 }
+sort.Ints(choiceIndices)

 for _, idx := range choiceIndices {
     ...
         // Sort tool calls by index
         tcIndices := make([]int, 0, len(accum.toolCalls))
         for tcIdx := range accum.toolCalls {
             tcIndices = append(tcIndices, tcIdx)
         }
+        sort.Ints(tcIndices)

Also applies to: 433-437

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

49-70: Plugin middleware collection is correct; consider pre-allocating the slice.

The implementation correctly:

  • Collects HTTPTransportMiddleware() from all plugins
  • Skips nil middlewares
  • Chains them via lib.ChainMiddlewares

For a minor efficiency gain on hot paths, consider pre-allocating the slice:

🔎 Optional optimization
-pluginsMiddlewareChain := []schemas.BifrostHTTPMiddleware{}
+pluginsMiddlewareChain := make([]schemas.BifrostHTTPMiddleware, 0, len(plugins))
framework/streaming/responses.go (1)

742-756: LGTM! TTFT calculation correctly implemented.

The Time to First Token calculation is properly implemented with defensive checks for zero timestamps. The logic correctly computes TTFT in milliseconds and propagates it through AccumulatedData.

Optional: Consider extracting TTFT calculation to a helper function.

The same TTFT calculation logic is duplicated across multiple streaming files (responses.go, chat.go, transcription.go). Consider extracting this into a shared helper function to reduce duplication and improve maintainability.

🔎 Proposed helper function
+// calculateTTFT calculates Time to First Token in milliseconds
+func calculateTTFT(startTimestamp, firstChunkTimestamp time.Time) int64 {
+	if startTimestamp.IsZero() || firstChunkTimestamp.IsZero() {
+		return 0
+	}
+	return firstChunkTimestamp.Sub(startTimestamp).Nanoseconds() / 1e6
+}

 // Calculate Time to First Token (TTFT) in milliseconds
-var ttft int64
-if !accumulator.StartTimestamp.IsZero() && !accumulator.FirstChunkTimestamp.IsZero() {
-	ttft = accumulator.FirstChunkTimestamp.Sub(accumulator.StartTimestamp).Nanoseconds() / 1e6
-}
+ttft := calculateTTFT(accumulator.StartTimestamp, accumulator.FirstChunkTimestamp)
core/bifrost_test.go (1)

71-72: LGTM! Test calls correctly updated for new tracing integration.

All test call sites have been consistently updated to include the tracer parameter (schemas.DefaultTracer()) and request parameter (nil), aligning with the new signature of executeRequestWithRetries.

Optional: Consider clarifying the nil parameter.

The trailing nil parameter passed to executeRequestWithRetries could be clearer with a comment or named constant (e.g., var nilRequest *schemas.BifrostRequest = nil) to improve test readability.

Also applies to: 106-107, 141-142, 205-206, 279-280, 509-510

plugins/semanticcache/main.go (1)

338-341: Semantic cache transport middleware + CacheType gating look sane, with a small edge-case

Returning nil from HTTPTransportMiddleware is fine if the transport chain skips nils. The new CacheTypeKey logic correctly narrows to direct-only or semantic-only caching when set.

Be aware that if a new CacheType value is ever introduced and written into context, both performDirectSearch and performSemanticSearch will be false, effectively disabling cache lookup. If you’d rather fall back to “both enabled” on unknown values, you could keep the defaults and only flip the one that should be disabled.

Please confirm that callers only ever set CacheTypeKey to CacheTypeDirect or CacheTypeSemantic at this point; otherwise you may want a default/fallback branch.

Also applies to: 381-390

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

881-900: Streaming tracing integration is coherent; consider covering scanner-error path too

The new tracing flow makes sense:

  • Each streaming chunk is accumulated via Tracer.AddStreamingChunk.
  • Final chunks (as marked by BifrostContextKeyStreamEndIndicator) call completeDeferredSpan, which:
    • Looks up the deferred LLM span by traceID.
    • Uses GetAccumulatedChunks to drive PopulateLLMResponseAttributes.
    • Sets TTFT and total chunk attributes.
    • Finalizes post-hook spans via BifrostContextKeyPostHookSpanFinalizer and ends the LLM span with OK/Error.
    • Clears the deferred span.

ProcessAndSendResponse and ProcessAndSendBifrostError are both wired into this, so normal and provider-error streaming terminations are handled.

One gap you may want to close: ProcessAndSendError (the low-level stream read/scanner error path) doesn’t currently invoke completeDeferredSpan, so a streaming request that fails before a BifrostResponse/BifrostError is produced could leave the deferred LLM span open. If that function is reachable after the LLM span has been created, calling completeDeferredSpan(&ctx, nil, bifrostError) there as well would make streaming span completion fully symmetric.

Please double-check where ProcessAndSendError is used; if it’s part of the same LLM call lifecycle, consider wiring it into completeDeferredSpan to avoid dangling spans on scanner failures.

Also applies to: 939-945, 1412-1501

docs/plugins/writing-plugin.mdx (1)

53-59: Plugin v2 example is correct; consider aligning the documented core version with v1.4.x+

The v1.4.x+ tab’s hello-world plugin:

  • Exposes HTTPTransportMiddleware() schemas.BifrostHTTPMiddleware,
  • Uses the standard next fasthttp.RequestHandler pattern, and
  • Keeps Init, GetName, PreHook, PostHook, and Cleanup signatures consistent with the loader,

which matches the updated Plugin interface and the example code in examples/plugins/hello-world.

One documentation tweak: Step 1’s go.mod still pins github.com/maximhq/bifrost/core v1.2.38, while the v1.4.x+ code relies on the newer HTTP middleware surface. It might be clearer either to:

  • Update that snippet to a v1.4.x-compatible core version, or
  • Annotate that snippet as v1.3.x-only and add a separate v1.4.x+ go.mod example (or simply say “match the Bifrost version you’re running”).

Also applies to: 65-127, 213-241, 429-444

core/bifrost.go (1)

3270-3436: Streaming post‑hook timing aggregation and spans are well-structured

The new PluginPipeline streaming timing machinery looks coherent:

  • RunPostHooks switches to timing accumulation when BifrostContextKeyStreamStartTime is present, avoiding per-chunk span spam.
  • accumulatePluginTiming maintains per-plugin totals and call counts plus error counts, recording order of first occurrence.
  • FinalizeStreamingPostHookSpans builds a nested span hierarchy (plugin.<name>.posthook) in that order, sets aggregated metrics (invocations, avg/total duration, error count), and closes spans in reverse order like a call stack.

This design gives a clear, low-cardinality representation of streaming plugin cost while keeping allocation and span volume low.

framework/tracing/helpers.go (1)

10-83: Tracing helpers are straightforward; consider optional nil-store guard

The context-based helpers (GetTraceID, GetTrace, AddSpan, AddChildSpan, EndSpan, SetSpanAttribute, AddSpanEvent) are all defensive w.r.t. missing trace IDs and missing spans, which is good.

If you expect tests or optional wiring to sometimes pass a nil *TraceStore, a small extra guard in GetTrace/AddSpan/EndSpan (early-return on nil store) would make them fully panic-proof, but that’s optional given current usage.

plugins/otel/converter.go (1)

49-70: OTEL conversion path looks good; minor type-coverage nits only

The trace/span → OTEL conversion is generally solid:

  • hexToBytes cleans and normalizes IDs to fixed byte lengths before assigning TraceId/SpanId/ParentSpanId.
  • convertTraceToResourceSpan and convertSpanToOTELSpan correctly fill IDs, timestamps, attributes, status, events, and parent span linkage.
  • anyToKeyValue and convertAttributesToKeyValues cover the common scalar/slice/map cases and gracefully skip empty values.

If you want to tighten things further, consider:

  • Adding explicit handling for []bool in anyToKeyValue so boolean slices don’t fall back to string formatting.
  • Optionally logging or asserting when hex.DecodeString in hexToBytes fails (currently the error is ignored, though the input is sanitized first).

These are minor polish items; the core conversion logic is sound.

Also applies to: 72-107, 133-229, 271-285

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fb945b9 and 1cecccb.

⛔ Files ignored due to path filters (2)
  • core/go.sum is excluded by !**/*.sum
  • examples/plugins/hello-world/go.sum is excluded by !**/*.sum
📒 Files selected for processing (66)
  • core/bifrost.go
  • core/bifrost_test.go
  • core/go.mod
  • core/mcp.go
  • core/providers/utils/utils.go
  • core/schemas/bifrost.go
  • core/schemas/context.go
  • core/schemas/plugin.go
  • core/schemas/trace.go
  • core/schemas/tracer.go
  • docs/docs.json
  • docs/plugins/getting-started.mdx
  • docs/plugins/migration-guide.mdx
  • docs/plugins/writing-plugin.mdx
  • examples/plugins/hello-world/go.mod
  • examples/plugins/hello-world/main.go
  • framework/configstore/tables/mcp.go
  • framework/plugins/dynamicplugin.go
  • framework/plugins/dynamicplugin_test.go
  • framework/streaming/accumulator.go
  • framework/streaming/audio.go
  • framework/streaming/chat.go
  • framework/streaming/responses.go
  • framework/streaming/transcription.go
  • framework/streaming/types.go
  • framework/tracing/helpers.go
  • framework/tracing/llmspan.go
  • framework/tracing/propagation.go
  • framework/tracing/store.go
  • framework/tracing/tracer.go
  • plugins/governance/go.mod
  • plugins/governance/main.go
  • plugins/jsonparser/main.go
  • plugins/logging/main.go
  • plugins/logging/utils.go
  • plugins/maxim/main.go
  • plugins/mocker/main.go
  • plugins/otel/converter.go
  • plugins/otel/main.go
  • plugins/otel/ttlsyncmap.go
  • plugins/semanticcache/main.go
  • plugins/telemetry/main.go
  • transports/bifrost-http/handlers/cache.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/handlers/health.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/handlers/logging.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/handlers/mcpServer.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • transports/bifrost-http/handlers/plugins.go
  • transports/bifrost-http/handlers/providers.go
  • transports/bifrost-http/handlers/session.go
  • transports/bifrost-http/handlers/ui.go
  • transports/bifrost-http/handlers/websocket.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/lib/middleware.go
  • transports/bifrost-http/server/server.go
  • transports/go.mod
  • ui/app/workspace/logs/views/logDetailsSheet.tsx
  • ui/components/sidebar.tsx
  • ui/components/ui/sheet.tsx
  • ui/components/ui/switch.tsx
💤 Files with no reviewable changes (2)
  • framework/configstore/tables/mcp.go
  • plugins/otel/ttlsyncmap.go
✅ Files skipped from review due to trivial changes (1)
  • docs/plugins/migration-guide.mdx
🚧 Files skipped from review as they are similar to previous changes (24)
  • core/go.mod
  • framework/streaming/audio.go
  • transports/bifrost-http/handlers/providers.go
  • transports/bifrost-http/handlers/logging.go
  • framework/plugins/dynamicplugin_test.go
  • core/schemas/context.go
  • core/mcp.go
  • framework/streaming/types.go
  • plugins/maxim/main.go
  • plugins/governance/go.mod
  • transports/bifrost-http/handlers/ui.go
  • transports/go.mod
  • transports/bifrost-http/handlers/mcp.go
  • plugins/mocker/main.go
  • transports/bifrost-http/lib/middleware.go
  • core/schemas/bifrost.go
  • ui/app/workspace/logs/views/logDetailsSheet.tsx
  • transports/bifrost-http/handlers/config.go
  • ui/components/sidebar.tsx
  • docs/docs.json
  • plugins/jsonparser/main.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/handlers/health.go
  • framework/plugins/dynamicplugin.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:

  • plugins/logging/utils.go
  • transports/bifrost-http/handlers/governance.go
  • framework/streaming/responses.go
  • plugins/semanticcache/main.go
  • core/schemas/plugin.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • core/bifrost.go
  • transports/bifrost-http/handlers/integrations.go
  • examples/plugins/hello-world/main.go
  • transports/bifrost-http/handlers/session.go
  • framework/streaming/chat.go
  • framework/streaming/transcription.go
  • core/bifrost_test.go
  • framework/tracing/store.go
  • plugins/governance/main.go
  • docs/plugins/writing-plugin.mdx
  • core/providers/utils/utils.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/server/server.go
  • transports/bifrost-http/handlers/cache.go
  • framework/tracing/propagation.go
  • plugins/telemetry/main.go
  • plugins/otel/main.go
  • framework/streaming/accumulator.go
  • transports/bifrost-http/handlers/plugins.go
  • plugins/otel/converter.go
  • ui/components/ui/sheet.tsx
  • examples/plugins/hello-world/go.mod
  • framework/tracing/helpers.go
  • transports/bifrost-http/handlers/websocket.go
  • transports/bifrost-http/handlers/mcpServer.go
  • core/schemas/trace.go
  • ui/components/ui/switch.tsx
  • docs/plugins/getting-started.mdx
  • core/schemas/tracer.go
  • framework/tracing/tracer.go
  • plugins/logging/main.go
  • transports/bifrost-http/handlers/middlewares.go
  • framework/tracing/llmspan.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:

  • plugins/logging/utils.go
  • transports/bifrost-http/handlers/governance.go
  • framework/streaming/responses.go
  • plugins/semanticcache/main.go
  • core/schemas/plugin.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • core/bifrost.go
  • transports/bifrost-http/handlers/integrations.go
  • examples/plugins/hello-world/main.go
  • transports/bifrost-http/handlers/session.go
  • framework/streaming/chat.go
  • framework/streaming/transcription.go
  • core/bifrost_test.go
  • framework/tracing/store.go
  • plugins/governance/main.go
  • core/providers/utils/utils.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/server/server.go
  • transports/bifrost-http/handlers/cache.go
  • framework/tracing/propagation.go
  • plugins/telemetry/main.go
  • plugins/otel/main.go
  • framework/streaming/accumulator.go
  • transports/bifrost-http/handlers/plugins.go
  • plugins/otel/converter.go
  • framework/tracing/helpers.go
  • transports/bifrost-http/handlers/websocket.go
  • transports/bifrost-http/handlers/mcpServer.go
  • core/schemas/trace.go
  • core/schemas/tracer.go
  • framework/tracing/tracer.go
  • plugins/logging/main.go
  • transports/bifrost-http/handlers/middlewares.go
  • framework/tracing/llmspan.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/governance.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/handlers/session.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/server/server.go
  • transports/bifrost-http/handlers/cache.go
  • transports/bifrost-http/handlers/plugins.go
  • transports/bifrost-http/handlers/websocket.go
  • transports/bifrost-http/handlers/mcpServer.go
  • transports/bifrost-http/handlers/middlewares.go
📚 Learning: 2025-12-24T04:36:57.698Z
Learnt from: danpiths
Repo: maximhq/bifrost PR: 1169
File: transports/bifrost-http/handlers/governance.go:1708-1878
Timestamp: 2025-12-24T04:36:57.698Z
Learning: In governance update handlers (e.g., updateModelConfig, updateProviderGovernance), design updates to support clearing individual fields by sending null/empty values (e.g., {"rate_limit": {"token_max_limit": null}} clears token_max_limit). Follow this pattern for partial updates so users can remove specific governance settings without deleting the whole entity. Ensure budget updates follow the same approach using direct field assignment. Review input validation, JSON decoding (e.g., pointers vs values in Go structs), and API documentation to reflect nullable fields and expected behavior.

Applied to files:

  • transports/bifrost-http/handlers/governance.go
📚 Learning: 2025-12-22T10:50:40.990Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1154
File: plugins/governance/store.go:1165-1186
Timestamp: 2025-12-22T10:50:40.990Z
Learning: In the Bifrost governance plugin, budgets and rate limits have 1:1 relationships with their parent entities (virtual keys, teams, customers). Do not assume sharing; ensure cascade deletion logic only deletes budgets/rate limits when there are no shared references. Enforce invariants in code and add tests to verify no cross-entity sharing and that cascade deletes only remove the specific child of the parent. If a counterexample arises, adjust data model or add guards.

Applied to files:

  • plugins/governance/main.go
📚 Learning: 2025-12-19T09:26:54.961Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/providers/utils/utils.go:1050-1051
Timestamp: 2025-12-19T09:26:54.961Z
Learning: Update streaming end-marker handling so HuggingFace is treated as a non-[DONE] provider for backends that do not emit a DONE marker (e.g., meta llama on novita). In core/providers/utils/utils.go, adjust ProviderSendsDoneMarker() (or related logic) to detect providers that may not emit DONE and avoid relying on DONE as the sole end signal. Add tests to cover both DONE-emitting and non-DONE backends, with clear documentation in code comments explaining the rationale and any fallback behavior.

Applied to files:

  • core/providers/utils/utils.go
🧬 Code graph analysis (27)
plugins/logging/utils.go (3)
core/schemas/tracer.go (1)
  • StreamAccumulatorResult (15-33)
framework/streaming/types.go (9)
  • ProcessedStreamResponse (118-126)
  • StreamType (10-10)
  • StreamTypeText (13-13)
  • StreamTypeChat (14-14)
  • StreamTypeResponses (17-17)
  • StreamTypeAudio (15-15)
  • StreamTypeTranscription (16-16)
  • StreamResponseType (20-20)
  • AccumulatedData (28-48)
core/schemas/chatcompletions.go (1)
  • ChatAssistantMessage (649-656)
transports/bifrost-http/handlers/governance.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
framework/streaming/responses.go (1)
framework/streaming/types.go (1)
  • AccumulatedData (28-48)
core/schemas/plugin.go (2)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-26)
core/schemas/trace.go (1)
  • Trace (10-19)
transports/bifrost-http/handlers/middlewares_test.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/integrations.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
examples/plugins/hello-world/main.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/session.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
framework/streaming/chat.go (1)
framework/streaming/types.go (1)
  • AccumulatedData (28-48)
framework/streaming/transcription.go (1)
framework/streaming/types.go (1)
  • AccumulatedData (28-48)
core/bifrost_test.go (1)
core/schemas/tracer.go (1)
  • DefaultTracer (174-176)
framework/tracing/store.go (1)
core/schemas/trace.go (6)
  • Trace (10-19)
  • Span (52-65)
  • SpanEvent (107-111)
  • SpanKind (115-115)
  • SpanStatusUnset (147-147)
  • SpanStatus (143-143)
plugins/governance/main.go (3)
core/utils.go (1)
  • Ptr (56-58)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-26)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
core/providers/utils/utils.go (4)
core/schemas/bifrost.go (6)
  • BifrostResponse (335-355)
  • BifrostContextKeyTracer (146-146)
  • BifrostContextKeyTraceID (143-143)
  • BifrostContextKeyStreamEndIndicator (131-131)
  • BifrostError (474-483)
  • BifrostContextKeyPostHookSpanFinalizer (149-149)
core/schemas/tracer.go (1)
  • Tracer (38-112)
framework/tracing/tracer.go (1)
  • Tracer (17-20)
core/schemas/trace.go (2)
  • AttrTimeToFirstToken (198-198)
  • AttrTotalChunks (199-199)
transports/bifrost-http/integrations/router.go (3)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
core/schemas/bifrost.go (9)
  • ListModelsRequest (92-92)
  • TextCompletionRequest (93-93)
  • ChatCompletionRequest (95-95)
  • ResponsesRequest (97-97)
  • EmbeddingRequest (99-99)
  • SpeechRequest (100-100)
  • TranscriptionRequest (102-102)
  • BifrostContextKeyDeferTraceCompletion (147-147)
  • BifrostContextKeyTraceCompleter (148-148)
transports/bifrost-http/handlers/inference.go (5)
  • ChatRequest (172-176)
  • ResponsesRequest (266-270)
  • EmbeddingRequest (273-277)
  • SpeechRequest (279-283)
  • TranscriptionRequest (285-289)
transports/bifrost-http/server/server.go (4)
core/schemas/plugin.go (2)
  • BifrostHTTPMiddleware (38-38)
  • ObservabilityPlugin (123-137)
transports/bifrost-http/handlers/middlewares.go (2)
  • TransportInterceptorMiddleware (50-70)
  • NewTracingMiddleware (207-214)
framework/tracing/store.go (1)
  • NewTraceStore (38-65)
framework/tracing/tracer.go (1)
  • NewTracer (24-29)
transports/bifrost-http/handlers/cache.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
plugins/otel/main.go (2)
core/schemas/plugin.go (2)
  • BifrostHTTPMiddleware (38-38)
  • ObservabilityPlugin (123-137)
core/schemas/trace.go (1)
  • Trace (10-19)
framework/streaming/accumulator.go (1)
framework/streaming/types.go (3)
  • StreamAccumulator (103-115)
  • ChatStreamChunk (77-87)
  • ResponsesStreamChunk (90-100)
transports/bifrost-http/handlers/plugins.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
plugins/otel/converter.go (2)
plugins/otel/main.go (1)
  • OtelPlugin (58-75)
core/schemas/trace.go (15)
  • Trace (10-19)
  • Span (52-65)
  • SpanKind (115-115)
  • SpanKindLLMCall (121-121)
  • SpanKindInternal (139-139)
  • SpanKindRetry (127-127)
  • SpanKindFallback (129-129)
  • SpanKindMCPTool (125-125)
  • SpanKindEmbedding (133-133)
  • SpanKindSpeech (135-135)
  • SpanKindTranscription (137-137)
  • SpanStatus (143-143)
  • SpanStatusOk (149-149)
  • SpanStatusError (151-151)
  • SpanEvent (107-111)
framework/tracing/helpers.go (3)
core/schemas/bifrost.go (1)
  • BifrostContextKeyTraceID (143-143)
framework/tracing/store.go (1)
  • TraceStore (24-35)
core/schemas/trace.go (5)
  • Trace (10-19)
  • SpanKind (115-115)
  • Span (52-65)
  • SpanStatus (143-143)
  • SpanEvent (107-111)
transports/bifrost-http/handlers/websocket.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/mcpServer.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
core/schemas/trace.go (2)
framework/tracing/helpers.go (1)
  • AddSpan (32-38)
ui/lib/constants/logs.ts (1)
  • Status (163-163)
framework/tracing/tracer.go (5)
framework/tracing/store.go (1)
  • TraceStore (24-35)
core/schemas/tracer.go (3)
  • Tracer (38-112)
  • SpanHandle (11-11)
  • StreamAccumulatorResult (15-33)
framework/tracing/helpers.go (3)
  • GetTraceID (11-20)
  • EndSpan (50-56)
  • GetTrace (23-29)
core/schemas/bifrost.go (6)
  • BifrostContextKeySpanID (144-144)
  • BifrostRequest (173-193)
  • BifrostResponse (335-355)
  • BifrostError (474-483)
  • BifrostContextKeyRequestID (124-124)
  • BifrostContextKeyStreamEndIndicator (131-131)
framework/streaming/types.go (1)
  • StreamResponseTypeFinal (24-24)
framework/tracing/llmspan.go (6)
core/schemas/bifrost.go (24)
  • BifrostRequest (173-193)
  • RequestType (89-89)
  • ChatCompletionRequest (95-95)
  • ChatCompletionStreamRequest (96-96)
  • TextCompletionRequest (93-93)
  • TextCompletionStreamRequest (94-94)
  • EmbeddingRequest (99-99)
  • TranscriptionRequest (102-102)
  • TranscriptionStreamRequest (103-103)
  • SpeechRequest (100-100)
  • SpeechStreamRequest (101-101)
  • ResponsesRequest (97-97)
  • ResponsesStreamRequest (98-98)
  • BatchCreateRequest (104-104)
  • BatchListRequest (105-105)
  • BatchRetrieveRequest (106-106)
  • BatchCancelRequest (107-107)
  • BatchResultsRequest (108-108)
  • FileUploadRequest (109-109)
  • FileListRequest (110-110)
  • FileRetrieveRequest (111-111)
  • FileDeleteRequest (112-112)
  • FileContentRequest (113-113)
  • BifrostResponse (335-355)
core/schemas/trace.go (5)
  • AttrProviderName (159-159)
  • AttrRequestModel (160-160)
  • AttrError (216-216)
  • AttrErrorType (217-217)
  • AttrErrorCode (218-218)
core/schemas/chatcompletions.go (2)
  • BifrostChatRequest (12-19)
  • BifrostChatResponse (27-42)
core/schemas/responses.go (2)
  • BifrostResponsesRequest (32-39)
  • BifrostResponsesResponse (45-85)
core/schemas/batch.go (10)
  • BifrostBatchCreateRequest (65-83)
  • BifrostBatchListRequest (118-133)
  • BifrostBatchRetrieveRequest (150-159)
  • BifrostBatchCancelRequest (205-214)
  • BifrostBatchResultsRequest (234-246)
  • BifrostBatchCreateResponse (91-115)
  • BifrostBatchListResponse (136-147)
  • BifrostBatchRetrieveResponse (167-202)
  • BifrostBatchCancelResponse (222-231)
  • BifrostBatchResultsResponse (285-294)
core/schemas/files.go (10)
  • BifrostFileUploadRequest (53-67)
  • BifrostFileListRequest (109-128)
  • BifrostFileRetrieveRequest (146-159)
  • BifrostFileDeleteRequest (186-198)
  • BifrostFileContentRequest (215-227)
  • BifrostFileUploadResponse (90-106)
  • BifrostFileListResponse (136-143)
  • BifrostFileRetrieveResponse (167-183)
  • BifrostFileDeleteResponse (206-212)
  • BifrostFileContentResponse (235-241)

Comment thread core/schemas/plugin.go
Comment thread framework/tracing/propagation.go Outdated
Comment thread ui/components/ui/sheet.tsx Outdated
Copy link
Copy Markdown
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: 6

♻️ Duplicate comments (17)
plugins/jsonparser/version (1)

1-1: Patch version increment inconsistent with breaking changes.

This concern was already raised in a previous review. The changelog documents breaking changes (TransportInterceptor → HTTPTransportMiddleware), but the version increment is only a patch bump (1.3.52 → 1.3.53). Per semantic versioning, breaking changes warrant at minimum a minor version bump.

plugins/otel/changelog.md (1)

19-19: Verify the migration guide link exists.

The changelog references /docs/plugins/migration-guide at line 19. A previous review comment flagged that this file doesn't exist in the repository. Ensure the migration guide is created before merging, or update this link to point to the correct location.

#!/bin/bash
# Description: Verify the migration guide exists and check alternative paths

# Check if the exact path exists
if fd -t f "migration-guide" docs/; then
  echo "Migration guide found:"
  fd -t f "migration-guide" docs/
else
  echo "Migration guide not found at expected path"
fi

# Also check for plugin documentation structure
echo "Plugin docs structure:"
fd -t f . docs/plugins/ 2>/dev/null || echo "docs/plugins/ directory not found"
fd -t f . docs/features/plugins/ 2>/dev/null || echo "docs/features/plugins/ directory not found"
ui/app/workspace/logs/views/logDetailsSheet.tsx (1)

183-187: Add aria-label for screen reader accessibility.

The icon-only button at line 184 lacks an accessible name for screen reader users. Add an aria-label attribute to improve accessibility.

🔎 Proposed fix
 <DropdownMenuTrigger asChild>
-  <Button variant="ghost" size="icon">
+  <Button variant="ghost" size="icon" aria-label="Log actions">
     <MoreVertical className="h-3 w-3" />
   </Button>
 </DropdownMenuTrigger>
core/providers/utils/utils.go (1)

1424-1513: Refactor to pass context by value.

The function signature completeDeferredSpan(ctx *context.Context, ...) violates Go conventions. The context.Context interface should always be passed by value, never by pointer, as it's designed for safe concurrent use and value semantics.

🔎 Proposed fix

Update the function signature and body:

-func completeDeferredSpan(ctx *context.Context, result *schemas.BifrostResponse, err *schemas.BifrostError) {
+func completeDeferredSpan(ctx context.Context, result *schemas.BifrostResponse, err *schemas.BifrostError) {
   if ctx == nil {
     return
   }
 
-  traceID, ok := (*ctx).Value(schemas.BifrostContextKeyTraceID).(string)
+  traceID, ok := ctx.Value(schemas.BifrostContextKeyTraceID).(string)
   if !ok || traceID == "" {
     return
   }
 
-  tracerVal := (*ctx).Value(schemas.BifrostContextKeyTracer)
+  tracerVal := ctx.Value(schemas.BifrostContextKeyTracer)
   // ... rest of function updated similarly

Update all call sites:

-completeDeferredSpan(&ctx, processedResponse, processedError)
+completeDeferredSpan(ctx, processedResponse, processedError)
framework/streaming/chat.go (1)

157-171: TTFT calculation is correct but duplicated.

The TTFT calculation logic is correct and properly guards against zero timestamps. However, this is identical to the implementation in framework/streaming/responses.go. See the refactoring suggestion in that file to extract this into a shared helper function.

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

508-514: Non-streaming path passes wrong context to Bifrost client.

The non-streaming request handlers pass ctx (*fasthttp.RequestCtx) to Bifrost client methods (Lines 514, 534, 557, 580, 603, 626, 660), but the comment at Line 503-507 states "Use the cancellable context from ConvertToBifrostContext". The bifrostCtx contains trace propagation, plugin context keys, and timeout handling that would be lost.

This appears to be the same issue flagged in a previous review. Passing *bifrostCtx (dereferenced) instead of ctx would ensure tracing and plugin hooks work correctly.

🔎 Suggested fix
 case bifrostReq.ListModelsRequest != nil:
-	listModelsResponse, bifrostErr := g.client.ListModelsRequest(ctx, bifrostReq.ListModelsRequest)
+	listModelsResponse, bifrostErr := g.client.ListModelsRequest(*bifrostCtx, bifrostReq.ListModelsRequest)

Apply similar changes to all other non-streaming request type cases (TextCompletionRequest, ChatCompletionRequest, ResponsesRequest, EmbeddingRequest, SpeechRequest, TranscriptionRequest).

transports/changelog.md (1)

16-23: Add language specifier to fenced code block.

The fenced code block lacks a language identifier, which prevents proper syntax highlighting. Add go or text after the opening backticks.

🔎 Suggested fix
   **Migration summary:**
-  ```
+  ```go
   // v1.3.x (removed)
   TransportInterceptor(ctx *BifrostContext, url string, headers map[string]string, body map[string]any) (map[string]string, map[string]any, error)
plugins/governance/main.go (1)

233-256: Missing prefix validation for x-bf-vk header.

The parseVirtualKey function validates that values from Authorization (Bearer) and x-api-key headers start with VirtualKeyPrefix, but the x-bf-vk header at lines 235-238 is returned without prefix validation. This inconsistency could allow invalid virtual keys to pass through.

🔎 Proposed fix
 	vkHeader := ctx.Request.Header.Peek("x-bf-vk")
 	if string(vkHeader) != "" {
+		vkValue := string(vkHeader)
+		if strings.HasPrefix(strings.ToLower(vkValue), VirtualKeyPrefix) {
-		return bifrost.Ptr(string(vkHeader))
+			return bifrost.Ptr(vkValue)
+		}
 	}
framework/tracing/store.go (1)

13-22: Trim unused fields from DeferredSpanInfo and clear streaming state on TTL cleanup

  • DeferredSpanInfo.Tracer and DeferredSpanInfo.RequestID are never assigned or read; they just bloat each deferred entry. This was called out in an earlier review and still appears unused here.
  • cleanupOldTraces evicts expired traces from traces and releases them, but does not delete any corresponding entries in deferredSpans. For long‑running processes with abandoned streaming traces, this can leak accumulated chunk buffers.

Consider:

  • Dropping the unused fields from DeferredSpanInfo, or wiring them up if you really need them.
  • In cleanupOldTraces, deleting the deferred span entry for each removed trace (e.g. using trace.TraceID from the deleted value).
Example adjustments
 type DeferredSpanInfo struct {
 	SpanID             string
 	StartTime          time.Time
-	Tracer             schemas.Tracer // Reference to tracer for completing the span
-	RequestID          string         // Request ID for accumulator lookup
 	FirstChunkTime     time.Time      // Timestamp of first chunk (for TTFT calculation)
 	AccumulatedChunks  []*schemas.BifrostResponse // Accumulated streaming chunks
 	mu                 sync.Mutex     // Mutex for thread-safe chunk accumulation
@@
 func (s *TraceStore) cleanupOldTraces() {
 	cutoff := time.Now().Add(-s.ttl)
@@
-	s.traces.Range(func(key, value any) bool {
-		trace := value.(*schemas.Trace)
+	s.traces.Range(func(key, value any) bool {
+		trace := value.(*schemas.Trace)
 		if trace.StartTime.Before(cutoff) {
 			if deleted, ok := s.traces.LoadAndDelete(key); ok {
-				s.ReleaseTrace(deleted.(*schemas.Trace))
+				t := deleted.(*schemas.Trace)
+				// Drop any deferred streaming state for this trace
+				s.ClearDeferredSpan(t.TraceID)
+				s.ReleaseTrace(t)
 				count++
 			}

Also applies to: 119-177, 341-360

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

191-307: Fix TracingMiddleware to pass a context.Context into StartSpan instead of *fasthttp.RequestCtx

Tracer.StartSpan is defined against context.Context, but Middleware() currently calls:

spanCtx, rootSpan := m.tracer.Load().StartSpan(ctx, string(ctx.RequestURI()), schemas.SpanKindHTTPRequest)

where ctx is *fasthttp.RequestCtx. This does not satisfy context.Context and will not compile; even if it did, the tracer implementation expects a real context.Context (it calls Value on it, etc.).

Use the existing adapter pattern (as in the inference handlers) to convert the fasthttp context to a Bifrost context first, and then pass that into StartSpan, while still propagating the resulting span ID back into the fasthttp RequestCtx:

Suggested fix using `lib.ConvertToBifrostContext`
 func (m *TracingMiddleware) Middleware() schemas.BifrostHTTPMiddleware {
 	return func(next fasthttp.RequestHandler) fasthttp.RequestHandler {
 		return func(ctx *fasthttp.RequestCtx) {
@@
-			// Create root span for the HTTP request
-			spanCtx, rootSpan := m.tracer.Load().StartSpan(ctx, string(ctx.RequestURI()), schemas.SpanKindHTTPRequest)
+			// Create root span for the HTTP request using a standard context
+			bfCtx := lib.ConvertToBifrostContext(ctx)
+			spanCtx, rootSpan := m.tracer.Load().StartSpan(bfCtx, string(ctx.RequestURI()), schemas.SpanKindHTTPRequest)
 			if rootSpan != nil {
 				m.tracer.Load().SetAttribute(rootSpan, "http.method", string(ctx.Method()))
 				m.tracer.Load().SetAttribute(rootSpan, "http.url", string(ctx.RequestURI()))
 				m.tracer.Load().SetAttribute(rootSpan, "http.user_agent", string(ctx.Request.Header.UserAgent()))
 				// Set root span ID in context for child span creation
 				if spanID, ok := spanCtx.Value(schemas.BifrostContextKeySpanID).(string); ok {
 					ctx.SetUserValue(schemas.BifrostContextKeySpanID, spanID)
 				}
 			}

This keeps tracing consistent with the rest of the stack and avoids a type mismatch at compile time.

core/schemas/trace.go (3)

40-49: Trace.Reset() lacks mutex protection — potential data race.

This method modifies all fields including Spans without holding the mutex, creating a data race if Reset is called while another goroutine is accessing the trace (e.g., via AddSpan or GetSpan).

This was flagged in a previous review. Add mutex protection:

🔎 Proposed fix
 // Reset clears the trace for reuse from pool
 func (t *Trace) Reset() {
+	t.mu.Lock()
+	defer t.mu.Unlock()
 	t.TraceID = ""
 	t.ParentID = ""
 	t.RootSpan = nil
 	t.Spans = t.Spans[:0]
 	t.StartTime = time.Time{}
 	t.EndTime = time.Time{}
 	t.Attributes = nil
 }

84-89: Span.End() lacks mutex protection — potential data race.

Unlike SetAttribute and AddEvent, the End method modifies EndTime, Status, and StatusMsg without holding the mutex.

🔎 Proposed fix
 // End marks the span as complete with the given status
 func (s *Span) End(status SpanStatus, statusMsg string) {
+	s.mu.Lock()
+	defer s.mu.Unlock()
 	s.EndTime = time.Now()
 	s.Status = status
 	s.StatusMsg = statusMsg
 }

91-104: Span.Reset() lacks mutex protection — potential data race.

Similar to Trace.Reset(), this method modifies fields without holding the mutex.

🔎 Proposed fix
 // Reset clears the span for reuse from pool
 func (s *Span) Reset() {
+	s.mu.Lock()
+	defer s.mu.Unlock()
 	s.SpanID = ""
 	s.ParentID = ""
 	// ... rest of fields
 }
framework/tracing/tracer.go (2)

393-398: Choice indices are not sorted despite the comment.

The comment at line 394 says "Sort choice indices for deterministic output" but no sorting is performed. The iteration order over a map in Go is non-deterministic, so the resulting choices may appear in different orders across runs.

This was flagged in a previous review and marked as addressed, but the sorting code is still missing.

🔎 Proposed fix
+import "sort"

 // Build final choices from accumulated data
 // Sort choice indices for deterministic output
 choiceIndices := make([]int, 0, len(choiceMap))
 for idx := range choiceMap {
     choiceIndices = append(choiceIndices, idx)
 }
+sort.Ints(choiceIndices)

433-441: Tool call indices are not sorted despite the comment.

Similarly, the comment at line 433 says "Sort tool calls by index" but no sorting is performed. The iteration order will be non-deterministic.

🔎 Proposed fix
 // Sort tool calls by index
 tcIndices := make([]int, 0, len(accum.toolCalls))
 for tcIdx := range accum.toolCalls {
     tcIndices = append(tcIndices, tcIdx)
 }
+sort.Ints(tcIndices)
 toolCalls := make([]schemas.ChatAssistantMessageToolCall, 0, len(accum.toolCalls))
framework/tracing/llmspan.go (2)

190-193: ExtraParams keys may collide with standard attribute names.

User-provided keys from ExtraParams are written directly to the attributes map without namespacing. If a user sets a key like "gen_ai.request.model" in ExtraParams, it would overwrite the standard attribute set earlier.

This pattern repeats at lines 298-300, 365-367, 575-577, 708-710, 738-740, 753-755, 768-770, 783-785, 958-960, 982-984, 997-999, 1012-1014, 1027-1029.

🔎 Suggested approach: namespace ExtraParams
 // ExtraParams
 for k, v := range req.Params.ExtraParams {
-    attrs[k] = fmt.Sprintf("%v", v)
+    attrs["gen_ai.extra."+k] = fmt.Sprintf("%v", v)
 }

376-382: Use a float-appropriate formatter for embedding values.

The code uses %d format verb for embedding values:

embedding[i] = fmt.Sprintf("%d", v)

If Embedding is a float slice (typical for embeddings), %d is incorrect and will truncate values or produce unexpected results.

🔎 Proposed fix
 } else if req.Input.Embedding != nil {
     embedding := make([]string, len(req.Input.Embedding))
     for i, v := range req.Input.Embedding {
-        embedding[i] = fmt.Sprintf("%d", v)
+        embedding[i] = fmt.Sprintf("%g", v)
     }
     attrs[schemas.AttrInputEmbedding] = strings.Join(embedding, ",")
 }
🧹 Nitpick comments (8)
framework/streaming/responses.go (1)

742-756: Extract duplicated TTFT calculation logic.

The TTFT calculation logic here is identical to the implementation in framework/streaming/chat.go (lines 157-171). This violates the DRY principle and creates maintenance burden.

🔎 Proposed refactor to extract common TTFT calculation

Add a helper function in framework/streaming/types.go or framework/streaming/accumulator.go:

// CalculateTTFT calculates Time to First Token in milliseconds from start and first chunk timestamps.
// Returns 0 if either timestamp is zero.
func CalculateTTFT(startTimestamp, firstChunkTimestamp time.Time) int64 {
	if startTimestamp.IsZero() || firstChunkTimestamp.IsZero() {
		return 0
	}
	return firstChunkTimestamp.Sub(startTimestamp).Nanoseconds() / 1e6
}

Then update this file:

-	// Calculate Time to First Token (TTFT) in milliseconds
-	var ttft int64
-	if !accumulator.StartTimestamp.IsZero() && !accumulator.FirstChunkTimestamp.IsZero() {
-		ttft = accumulator.FirstChunkTimestamp.Sub(accumulator.StartTimestamp).Nanoseconds() / 1e6
-	}
+	// Calculate Time to First Token (TTFT) in milliseconds
+	ttft := CalculateTTFT(accumulator.StartTimestamp, accumulator.FirstChunkTimestamp)

	// Initialize accumulated data
	data := &AccumulatedData{

Apply the same change to framework/streaming/chat.go, framework/streaming/audio.go, and framework/streaming/transcription.go.

ui/components/devProfiler.tsx (2)

17-24: Handle edge cases in formatBytes for negative or extremely large values.

The function could produce unexpected results for edge cases: Math.log of a negative number returns NaN, and very large numbers could exceed the sizes array bounds.

🔎 Suggested improvement
 function formatBytes (bytes: number): string {
   if (bytes === 0) return '0 B'
+  if (bytes < 0) return `-${formatBytes(-bytes)}`
   const k = 1024
   const sizes = ['B', 'KB', 'MB', 'GB']
-  const i = Math.floor(Math.log(bytes) / Math.log(k))
+  const i = Math.min(Math.floor(Math.log(bytes) / Math.log(k)), sizes.length - 1)
   return `${(bytes / Math.pow(k, i)).toFixed(1)} ${sizes[i]}`
 }

369-394: Consider using a stable key for allocation items.

Using array index as key is generally discouraged for dynamic lists, but since this list is refreshed as a whole from the API and doesn't support reordering/insertion, it's acceptable here. If allocations gain unique identifiers in the future, prefer using those.

framework/streaming/accumulator.go (1)

138-141: Consider extracting duplicated FirstChunkTimestamp logic.

The same FirstChunkTimestamp tracking logic is repeated across all four add*StreamChunk methods. While functional, this could be extracted into a helper method to reduce duplication.

🔎 Example helper extraction
// trackFirstChunk sets FirstChunkTimestamp if not already set.
// Must be called while holding accumulator.mu lock.
func (acc *StreamAccumulator) trackFirstChunk(chunkTimestamp time.Time) {
    if acc.FirstChunkTimestamp.IsZero() {
        acc.FirstChunkTimestamp = chunkTimestamp
    }
}

Then in each add method:

accumulator.trackFirstChunk(chunk.Timestamp)

Also applies to: 162-165, 186-189, 210-213

framework/tracing/propagation.go (1)

118-121: Consider allowing higher version parsing per W3C spec.

The W3C Trace Context spec recommends that implementations attempt to parse higher versions by extracting trace-id, parent-id, and the sampled bit, rather than rejecting them outright. However, strictly supporting only version "00" is a reasonable conservative choice for now.

core/bifrost.go (1)

70-71: Tighten tracer atomic access and fix stale field comment

  • The field comment on Line 70 says atomic.Value “stores schemas.Tracer”, but you now always store *tracerWrapper. This is misleading for future maintainers.
  • Both getTracer() and Shutdown() currently do an unchecked .(*tracerWrapper) on atomic.Value.Load(). If Bifrost were ever constructed without Init/SetTracer (tests, future code, or partial initialization), these would panic.

Consider:

  • Updating the comment to reflect *tracerWrapper.
  • Making getTracer and Shutdown defensively handle a nil/incorrect value and fall back to schemas.DefaultTracer() or simply no-op in Shutdown.
Example defensive tweak
-	tracer              atomic.Value                       // tracer for distributed tracing (stores schemas.Tracer, NoOpTracer if not configured)
+	tracer              atomic.Value                       // stores *tracerWrapper wrapping a schemas.Tracer (NoOpTracer if not configured)

 func (bifrost *Bifrost) getTracer() schemas.Tracer {
-	return bifrost.tracer.Load().(*tracerWrapper).tracer
+	if v := bifrost.tracer.Load(); v != nil {
+		if tw, ok := v.(*tracerWrapper); ok && tw != nil && tw.tracer != nil {
+			return tw.tracer
+		}
+	}
+	return schemas.DefaultTracer()
 }

 func (bifrost *Bifrost) Shutdown() {
@@
-	// Stop the tracerWrapper to clean up background goroutines
-	if tracerWrapper := bifrost.tracer.Load().(*tracerWrapper); tracerWrapper != nil && tracerWrapper.tracer != nil {
-		tracerWrapper.tracer.Stop()
-	}
+	// Stop the tracerWrapper to clean up background goroutines
+	if v := bifrost.tracer.Load(); v != nil {
+		if tw, ok := v.(*tracerWrapper); ok && tw != nil && tw.tracer != nil {
+			tw.tracer.Stop()
+		}
+	}

Also applies to: 95-107, 129-147, 253-265, 3893-3896

framework/tracing/helpers.go (1)

10-82: Helpers are clean; optionally guard against nil TraceStore

The context-based helpers are straightforward and defensive on missing trace/ID/span. The only additional hardening you might consider is an early if store == nil { return } in GetTrace, AddSpan, AddChildSpan, EndSpan, SetSpanAttribute, and AddSpanEvent to avoid panics if a nil store is ever passed in.

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

270-368: getTopAllocations provides synthetic data, not actual allocation hotspots.

The function attempts to provide allocation data but:

  1. runtime.Caller(i+2) captures the call stack of getTopAllocations itself, not actual allocation sites
  2. The "distributed estimate" calculation (HeapAlloc / (i+1)) produces arbitrary numbers
  3. The fallback to hardcoded runtime function names with unrelated memory stats doesn't reflect real allocation patterns

This is acceptable for a dev-only diagnostic tool, but the data should be clearly labeled as estimates in the API response or documentation to avoid misinterpretation.

Consider adding a comment in the PprofData struct or API documentation indicating that TopAllocations provides estimated/synthetic data rather than actual allocation profiling results. For accurate heap profiling, users should use go tool pprof directly.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 330f7f3 and 6fb3318.

⛔ Files ignored due to path filters (2)
  • examples/plugins/hello-world/go.sum is excluded by !**/*.sum
  • plugins/governance/go.sum is excluded by !**/*.sum
📒 Files selected for processing (96)
  • .github/workflows/release-pipeline.yml
  • .github/workflows/scripts/push-mintlify-changelog.sh
  • .github/workflows/scripts/release-bifrost-http.sh
  • core/bifrost.go
  • core/bifrost_test.go
  • core/changelog.md
  • core/mcp.go
  • core/providers/utils/utils.go
  • core/schemas/bifrost.go
  • core/schemas/context.go
  • core/schemas/plugin.go
  • core/schemas/trace.go
  • core/schemas/tracer.go
  • core/version
  • docs/docs.json
  • docs/plugins/getting-started.mdx
  • docs/plugins/migration-guide.mdx
  • docs/plugins/writing-plugin.mdx
  • examples/plugins/hello-world/go.mod
  • examples/plugins/hello-world/main.go
  • framework/changelog.md
  • framework/configstore/tables/mcp.go
  • framework/plugins/dynamicplugin.go
  • framework/plugins/dynamicplugin_test.go
  • framework/streaming/accumulator.go
  • framework/streaming/audio.go
  • framework/streaming/chat.go
  • framework/streaming/responses.go
  • framework/streaming/transcription.go
  • framework/streaming/types.go
  • framework/tracing/helpers.go
  • framework/tracing/llmspan.go
  • framework/tracing/propagation.go
  • framework/tracing/store.go
  • framework/tracing/tracer.go
  • framework/version
  • plugins/governance/changelog.md
  • plugins/governance/go.mod
  • plugins/governance/main.go
  • plugins/governance/version
  • plugins/jsonparser/changelog.md
  • plugins/jsonparser/main.go
  • plugins/jsonparser/version
  • plugins/logging/changelog.md
  • plugins/logging/main.go
  • plugins/logging/utils.go
  • plugins/logging/version
  • plugins/maxim/changelog.md
  • plugins/maxim/main.go
  • plugins/maxim/version
  • plugins/mocker/changelog.md
  • plugins/mocker/main.go
  • plugins/mocker/version
  • plugins/otel/changelog.md
  • plugins/otel/converter.go
  • plugins/otel/main.go
  • plugins/otel/ttlsyncmap.go
  • plugins/otel/version
  • plugins/semanticcache/changelog.md
  • plugins/semanticcache/main.go
  • plugins/semanticcache/version
  • plugins/telemetry/changelog.md
  • plugins/telemetry/main.go
  • plugins/telemetry/version
  • transports/bifrost-http/handlers/cache.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/devpprof.go
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/handlers/health.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/handlers/logging.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/handlers/mcpserver.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • transports/bifrost-http/handlers/plugins.go
  • transports/bifrost-http/handlers/providers.go
  • transports/bifrost-http/handlers/session.go
  • transports/bifrost-http/handlers/ui.go
  • transports/bifrost-http/handlers/websocket.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/lib/config_test.go
  • transports/bifrost-http/lib/middleware.go
  • transports/bifrost-http/server/server.go
  • transports/changelog.md
  • transports/version
  • ui/app/clientLayout.tsx
  • ui/app/workspace/logs/views/logDetailsSheet.tsx
  • ui/components/devProfiler.tsx
  • ui/components/sidebar.tsx
  • ui/components/ui/sheet.tsx
  • ui/components/ui/switch.tsx
  • ui/lib/store/apis/devApi.ts
  • ui/lib/store/apis/index.ts
💤 Files with no reviewable changes (3)
  • core/mcp.go
  • plugins/otel/ttlsyncmap.go
  • framework/configstore/tables/mcp.go
✅ Files skipped from review due to trivial changes (2)
  • framework/version
  • plugins/governance/version
🚧 Files skipped from review as they are similar to previous changes (35)
  • transports/bifrost-http/handlers/plugins.go
  • plugins/semanticcache/changelog.md
  • ui/components/ui/switch.tsx
  • examples/plugins/hello-world/go.mod
  • plugins/mocker/version
  • docs/plugins/getting-started.mdx
  • .github/workflows/scripts/push-mintlify-changelog.sh
  • plugins/mocker/main.go
  • ui/components/ui/sheet.tsx
  • core/version
  • plugins/logging/utils.go
  • plugins/governance/go.mod
  • core/bifrost_test.go
  • transports/bifrost-http/handlers/governance.go
  • plugins/semanticcache/main.go
  • core/schemas/context.go
  • .github/workflows/scripts/release-bifrost-http.sh
  • transports/bifrost-http/handlers/mcp.go
  • docs/plugins/migration-guide.mdx
  • transports/bifrost-http/handlers/websocket.go
  • plugins/logging/changelog.md
  • plugins/telemetry/version
  • transports/bifrost-http/handlers/providers.go
  • plugins/maxim/main.go
  • transports/version
  • examples/plugins/hello-world/main.go
  • transports/bifrost-http/handlers/logging.go
  • transports/bifrost-http/lib/config.go
  • plugins/semanticcache/version
  • framework/streaming/transcription.go
  • plugins/jsonparser/main.go
  • docs/docs.json
  • plugins/otel/version
  • framework/streaming/audio.go
  • plugins/logging/version
🧰 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:

  • framework/streaming/responses.go
  • ui/lib/store/apis/index.ts
  • plugins/mocker/changelog.md
  • transports/bifrost-http/handlers/ui.go
  • transports/bifrost-http/lib/middleware.go
  • transports/bifrost-http/handlers/health.go
  • framework/streaming/types.go
  • plugins/maxim/version
  • plugins/jsonparser/changelog.md
  • framework/plugins/dynamicplugin_test.go
  • transports/bifrost-http/integrations/router.go
  • framework/changelog.md
  • core/providers/utils/utils.go
  • plugins/otel/changelog.md
  • ui/lib/store/apis/devApi.ts
  • plugins/telemetry/changelog.md
  • docs/plugins/writing-plugin.mdx
  • plugins/governance/main.go
  • plugins/telemetry/main.go
  • transports/bifrost-http/handlers/cache.go
  • ui/components/devProfiler.tsx
  • transports/changelog.md
  • transports/bifrost-http/handlers/config.go
  • core/schemas/bifrost.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • transports/bifrost-http/handlers/integrations.go
  • ui/components/sidebar.tsx
  • framework/tracing/helpers.go
  • plugins/governance/changelog.md
  • plugins/otel/main.go
  • framework/tracing/store.go
  • framework/plugins/dynamicplugin.go
  • core/schemas/tracer.go
  • transports/bifrost-http/handlers/inference.go
  • core/bifrost.go
  • transports/bifrost-http/handlers/session.go
  • plugins/otel/converter.go
  • transports/bifrost-http/server/server.go
  • framework/streaming/accumulator.go
  • core/schemas/plugin.go
  • framework/tracing/propagation.go
  • transports/bifrost-http/lib/config_test.go
  • ui/app/workspace/logs/views/logDetailsSheet.tsx
  • plugins/logging/main.go
  • core/schemas/trace.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/handlers/mcpserver.go
  • plugins/maxim/changelog.md
  • framework/streaming/chat.go
  • framework/tracing/llmspan.go
  • framework/tracing/tracer.go
  • ui/app/clientLayout.tsx
  • transports/bifrost-http/handlers/devpprof.go
  • core/changelog.md
  • plugins/jsonparser/version
🧠 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:

  • framework/streaming/responses.go
  • transports/bifrost-http/handlers/ui.go
  • transports/bifrost-http/lib/middleware.go
  • transports/bifrost-http/handlers/health.go
  • framework/streaming/types.go
  • framework/plugins/dynamicplugin_test.go
  • transports/bifrost-http/integrations/router.go
  • core/providers/utils/utils.go
  • plugins/governance/main.go
  • plugins/telemetry/main.go
  • transports/bifrost-http/handlers/cache.go
  • transports/bifrost-http/handlers/config.go
  • core/schemas/bifrost.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • transports/bifrost-http/handlers/integrations.go
  • framework/tracing/helpers.go
  • plugins/otel/main.go
  • framework/tracing/store.go
  • framework/plugins/dynamicplugin.go
  • core/schemas/tracer.go
  • transports/bifrost-http/handlers/inference.go
  • core/bifrost.go
  • transports/bifrost-http/handlers/session.go
  • plugins/otel/converter.go
  • transports/bifrost-http/server/server.go
  • framework/streaming/accumulator.go
  • core/schemas/plugin.go
  • framework/tracing/propagation.go
  • transports/bifrost-http/lib/config_test.go
  • plugins/logging/main.go
  • core/schemas/trace.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/handlers/mcpserver.go
  • framework/streaming/chat.go
  • framework/tracing/llmspan.go
  • framework/tracing/tracer.go
  • transports/bifrost-http/handlers/devpprof.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/ui.go
  • transports/bifrost-http/lib/middleware.go
  • transports/bifrost-http/handlers/health.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/handlers/cache.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/handlers/session.go
  • transports/bifrost-http/server/server.go
  • transports/bifrost-http/lib/config_test.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/handlers/mcpserver.go
  • transports/bifrost-http/handlers/devpprof.go
📚 Learning: 2025-12-19T09:26:54.961Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/providers/utils/utils.go:1050-1051
Timestamp: 2025-12-19T09:26:54.961Z
Learning: Update streaming end-marker handling so HuggingFace is treated as a non-[DONE] provider for backends that do not emit a DONE marker (e.g., meta llama on novita). In core/providers/utils/utils.go, adjust ProviderSendsDoneMarker() (or related logic) to detect providers that may not emit DONE and avoid relying on DONE as the sole end signal. Add tests to cover both DONE-emitting and non-DONE backends, with clear documentation in code comments explaining the rationale and any fallback behavior.

Applied to files:

  • core/providers/utils/utils.go
📚 Learning: 2025-12-22T10:50:40.990Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1154
File: plugins/governance/store.go:1165-1186
Timestamp: 2025-12-22T10:50:40.990Z
Learning: In the Bifrost governance plugin, budgets and rate limits have 1:1 relationships with their parent entities (virtual keys, teams, customers). Do not assume sharing; ensure cascade deletion logic only deletes budgets/rate limits when there are no shared references. Enforce invariants in code and add tests to verify no cross-entity sharing and that cascade deletes only remove the specific child of the parent. If a counterexample arises, adjust data model or add guards.

Applied to files:

  • plugins/governance/main.go
📚 Learning: 2025-12-24T10:55:31.424Z
Learnt from: akshaydeo
Repo: maximhq/bifrost PR: 888
File: framework/tracing/propagation.go:24-36
Timestamp: 2025-12-24T10:55:31.424Z
Learning: In framework/tracing/propagation.go, ensure ExtractParentID() returns ctx.TraceID (not ctx.ParentID) because the parent request's traceparent header carries the trace ID used for continuing the trace.

Applied to files:

  • framework/tracing/propagation.go
🧬 Code graph analysis (31)
framework/streaming/responses.go (1)
framework/streaming/types.go (1)
  • AccumulatedData (28-48)
transports/bifrost-http/handlers/ui.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/lib/middleware.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/health.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
framework/plugins/dynamicplugin_test.go (1)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-27)
transports/bifrost-http/integrations/router.go (2)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
core/schemas/bifrost.go (2)
  • BifrostContextKeyDeferTraceCompletion (147-147)
  • BifrostContextKeyTraceCompleter (148-148)
plugins/governance/main.go (3)
core/utils.go (1)
  • Ptr (56-58)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-27)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
plugins/telemetry/main.go (2)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-27)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/cache.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
ui/components/devProfiler.tsx (3)
npx/bin.js (3)
  • k (178-178)
  • sizes (179-179)
  • data (172-172)
ui/lib/utils/port.ts (1)
  • isDevelopmentMode (110-112)
ui/components/ui/tooltip.tsx (1)
  • Tooltip (43-43)
transports/bifrost-http/handlers/config.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
core/schemas/bifrost.go (2)
core/schemas/tracer.go (1)
  • Tracer (38-116)
framework/tracing/tracer.go (1)
  • Tracer (17-20)
transports/bifrost-http/handlers/middlewares_test.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/integrations.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
framework/tracing/helpers.go (3)
core/schemas/bifrost.go (1)
  • BifrostContextKeyTraceID (143-143)
framework/tracing/store.go (1)
  • TraceStore (25-37)
core/schemas/trace.go (5)
  • Trace (10-19)
  • SpanKind (115-115)
  • Span (52-65)
  • SpanStatus (143-143)
  • SpanEvent (107-111)
plugins/otel/main.go (2)
core/schemas/plugin.go (2)
  • BifrostHTTPMiddleware (38-38)
  • ObservabilityPlugin (123-137)
core/schemas/trace.go (1)
  • Trace (10-19)
framework/tracing/store.go (3)
core/schemas/tracer.go (1)
  • Tracer (38-116)
core/schemas/bifrost.go (1)
  • BifrostResponse (335-355)
core/schemas/trace.go (6)
  • Trace (10-19)
  • Span (52-65)
  • SpanEvent (107-111)
  • SpanKind (115-115)
  • SpanStatusUnset (147-147)
  • SpanStatus (143-143)
core/schemas/tracer.go (3)
core/schemas/bifrost.go (4)
  • ModelProvider (33-33)
  • BifrostError (474-483)
  • BifrostRequest (173-193)
  • BifrostResponse (335-355)
core/schemas/chatcompletions.go (1)
  • BifrostLLMUsage (845-852)
core/schemas/trace.go (2)
  • SpanKind (115-115)
  • SpanStatus (143-143)
transports/bifrost-http/handlers/inference.go (2)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
core/schemas/bifrost.go (2)
  • BifrostContextKeyDeferTraceCompletion (147-147)
  • BifrostContextKeyTraceCompleter (148-148)
core/bifrost.go (6)
core/schemas/tracer.go (3)
  • Tracer (38-116)
  • DefaultTracer (181-183)
  • SpanHandle (11-11)
framework/tracing/tracer.go (1)
  • Tracer (17-20)
core/schemas/bifrost.go (10)
  • ListModelsRequest (92-92)
  • BifrostRequest (173-193)
  • BifrostContextKeyTracer (146-146)
  • BifrostContextKeyStreamStartTime (145-145)
  • BifrostContextKeyTraceID (143-143)
  • BifrostResponse (335-355)
  • RequestType (89-89)
  • BifrostContextKeyPostHookSpanFinalizer (149-149)
  • BifrostError (474-483)
  • BifrostContextKeySpanID (144-144)
framework/tracing/helpers.go (1)
  • EndSpan (50-56)
core/utils.go (1)
  • IsStreamRequestType (197-199)
core/schemas/context.go (1)
  • NewBifrostContextWithTimeout (70-73)
transports/bifrost-http/handlers/session.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
plugins/otel/converter.go (2)
plugins/otel/main.go (1)
  • OtelPlugin (58-75)
core/schemas/trace.go (15)
  • Trace (10-19)
  • Span (52-65)
  • SpanKind (115-115)
  • SpanKindLLMCall (121-121)
  • SpanKindInternal (139-139)
  • SpanKindRetry (127-127)
  • SpanKindFallback (129-129)
  • SpanKindMCPTool (125-125)
  • SpanKindEmbedding (133-133)
  • SpanKindSpeech (135-135)
  • SpanKindTranscription (137-137)
  • SpanStatus (143-143)
  • SpanStatusOk (149-149)
  • SpanStatusError (151-151)
  • SpanEvent (107-111)
framework/streaming/accumulator.go (1)
framework/streaming/types.go (3)
  • StreamAccumulator (103-115)
  • ChatStreamChunk (77-87)
  • ResponsesStreamChunk (90-100)
transports/bifrost-http/lib/config_test.go (1)
transports/bifrost-http/lib/config.go (1)
  • LoadConfig (276-356)
ui/app/workspace/logs/views/logDetailsSheet.tsx (4)
ui/components/ui/sheet.tsx (1)
  • SheetContent (137-137)
ui/components/ui/alertDialog.tsx (9)
  • AlertDialog (83-83)
  • AlertDialogTrigger (93-93)
  • AlertDialogContent (86-86)
  • AlertDialogHeader (89-89)
  • AlertDialogTitle (92-92)
  • AlertDialogDescription (87-87)
  • AlertDialogFooter (88-88)
  • AlertDialogCancel (85-85)
  • AlertDialogAction (84-84)
ui/components/ui/dropdownMenu.tsx (5)
  • DropdownMenu (193-193)
  • DropdownMenuTrigger (207-207)
  • DropdownMenuContent (195-195)
  • DropdownMenuItem (197-197)
  • DropdownMenuSeparator (202-202)
ui/components/ui/button.tsx (1)
  • Button (70-70)
plugins/logging/main.go (3)
core/schemas/bifrost.go (3)
  • RequestType (89-89)
  • BifrostContextKeyTracer (146-146)
  • BifrostContextKeyTraceID (143-143)
core/schemas/tracer.go (1)
  • Tracer (38-116)
framework/tracing/tracer.go (1)
  • Tracer (17-20)
core/schemas/trace.go (1)
framework/tracing/helpers.go (1)
  • AddSpan (32-38)
transports/bifrost-http/handlers/middlewares.go (6)
core/schemas/plugin.go (3)
  • BifrostHTTPMiddleware (38-38)
  • ObservabilityPlugin (123-137)
  • Plugin (71-97)
transports/bifrost-http/lib/middleware.go (1)
  • ChainMiddlewares (11-23)
framework/tracing/tracer.go (1)
  • Tracer (17-20)
framework/tracing/propagation.go (1)
  • ExtractParentID (74-84)
core/schemas/bifrost.go (4)
  • BifrostContextKeyTraceID (143-143)
  • BifrostContextKeyTraceCompleter (148-148)
  • BifrostContextKeySpanID (144-144)
  • BifrostContextKeyDeferTraceCompletion (147-147)
core/schemas/trace.go (3)
  • SpanKindHTTPRequest (131-131)
  • SpanStatusError (151-151)
  • SpanStatusOk (149-149)
transports/bifrost-http/handlers/mcpserver.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
framework/streaming/chat.go (1)
framework/streaming/types.go (1)
  • AccumulatedData (28-48)
ui/app/clientLayout.tsx (1)
ui/components/devProfiler.tsx (1)
  • DevProfiler (55-422)
🪛 markdownlint-cli2 (0.18.1)
transports/changelog.md

16-16: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

Comment thread plugins/logging/main.go
Comment thread transports/bifrost-http/handlers/devpprof.go
Comment thread transports/bifrost-http/handlers/devpprof.go Outdated
Comment thread transports/bifrost-http/handlers/devpprof.go
Comment thread transports/bifrost-http/server/server.go
@akshaydeo akshaydeo force-pushed the 11-19-plugins_v2_architecture branch from 3f130fd to e8ca91d Compare December 25, 2025 08:08
Copy link
Copy Markdown
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: 8

♻️ Duplicate comments (18)
framework/tracing/llmspan.go (1)

190-194: Namespace ExtraParams keys to avoid clobbering standard trace attributes

Across the various Populate*RequestAttributes helpers, ExtraParams is currently merged into attrs with user-supplied keys (e.g., attrs[k] = fmt.Sprintf("%v", v)). That means a caller can accidentally overwrite standard keys like gen_ai.request.model, gen_ai.usage.*, etc., by choosing the same key name in ExtraParams.

To keep the core attribute namespace stable while still exposing user data, consider:

  • Prefixing all extra keys (for example, attrs["gen_ai.extra."+k] = fmt.Sprintf("%v", v)), and
  • Applying this consistently in all the ExtraParams loops (chat, text, embedding, responses, batch, and file operations).

Optionally, you can also guard against duplicate prefixed keys if you want to avoid overwriting on collision.

Also applies to: 298-301, 365-368, 575-577, 708-710, 739-741, 753-756, 768-771, 783-786, 958-961, 982-985, 997-1000, 1012-1015, 1027-1030

core/schemas/trace.go (1)

40-49: Guard Trace.Reset, Span.End, and Span.Reset with their mutexes to avoid data races

These methods mutate shared state (Trace fields and Span timing/status/collections) without acquiring the corresponding mutex, while other methods (AddSpan, SetAttribute, AddEvent) do take the lock. If traces/spans are used concurrently (which tracing callers commonly do), this will lead to data races when spans are ended or traces/spans are reset for pool reuse.

Consider wrapping each of these in the appropriate lock/unlock:

  • Trace.Reset: lock t.mu for the duration of the field/slice resets.
  • Span.End: lock s.mu while setting EndTime, Status, and StatusMsg.
  • Span.Reset: lock s.mu while clearing fields and truncating Events.

This keeps the concurrency model consistent with the rest of the API and avoids subtle pool-related races.

Proposed change
 func (t *Trace) Reset() {
-	t.TraceID = ""
-	t.ParentID = ""
-	t.RootSpan = nil
-	t.Spans = t.Spans[:0]
-	t.StartTime = time.Time{}
-	t.EndTime = time.Time{}
-	t.Attributes = nil
+	t.mu.Lock()
+	defer t.mu.Unlock()
+	t.TraceID = ""
+	t.ParentID = ""
+	t.RootSpan = nil
+	t.Spans = t.Spans[:0]
+	t.StartTime = time.Time{}
+	t.EndTime = time.Time{}
+	t.Attributes = nil
 }

 // End marks the span as complete with the given status
 func (s *Span) End(status SpanStatus, statusMsg string) {
-	s.EndTime = time.Now()
-	s.Status = status
-	s.StatusMsg = statusMsg
+	s.mu.Lock()
+	defer s.mu.Unlock()
+	s.EndTime = time.Now()
+	s.Status = status
+	s.StatusMsg = statusMsg
 }

 // Reset clears the span for reuse from pool
 func (s *Span) Reset() {
-	s.SpanID = ""
-	s.ParentID = ""
-	s.TraceID = ""
-	s.Name = ""
-	s.Kind = SpanKindUnspecified
-	s.StartTime = time.Time{}
-	s.EndTime = time.Time{}
-	s.Status = SpanStatusUnset
-	s.StatusMsg = ""
-	s.Attributes = nil
-	s.Events = s.Events[:0]
+	s.mu.Lock()
+	defer s.mu.Unlock()
+	s.SpanID = ""
+	s.ParentID = ""
+	s.TraceID = ""
+	s.Name = ""
+	s.Kind = SpanKindUnspecified
+	s.StartTime = time.Time{}
+	s.EndTime = time.Time{}
+	s.Status = SpanStatusUnset
+	s.StatusMsg = ""
+	s.Attributes = nil
+	s.Events = s.Events[:0]
 }

Also applies to: 84-104

docs/plugins/migration-guide.mdx (1)

247-250: Verify v1.4.0 tag will exist at merge time.

The go get command references @v1.4.0, but this tag may not exist in the repository yet. Ensure the v1.4.0 tag is created before merging this PR, or update the documentation to use an existing version tag, a specific commit hash, or add a note indicating this is for an upcoming release.

Run this script to verify the tag status:

#!/bin/bash
# Check if v1.4.0 tag exists and show latest tags
git tag -l "v1.4.0"
echo "---"
echo "Latest tags:"
git tag -l --sort=-version:refname | head -10
transports/changelog.md (1)

16-16: Add language specifier to code fence.

The fenced code block is missing a language identifier, which prevents syntax highlighting and violates markdownlint rule MD040.

🔎 Proposed fix
   **Migration summary:**
-  ```
+  ```go
   // v1.3.x (removed)
   TransportInterceptor(ctx *BifrostContext, url string, headers map[string]string, body map[string]any) (map[string]string, map[string]any, error)
examples/plugins/hello-world/main.go (1)

19-27: HTTPTransportMiddleware sets fasthttp context value but PreHook/PostHook read from BifrostContext.

The middleware on line 23 sets ctx.SetUserValue(...) on fasthttp.RequestCtx, but PreHook (line 30) and PostHook (lines 39-40) attempt to read from *schemas.BifrostContext. These are separate context types, so the value will never propagate. This breaks the example's demonstration of data flow between hooks and middleware.

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

514-514: Non-streaming handlers pass fasthttp.RequestCtx instead of bifrostCtx to Bifrost client.

All non-streaming client calls (lines 514, 534, 557, 580, 603, 626, 660) pass ctx (the *fasthttp.RequestCtx) instead of the converted *bifrostCtx. This breaks tracing, plugin context propagation, and may cause type errors if the client expects context.Context. The comment on line 503 even says "Use the cancellable context from ConvertToBifrostContext" but the code doesn't follow through.

Also applies to: 534-534, 557-557, 580-580, 603-603, 626-626, 660-660

framework/plugins/dynamicplugin.go (1)

140-147: Type mismatch: HTTPTransportMiddleware symbol is a factory function, not the middleware itself.

Line 145 attempts to cast the symbol directly to schemas.BifrostHTTPMiddleware, but the example plugin at examples/plugins/hello-world/main.go (lines 19-27) exports HTTPTransportMiddleware as a factory function:

func HTTPTransportMiddleware() schemas.BifrostHTTPMiddleware

The type assertion will fail at runtime. The symbol should be cast to func() schemas.BifrostHTTPMiddleware, then called to obtain the middleware.

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

1428-1428: Pass context by value, not by pointer.

The function signature completeDeferredSpan(ctx *context.Context, ...) violates Go conventions. Contexts should always be passed by value. The context.Context type is designed to be passed by value and is safe for concurrent use.

plugins/logging/main.go (1)

225-233: Revisit PreHook’s tracer-based accumulator creation; likely dead with current tracer injection order.

In PreHook you gate CreateStreamAccumulator on:

if bifrost.IsStreamRequestType(req.RequestType) {
    if tracer, ok := ctx.Value(schemas.BifrostContextKeyTracer).(schemas.Tracer); ok && tracer != nil {
        if traceID, ok := ctx.Value(schemas.BifrostContextKeyTraceID).(string); ok && traceID != "" {
            tracer.CreateStreamAccumulator(traceID, createdTimestamp)
        }
    }
}

But BifrostContextKeyTracer is injected by core inside executeRequestWithRetries, which runs on the worker side after plugin PreHooks have already executed. Unless another component sets BifrostContextKeyTracer earlier, this condition will almost never be true in the normal HTTP→Bifrost path, so the PreHook accumulator creation is effectively dead code.

Given that:

  • streaming aggregation now flows through tracer.ProcessStreamingChunk in PostHook, and
  • cleanup is explicitly delegated to the tracing middleware/tracer,

it would be cleaner and less confusing to either:

  • remove the PreHook CreateStreamAccumulator block and let the tracer lazily create accumulators inside ProcessStreamingChunk, or
  • move tracer injection earlier (before RunPreHooks) if you really want PreHook to own creation.

Right now the lifecycle is split in a way that’s hard to reason about and easy to drift.

Also applies to: 382-471, 615-625

plugins/governance/main.go (2)

233-261: Validate x-bf-vk with VirtualKeyPrefix like the other headers.

parseVirtualKey currently returns the x-bf-vk header verbatim without checking that it starts with VirtualKeyPrefix, while Authorization, x-api-key, and x-goog-api-key all enforce that prefix. This inconsistency allows malformed or non-virtual keys via x-bf-vk to bypass the basic format check.

Consider normalizing and validating x-bf-vk similarly before returning:

Suggested adjustment
 func parseVirtualKey(ctx *fasthttp.RequestCtx) *string {
 	var virtualKeyValue string
 	vkHeader := ctx.Request.Header.Peek("x-bf-vk")
-	if string(vkHeader) != "" {
-		return bifrost.Ptr(string(vkHeader))
-	}
+	if raw := strings.TrimSpace(string(vkHeader)); raw != "" {
+		if strings.HasPrefix(strings.ToLower(raw), VirtualKeyPrefix) {
+			return bifrost.Ptr(raw)
+		}
+	}

This keeps all header paths aligned and avoids treating x-bf-vk as a privileged bypass.


291-307: Fix log message: this block is doing unmarshal, not marshal.

In the HTTP middleware body handling, the error log on sonic.Unmarshal still says "failed to marshal request body", which is misleading for debugging.

Suggested fix
-	err = sonic.Unmarshal(ctx.Request.Body(), &payload)
-	if err != nil {
-		p.logger.Error("failed to marshal request body to check for virtual key: %v", err)
+	err = sonic.Unmarshal(ctx.Request.Body(), &payload)
+	if err != nil {
+		p.logger.Error("failed to unmarshal request body to check for virtual key: %v", err)
core/bifrost.go (1)

70-71: Harden tracer storage/access to avoid panics from nil or unexpected values.

tracer is stored in an atomic.Value as *tracerWrapper, but both getTracer() and Shutdown() unconditionally type‑assert:

return bifrost.tracer.Load().(*tracerWrapper).tracer
// ...
if tracerWrapper := bifrost.tracer.Load().(*tracerWrapper); tracerWrapper != nil && tracerWrapper.tracer != nil {
    tracerWrapper.tracer.Stop()
}

If Load() ever returns nil or a non‑*tracerWrapper (e.g., a misconfigured test or external code constructing Bifrost without calling Init properly), these assertions will panic. Earlier feedback on tracer storage raised a similar concern.

Consider adding defensive guards both when setting and when reading:

  • Ensure SetTracer always stores a non‑nil *tracerWrapper.
  • Make getTracer and Shutdown handle Load()==nil or wrong types by falling back to schemas.DefaultTracer() (for reads) and skipping Stop() (for shutdown), optionally with a debug/warn log.
Illustrative adjustment
func (bifrost *Bifrost) SetTracer(tracer schemas.Tracer) {
    if tracer == nil {
        tracer = schemas.DefaultTracer()
    }
-   bifrost.tracer.Store(&tracerWrapper{tracer: tracer})
+   bifrost.tracer.Store(&tracerWrapper{tracer: tracer})
}

func (bifrost *Bifrost) getTracer() schemas.Tracer {
-   return bifrost.tracer.Load().(*tracerWrapper).tracer
+   if v := bifrost.tracer.Load(); v != nil {
+       if tw, ok := v.(*tracerWrapper); ok && tw != nil && tw.tracer != nil {
+           return tw.tracer
+       }
+   }
+   return schemas.DefaultTracer()
}

func (bifrost *Bifrost) Shutdown() {
    // ...
-   if tracerWrapper := bifrost.tracer.Load().(*tracerWrapper); tracerWrapper != nil && tracerWrapper.tracer != nil {
-       tracerWrapper.tracer.Stop()
-   }
+   if v := bifrost.tracer.Load(); v != nil {
+       if tw, ok := v.(*tracerWrapper); ok && tw != nil && tw.tracer != nil {
+           tw.tracer.Stop()
+       }
+   }
    // ...
}

This keeps the wrapper approach while making the public API resilient to accidental misuse and future refactors around tracer initialization.

Also applies to: 102-107, 118-147, 253-265, 3893-3896

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

142-150: Stop() can panic if called twice due to closing an already-closed channel.

The Stop() method closes c.stopCh without protection against double-close. If Stop() is called twice, it will panic. Additionally, Start() after Stop() will fail because stopCh remains closed.

🔎 Proposed fix using select to guard the close
 func (c *MetricsCollector) Stop() {
 	c.mu.Lock()
 	defer c.mu.Unlock()
-	if c.started {
+	if c.started && c.stopCh != nil {
+		select {
+		case <-c.stopCh:
+			// Already closed
+		default:
 		close(c.stopCh)
+		}
 		c.started = false
+		c.stopCh = nil
 	}
 }

176-190: getCPUSample uses Unix-only syscall.Getrusage and will not compile on Windows.

syscall.Getrusage is only available on Unix platforms. Since the project supports Windows (per platform-specific handling in server.go), this file needs a build constraint or platform-specific implementation.

Add a build constraint at the top of the file:

//go:build !windows
// +build !windows

Or create devpprof_unix.go and devpprof_windows.go with appropriate stubs.


370-376: devPprofHandler is created locally and never cleaned up, causing goroutine leaks.

In server.go (line 1022), devPprofHandler is instantiated as a local variable and RegisterRoutes starts the collector (line 373). Since the handler isn't stored on BifrostHTTPServer, its Cleanup() method is never called during shutdown, leaving the collector goroutine running indefinitely.

Store devPprofHandler as a field in BifrostHTTPServer and call Cleanup() in the shutdown sequence.

framework/tracing/store.go (1)

13-22: Remove unused Tracer and RequestID fields from DeferredSpanInfo.

The Tracer (line 17) and RequestID (line 18) fields are never assigned or used. StoreDeferredSpan() (lines 120-125) only initializes SpanID and StartTime. In contrast, FirstChunkTime and AccumulatedChunks are actively used in AppendStreamingChunk().

🔎 Proposed fix
 type DeferredSpanInfo struct {
 	SpanID             string
 	StartTime          time.Time
-	Tracer             schemas.Tracer // Reference to tracer for completing the span
-	RequestID          string         // Request ID for accumulator lookup
 	FirstChunkTime     time.Time      // Timestamp of first chunk (for TTFT calculation)
 	AccumulatedChunks  []*schemas.BifrostResponse // Accumulated streaming chunks
 	mu                 sync.Mutex     // Mutex for thread-safe chunk accumulation
 }
transports/bifrost-http/server/server.go (1)

959-959: Use s.GetGovernancePluginName() instead of hardcoded governance.PluginName.

Line 959 uses governance.PluginName directly, bypassing the enterprise plugin name override mechanism. Lines 510 and 657 correctly use s.GetGovernancePluginName() for the same operation.

🔎 Proposed fix
-	governancePlugin, _ := FindPluginByName[schemas.Plugin](s.Plugins, governance.PluginName)
+	governancePlugin, _ := FindPluginByName[schemas.Plugin](s.Plugins, s.GetGovernancePluginName())
framework/tracing/tracer.go (1)

393-398: Choice and tool-call indices are not sorted, causing nondeterministic output.

The comments say "Sort choice indices for deterministic output" (line 394) and "Sort tool calls by index" (line 433), but no actual sorting is performed. The iteration order over maps in Go is nondeterministic, so the output order will vary between runs.

🔎 Proposed fix - add sorting
+import "sort"

 // Build final choices from accumulated data
 // Sort choice indices for deterministic output
 choiceIndices := make([]int, 0, len(choiceMap))
 for idx := range choiceMap {
     choiceIndices = append(choiceIndices, idx)
 }
+sort.Ints(choiceIndices)

 // ... later ...

 // Sort tool calls by index
 tcIndices := make([]int, 0, len(accum.toolCalls))
 for tcIdx := range accum.toolCalls {
     tcIndices = append(tcIndices, tcIdx)
 }
+sort.Ints(tcIndices)

Also applies to: 433-437

🧹 Nitpick comments (5)
.github/workflows/scripts/release-bifrost-http.sh (1)

266-266: Consider sourcing the PostgreSQL password dynamically.

The password bifrost_password is hardcoded in the docker exec command. While this is acceptable for test environments, it creates a maintenance burden if the password in docker-compose.yml changes.

🔎 Suggested improvement

Extract the password from the docker-compose file or environment:

+# Extract PostgreSQL password from docker-compose or environment
+POSTGRES_PASSWORD="${POSTGRES_PASSWORD:-bifrost_password}"
+
 docker exec -e PGPASSWORD=bifrost_password "$(docker compose -f "$CONFIGS_DIR/docker-compose.yml" ps -q postgres)" \

Or parse it from the compose file:

# Extract from docker-compose.yml
POSTGRES_PASSWORD=$(grep -A 5 'POSTGRES_PASSWORD' "$CONFIGS_DIR/docker-compose.yml" | grep -oP ':\s*\K\S+' | head -1)
POSTGRES_PASSWORD="${POSTGRES_PASSWORD:-bifrost_password}"
framework/streaming/accumulator.go (1)

104-127: Clarify StartTimestamp semantics and consider trimming redundant IsZero checks

createStreamAccumulator now always initializes StartTimestamp to time.Now(), while the add*StreamChunk methods still conditionally set StartTimestamp when it’s zero and separately track FirstChunkTimestamp for TTFT. In normal flows where accumulators are always created via createStreamAccumulator/CreateStreamAccumulator, those StartTimestamp.IsZero() branches will rarely, if ever, fire.

If accumulators aren’t constructed elsewhere with a zero StartTimestamp, consider either:

  • Removing the StartTimestamp.IsZero() blocks in the add*StreamChunk methods, or
  • Updating the comment above createStreamAccumulator to explicitly describe the “default to now, optionally overridden by CreateStreamAccumulator” behavior and that per-chunk code won’t back-fill StartTimestamp anymore.

This would make the TTFT/latency behavior easier to reason about and avoid dead code paths.

Also applies to: 129-223

framework/tracing/propagation.go (1)

72-84: Clarify ExtractParentID docstring to match what’s returned

The implementation intentionally returns the trace-id component (ctx.TraceID) from the incoming traceparent, not the parent/span ID. The current comment (“extracts the parent trace ID from W3C traceparent header”) can be read as span‑ID semantics; consider rephrasing to something like “extracts the trace ID from the parent request’s W3C traceparent header” to avoid confusion.

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

281-307: completeAndFlushTrace runs in a goroutine without error aggregation or timeout.

The trace completion and plugin injection runs asynchronously, which is good for not blocking the response. However:

  1. If plugin.Inject hangs, the goroutine will leak
  2. Errors are only logged, with no way to track injection failures across plugins

Consider adding a timeout context for plugin injection:

go func() {
    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()
    
    // ... existing cleanup code ...
    
    for _, plugin := range *m.obsPlugins.Load() {
        if plugin == nil {
            continue
        }
        if err := plugin.Inject(ctx, completedTrace); err != nil {
            logger.Warn("observability plugin %s failed to inject trace: %v", plugin.GetName(), err)
        }
    }
    // ...
}()
core/schemas/tracer.go (1)

9-11: SpanHandle as empty interface limits type safety.

Using interface{} for SpanHandle means any value can be passed, which could lead to runtime errors if incorrect types are used. This is intentional for flexibility across implementations but worth noting.

Consider documenting the expected handle structure or adding runtime type checks in implementations to fail fast on misuse. The current approach is acceptable given the interface-based design.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6fb3318 and 3f130fd.

⛔ Files ignored due to path filters (2)
  • examples/plugins/hello-world/go.sum is excluded by !**/*.sum
  • plugins/governance/go.sum is excluded by !**/*.sum
📒 Files selected for processing (96)
  • .github/workflows/release-pipeline.yml
  • .github/workflows/scripts/push-mintlify-changelog.sh
  • .github/workflows/scripts/release-bifrost-http.sh
  • core/bifrost.go
  • core/bifrost_test.go
  • core/changelog.md
  • core/mcp.go
  • core/providers/utils/utils.go
  • core/schemas/bifrost.go
  • core/schemas/context.go
  • core/schemas/plugin.go
  • core/schemas/trace.go
  • core/schemas/tracer.go
  • core/version
  • docs/docs.json
  • docs/plugins/getting-started.mdx
  • docs/plugins/migration-guide.mdx
  • docs/plugins/writing-plugin.mdx
  • examples/plugins/hello-world/go.mod
  • examples/plugins/hello-world/main.go
  • framework/changelog.md
  • framework/configstore/tables/mcp.go
  • framework/plugins/dynamicplugin.go
  • framework/plugins/dynamicplugin_test.go
  • framework/streaming/accumulator.go
  • framework/streaming/audio.go
  • framework/streaming/chat.go
  • framework/streaming/responses.go
  • framework/streaming/transcription.go
  • framework/streaming/types.go
  • framework/tracing/helpers.go
  • framework/tracing/llmspan.go
  • framework/tracing/propagation.go
  • framework/tracing/store.go
  • framework/tracing/tracer.go
  • framework/version
  • plugins/governance/changelog.md
  • plugins/governance/go.mod
  • plugins/governance/main.go
  • plugins/governance/version
  • plugins/jsonparser/changelog.md
  • plugins/jsonparser/main.go
  • plugins/jsonparser/version
  • plugins/logging/changelog.md
  • plugins/logging/main.go
  • plugins/logging/utils.go
  • plugins/logging/version
  • plugins/maxim/changelog.md
  • plugins/maxim/main.go
  • plugins/maxim/version
  • plugins/mocker/changelog.md
  • plugins/mocker/main.go
  • plugins/mocker/version
  • plugins/otel/changelog.md
  • plugins/otel/converter.go
  • plugins/otel/main.go
  • plugins/otel/ttlsyncmap.go
  • plugins/otel/version
  • plugins/semanticcache/changelog.md
  • plugins/semanticcache/main.go
  • plugins/semanticcache/version
  • plugins/telemetry/changelog.md
  • plugins/telemetry/main.go
  • plugins/telemetry/version
  • transports/bifrost-http/handlers/cache.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/devpprof.go
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/handlers/health.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/handlers/logging.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/handlers/mcpserver.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • transports/bifrost-http/handlers/plugins.go
  • transports/bifrost-http/handlers/providers.go
  • transports/bifrost-http/handlers/session.go
  • transports/bifrost-http/handlers/ui.go
  • transports/bifrost-http/handlers/websocket.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/lib/config_test.go
  • transports/bifrost-http/lib/middleware.go
  • transports/bifrost-http/server/server.go
  • transports/changelog.md
  • transports/version
  • ui/app/clientLayout.tsx
  • ui/app/workspace/logs/views/logDetailsSheet.tsx
  • ui/components/devProfiler.tsx
  • ui/components/sidebar.tsx
  • ui/components/ui/sheet.tsx
  • ui/components/ui/switch.tsx
  • ui/lib/store/apis/devApi.ts
  • ui/lib/store/apis/index.ts
💤 Files with no reviewable changes (3)
  • framework/configstore/tables/mcp.go
  • core/mcp.go
  • plugins/otel/ttlsyncmap.go
✅ Files skipped from review due to trivial changes (1)
  • plugins/telemetry/version
🚧 Files skipped from review as they are similar to previous changes (40)
  • framework/streaming/types.go
  • ui/lib/store/apis/index.ts
  • plugins/governance/version
  • transports/bifrost-http/handlers/ui.go
  • transports/version
  • plugins/otel/version
  • core/schemas/context.go
  • framework/streaming/audio.go
  • transports/bifrost-http/handlers/mcp.go
  • plugins/mocker/main.go
  • framework/version
  • plugins/logging/version
  • transports/bifrost-http/handlers/health.go
  • transports/bifrost-http/handlers/plugins.go
  • plugins/mocker/changelog.md
  • framework/plugins/dynamicplugin_test.go
  • .github/workflows/release-pipeline.yml
  • framework/streaming/responses.go
  • transports/bifrost-http/handlers/providers.go
  • plugins/maxim/changelog.md
  • .github/workflows/scripts/push-mintlify-changelog.sh
  • plugins/mocker/version
  • ui/lib/store/apis/devApi.ts
  • ui/components/ui/switch.tsx
  • plugins/maxim/main.go
  • core/schemas/plugin.go
  • ui/app/workspace/logs/views/logDetailsSheet.tsx
  • plugins/logging/utils.go
  • docs/plugins/writing-plugin.mdx
  • ui/components/ui/sheet.tsx
  • ui/app/clientLayout.tsx
  • transports/bifrost-http/handlers/websocket.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/cache.go
  • transports/bifrost-http/handlers/logging.go
  • plugins/jsonparser/version
  • framework/streaming/chat.go
  • docs/docs.json
  • plugins/telemetry/main.go
  • plugins/semanticcache/version
🧰 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/session.go
  • plugins/semanticcache/changelog.md
  • plugins/jsonparser/changelog.md
  • transports/bifrost-http/handlers/inference.go
  • plugins/governance/go.mod
  • transports/changelog.md
  • plugins/semanticcache/main.go
  • framework/tracing/propagation.go
  • transports/bifrost-http/lib/config_test.go
  • core/changelog.md
  • framework/plugins/dynamicplugin.go
  • plugins/logging/changelog.md
  • plugins/maxim/version
  • transports/bifrost-http/handlers/integrations.go
  • core/providers/utils/utils.go
  • docs/plugins/getting-started.mdx
  • transports/bifrost-http/lib/middleware.go
  • plugins/otel/changelog.md
  • framework/tracing/helpers.go
  • core/schemas/tracer.go
  • plugins/otel/main.go
  • transports/bifrost-http/integrations/router.go
  • plugins/telemetry/changelog.md
  • examples/plugins/hello-world/go.mod
  • transports/bifrost-http/handlers/devpprof.go
  • framework/tracing/store.go
  • core/schemas/trace.go
  • ui/components/sidebar.tsx
  • transports/bifrost-http/handlers/mcpserver.go
  • plugins/governance/changelog.md
  • transports/bifrost-http/handlers/governance.go
  • plugins/otel/converter.go
  • core/schemas/bifrost.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • examples/plugins/hello-world/main.go
  • framework/tracing/tracer.go
  • plugins/governance/main.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/handlers/middlewares.go
  • core/version
  • framework/tracing/llmspan.go
  • transports/bifrost-http/server/server.go
  • core/bifrost.go
  • plugins/logging/main.go
  • ui/components/devProfiler.tsx
  • framework/streaming/transcription.go
  • core/bifrost_test.go
  • docs/plugins/migration-guide.mdx
  • framework/changelog.md
  • framework/streaming/accumulator.go
  • plugins/jsonparser/main.go
🧠 Learnings (6)
📚 Learning: 2025-12-09T17:07:42.007Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/schemas/account.go:9-18
Timestamp: 2025-12-09T17:07:42.007Z
Learning: In core/schemas/account.go, the HuggingFaceKeyConfig field within the Key struct is currently unused and reserved for future Hugging Face inference endpoint deployments. Do not flag this field as missing from OpenAPI documentation or require its presence in the API spec until the feature is actively implemented and used. When the feature is added, update the OpenAPI docs accordingly; otherwise, treat this field as non-breaking and not part of the current API surface.

Applied to files:

  • transports/bifrost-http/handlers/session.go
  • transports/bifrost-http/handlers/inference.go
  • plugins/semanticcache/main.go
  • framework/tracing/propagation.go
  • transports/bifrost-http/lib/config_test.go
  • framework/plugins/dynamicplugin.go
  • transports/bifrost-http/handlers/integrations.go
  • core/providers/utils/utils.go
  • transports/bifrost-http/lib/middleware.go
  • framework/tracing/helpers.go
  • core/schemas/tracer.go
  • plugins/otel/main.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/handlers/devpprof.go
  • framework/tracing/store.go
  • core/schemas/trace.go
  • transports/bifrost-http/handlers/mcpserver.go
  • transports/bifrost-http/handlers/governance.go
  • plugins/otel/converter.go
  • core/schemas/bifrost.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • examples/plugins/hello-world/main.go
  • framework/tracing/tracer.go
  • plugins/governance/main.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/handlers/middlewares.go
  • framework/tracing/llmspan.go
  • transports/bifrost-http/server/server.go
  • core/bifrost.go
  • plugins/logging/main.go
  • framework/streaming/transcription.go
  • core/bifrost_test.go
  • framework/streaming/accumulator.go
  • plugins/jsonparser/main.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/session.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/lib/config_test.go
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/lib/middleware.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/handlers/devpprof.go
  • transports/bifrost-http/handlers/mcpserver.go
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/server/server.go
📚 Learning: 2025-12-24T10:55:31.424Z
Learnt from: akshaydeo
Repo: maximhq/bifrost PR: 888
File: framework/tracing/propagation.go:24-36
Timestamp: 2025-12-24T10:55:31.424Z
Learning: In framework/tracing/propagation.go, ensure ExtractParentID() returns ctx.TraceID (not ctx.ParentID) because the parent request's traceparent header carries the trace ID used for continuing the trace.

Applied to files:

  • framework/tracing/propagation.go
📚 Learning: 2025-12-19T09:26:54.961Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/providers/utils/utils.go:1050-1051
Timestamp: 2025-12-19T09:26:54.961Z
Learning: Update streaming end-marker handling so HuggingFace is treated as a non-[DONE] provider for backends that do not emit a DONE marker (e.g., meta llama on novita). In core/providers/utils/utils.go, adjust ProviderSendsDoneMarker() (or related logic) to detect providers that may not emit DONE and avoid relying on DONE as the sole end signal. Add tests to cover both DONE-emitting and non-DONE backends, with clear documentation in code comments explaining the rationale and any fallback behavior.

Applied to files:

  • core/providers/utils/utils.go
📚 Learning: 2025-12-24T04:36:57.698Z
Learnt from: danpiths
Repo: maximhq/bifrost PR: 1169
File: transports/bifrost-http/handlers/governance.go:1708-1878
Timestamp: 2025-12-24T04:36:57.698Z
Learning: In governance update handlers (e.g., updateModelConfig, updateProviderGovernance), design updates to support clearing individual fields by sending null/empty values (e.g., {"rate_limit": {"token_max_limit": null}} clears token_max_limit). Follow this pattern for partial updates so users can remove specific governance settings without deleting the whole entity. Ensure budget updates follow the same approach using direct field assignment. Review input validation, JSON decoding (e.g., pointers vs values in Go structs), and API documentation to reflect nullable fields and expected behavior.

Applied to files:

  • transports/bifrost-http/handlers/governance.go
📚 Learning: 2025-12-22T10:50:40.990Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1154
File: plugins/governance/store.go:1165-1186
Timestamp: 2025-12-22T10:50:40.990Z
Learning: In the Bifrost governance plugin, budgets and rate limits have 1:1 relationships with their parent entities (virtual keys, teams, customers). Do not assume sharing; ensure cascade deletion logic only deletes budgets/rate limits when there are no shared references. Enforce invariants in code and add tests to verify no cross-entity sharing and that cascade deletes only remove the specific child of the parent. If a counterexample arises, adjust data model or add guards.

Applied to files:

  • plugins/governance/main.go
🧬 Code graph analysis (26)
transports/bifrost-http/handlers/session.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/inference.go (2)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
core/schemas/bifrost.go (2)
  • BifrostContextKeyDeferTraceCompletion (147-147)
  • BifrostContextKeyTraceCompleter (148-148)
plugins/semanticcache/main.go (2)
core/schemas/plugin.go (2)
  • Plugin (71-97)
  • BifrostHTTPMiddleware (38-38)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-27)
transports/bifrost-http/lib/config_test.go (1)
transports/bifrost-http/lib/config.go (1)
  • LoadConfig (276-356)
framework/plugins/dynamicplugin.go (2)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
examples/plugins/hello-world/main.go (2)
  • GetName (15-17)
  • HTTPTransportMiddleware (19-27)
transports/bifrost-http/handlers/integrations.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/lib/middleware.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
framework/tracing/helpers.go (3)
core/schemas/bifrost.go (1)
  • BifrostContextKeyTraceID (143-143)
framework/tracing/store.go (1)
  • TraceStore (25-37)
core/schemas/trace.go (5)
  • Trace (10-19)
  • SpanKind (115-115)
  • Span (52-65)
  • SpanStatus (143-143)
  • SpanEvent (107-111)
transports/bifrost-http/integrations/router.go (2)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
core/schemas/bifrost.go (2)
  • BifrostContextKeyDeferTraceCompletion (147-147)
  • BifrostContextKeyTraceCompleter (148-148)
transports/bifrost-http/handlers/devpprof.go (4)
ui/lib/store/apis/devApi.ts (6)
  • MemoryStats (4-10)
  • CPUStats (13-17)
  • RuntimeStats (20-26)
  • AllocationInfo (29-35)
  • HistoryPoint (38-45)
  • PprofData (48-55)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/lib/middleware.go (1)
  • ChainMiddlewares (11-23)
transports/bifrost-http/handlers/utils.go (1)
  • SendJSON (16-22)
framework/tracing/store.go (3)
framework/tracing/tracer.go (1)
  • Tracer (17-20)
core/schemas/trace.go (6)
  • Trace (10-19)
  • Span (52-65)
  • SpanEvent (107-111)
  • SpanKind (115-115)
  • SpanStatusUnset (147-147)
  • SpanStatus (143-143)
framework/tracing/helpers.go (3)
  • GetTrace (23-29)
  • AddSpan (32-38)
  • EndSpan (50-56)
core/schemas/trace.go (2)
framework/tracing/helpers.go (1)
  • AddSpan (32-38)
ui/lib/constants/logs.ts (1)
  • Status (161-161)
transports/bifrost-http/handlers/mcpserver.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/governance.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
plugins/otel/converter.go (2)
plugins/otel/main.go (1)
  • OtelPlugin (58-75)
core/schemas/trace.go (15)
  • Trace (10-19)
  • Span (52-65)
  • SpanKind (115-115)
  • SpanKindLLMCall (121-121)
  • SpanKindHTTPRequest (131-131)
  • SpanKindPlugin (123-123)
  • SpanKindInternal (139-139)
  • SpanKindRetry (127-127)
  • SpanKindFallback (129-129)
  • SpanKindMCPTool (125-125)
  • SpanKindEmbedding (133-133)
  • SpanKindSpeech (135-135)
  • SpanKindTranscription (137-137)
  • SpanStatus (143-143)
  • SpanEvent (107-111)
core/schemas/bifrost.go (2)
core/schemas/tracer.go (1)
  • Tracer (38-116)
framework/tracing/tracer.go (1)
  • Tracer (17-20)
transports/bifrost-http/handlers/middlewares_test.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
examples/plugins/hello-world/main.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
framework/tracing/tracer.go (6)
framework/tracing/store.go (1)
  • TraceStore (25-37)
framework/streaming/accumulator.go (2)
  • Accumulator (14-30)
  • NewAccumulator (440-480)
core/schemas/tracer.go (3)
  • Tracer (38-116)
  • SpanHandle (11-11)
  • StreamAccumulatorResult (15-33)
framework/tracing/helpers.go (3)
  • GetTraceID (11-20)
  • EndSpan (50-56)
  • GetTrace (23-29)
core/schemas/bifrost.go (6)
  • BifrostContextKeySpanID (144-144)
  • BifrostRequest (173-193)
  • BifrostResponse (335-355)
  • BifrostError (474-483)
  • BifrostContextKeyRequestID (124-124)
  • BifrostContextKeyStreamEndIndicator (131-131)
core/schemas/context.go (2)
  • BifrostContext (32-42)
  • NewBifrostContext (47-65)
plugins/governance/main.go (3)
core/utils.go (1)
  • Ptr (56-58)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-27)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
framework/tracing/llmspan.go (3)
core/schemas/bifrost.go (18)
  • BifrostRequest (173-193)
  • RequestType (89-89)
  • TextCompletionRequest (93-93)
  • EmbeddingRequest (99-99)
  • TranscriptionRequest (102-102)
  • SpeechRequest (100-100)
  • ResponsesRequest (97-97)
  • BatchCreateRequest (104-104)
  • BatchListRequest (105-105)
  • BatchRetrieveRequest (106-106)
  • BatchCancelRequest (107-107)
  • BatchResultsRequest (108-108)
  • FileUploadRequest (109-109)
  • FileListRequest (110-110)
  • FileRetrieveRequest (111-111)
  • FileDeleteRequest (112-112)
  • FileContentRequest (113-113)
  • BifrostResponse (335-355)
core/schemas/trace.go (19)
  • AttrProviderName (159-159)
  • AttrRequestModel (160-160)
  • AttrError (216-216)
  • AttrErrorType (217-217)
  • AttrErrorCode (218-218)
  • AttrInputMessages (222-222)
  • AttrResponseID (191-191)
  • AttrResponseModel (192-192)
  • AttrObject (197-197)
  • AttrSystemFprint (194-194)
  • AttrCreated (196-196)
  • AttrServiceTier (195-195)
  • AttrOutputMessages (225-225)
  • AttrFinishReason (193-193)
  • AttrPromptTokens (208-208)
  • AttrCompletionTokens (209-209)
  • AttrTotalTokens (210-210)
  • AttrPrompt (181-181)
  • AttrUsageCost (213-213)
core/schemas/chatcompletions.go (1)
  • BifrostChatRequest (12-19)
core/bifrost.go (6)
core/schemas/tracer.go (3)
  • Tracer (38-116)
  • DefaultTracer (181-183)
  • SpanHandle (11-11)
framework/tracing/tracer.go (1)
  • Tracer (17-20)
core/schemas/bifrost.go (5)
  • BifrostRequest (173-193)
  • BifrostContextKeyTracer (146-146)
  • BifrostResponse (335-355)
  • RequestType (89-89)
  • BifrostError (474-483)
framework/tracing/helpers.go (1)
  • EndSpan (50-56)
core/utils.go (1)
  • IsStreamRequestType (197-199)
core/schemas/context.go (1)
  • NewBifrostContextWithTimeout (70-73)
ui/components/devProfiler.tsx (1)
ui/lib/utils/port.ts (1)
  • isDevelopmentMode (110-112)
framework/streaming/transcription.go (1)
framework/streaming/types.go (1)
  • AccumulatedData (28-48)
core/bifrost_test.go (1)
core/schemas/tracer.go (1)
  • DefaultTracer (181-183)
framework/streaming/accumulator.go (1)
framework/streaming/types.go (3)
  • StreamAccumulator (103-115)
  • ChatStreamChunk (77-87)
  • ResponsesStreamChunk (90-100)
🪛 markdownlint-cli2 (0.18.1)
transports/changelog.md

16-16: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

Comment thread .github/workflows/scripts/release-bifrost-http.sh
Comment thread examples/plugins/hello-world/go.mod
Comment thread framework/changelog.md Outdated
Comment thread framework/tracing/propagation.go
Comment thread plugins/otel/converter.go
Comment thread transports/bifrost-http/handlers/devpprof.go Outdated
Comment thread transports/bifrost-http/server/server.go
Copy link
Copy Markdown
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

Caution

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

⚠️ Outside diff range comments (1)
plugins/otel/main.go (1)

87-112: Fix OTEL resource attributes parsing: strings.SplitSeq + range usage won’t compile

Two problems here:

  1. strings.SplitSeq does not exist in the Go stdlib (and no alternative import is present), so this line won’t compile.
  2. for attribute := range ... iterates indexes, but strings.TrimSpace(attribute) expects a string; that’s a type mismatch.

This block should use strings.Split and capture the value, e.g.:

Proposed fix
-	if attributes, ok := os.LookupEnv(OTELResponseAttributesEnvKey); ok {
-		// We will split the attributes by , and then split each attribute by =
-		for attribute := range strings.SplitSeq(attributes, ",") {
-			attributeParts := strings.Split(strings.TrimSpace(attribute), "=")
+	if attributes, ok := os.LookupEnv(OTELResponseAttributesEnvKey); ok {
+		// We will split the attributes by , and then split each attribute by =
+		for _, attribute := range strings.Split(attributes, ",") {
+			attributeParts := strings.Split(strings.TrimSpace(attribute), "=")
 			if len(attributeParts) == 2 {
 				attributesFromEnvironment = append(
 					attributesFromEnvironment,
 					kvStr(strings.TrimSpace(attributeParts[0]), strings.TrimSpace(attributeParts[1])),
 				)
 			}
 		}
 	}
♻️ Duplicate comments (24)
examples/plugins/hello-world/go.mod (1)

10-22: Update transitive dependency versions to match the plugin stack.

The indirect dependencies still use outdated versions compared to the governance, logging, and telemetry plugins:

  • github.com/klauspost/compress should be v1.18.2 (line 16, currently v1.18.1)
  • golang.org/x/arch should be v0.23.0 (line 20, currently v0.22.0)
  • golang.org/x/sys should be v0.39.0 (line 21, currently v0.38.0)

Update these versions and run go mod tidy to ensure consistency across the plugin stack.

🔎 Proposed fix
 require (
 	github.com/andybalholm/brotli v1.2.0 // indirect
 	github.com/bytedance/gopkg v0.1.3 // indirect
 	github.com/bytedance/sonic v1.14.1 // indirect
 	github.com/bytedance/sonic/loader v0.3.0 // indirect
 	github.com/cloudwego/base64x v0.1.6 // indirect
-	github.com/klauspost/compress v1.18.1 // indirect
+	github.com/klauspost/compress v1.18.2 // indirect
 	github.com/klauspost/cpuid/v2 v2.3.0 // indirect
 	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
 	github.com/valyala/bytebufferpool v1.0.0 // indirect
-	golang.org/x/arch v0.22.0 // indirect
-	golang.org/x/sys v0.38.0 // indirect
+	golang.org/x/arch v0.23.0 // indirect
+	golang.org/x/sys v0.39.0 // indirect
 )

Then run:

cd examples/plugins/hello-world && go mod tidy
transports/bifrost-http/handlers/devpprof.go (5)

1-15: Missing Unix-only build constraint for syscall.Getrusage usage.

The file imports syscall and uses syscall.Getrusage (line 178), which is only available on Unix platforms. Without build constraints, this code will fail to compile on Windows. As noted in previous reviews, the project supports Windows (evidenced by server.go).

This is a duplicate of the previous review comment on lines 176-190, but the issue has not been addressed.

Add build constraints at the top of the file:

+//go:build !windows
+// +build !windows
+
 package handlers

Or split into platform-specific files (devpprof_unix.go with the constraint and devpprof_windows.go with a Windows-compatible implementation).


142-150: Stop() doesn't recreate stopCh, breaking Start() after Stop().

This is a duplicate of the previous review comment. The issue persists: Stop() closes stopCh but never recreates it. If Start() is called after Stop(), the goroutine will exit immediately because stopCh is already closed.

Apply the previously suggested fix to make Stop() idempotent and allow Start() after Stop():

🔎 Proposed fix
 func (c *MetricsCollector) Stop() {
 	c.mu.Lock()
 	defer c.mu.Unlock()
-	if c.started {
+	if c.started && c.stopCh != nil {
+		select {
+		case <-c.stopCh:
+			// Already closed
+		default:
 		close(c.stopCh)
+		}
 		c.started = false
+		c.stopCh = nil
 	}
 }

And in Start(), recreate the channel:

 func (c *MetricsCollector) Start() {
 	c.mu.Lock()
 	if c.started {
 		c.mu.Unlock()
 		return
 	}
+	if c.stopCh == nil {
+		c.stopCh = make(chan struct{})
+	}
 	c.started = true
 	c.mu.Unlock()
 
 	go c.collectLoop()
 }

175-190: Unix-specific syscall usage breaks Windows compilation.

This is a duplicate of the previous review comment. The issue persists: syscall.Getrusage is only available on Unix platforms and will cause compilation failures on Windows.

This was already flagged in the review of lines 1-15. Apply the build constraint solution mentioned there.


270-368: getTopAllocations provides misleading placeholder data.

This is a duplicate of the previous review comment on lines 293-311. The issue persists: the function claims to return "top allocation sites" but uses runtime.Caller (which captures the current call stack, not allocation hotspots) and arbitrary divisions of heap statistics.

As previously suggested, either:

  1. Document clearly that this is placeholder/approximated data
  2. Use runtime/pprof to parse real heap profile data
  3. Remove top_allocations until accurate data can be provided

The current implementation may confuse users who expect real profiling data.


370-376: Collector started but never stopped, causing goroutine leak.

This is a duplicate of the previous review comment on lines 370-376. The issue persists: RegisterRoutes starts the collector goroutine, but since the handler is created as a local variable in server.go, its Cleanup() method is never called during server shutdown, leaking the background goroutine.

As previously suggested, store devPprofHandler as a field in BifrostHTTPServer and call its Cleanup() method in the shutdown sequence (around line 1330-1348 in server.go).

transports/changelog.md (1)

16-23: Add language specifier to fenced code block.

The code block is missing a language identifier, which prevents syntax highlighting and violates MD040. This was flagged in a previous review but remains unaddressed.

🔎 Add language specifier
   **Migration summary:**
-  ```
+  ```go
   // v1.3.x (removed)
   TransportInterceptor(ctx *BifrostContext, url string, headers map[string]string, body map[string]any) (map[string]string, map[string]any, error)

As per static analysis hints.

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

1424-1513: Refactor completeDeferredSpan to take context.Context by value instead of *context.Context

completeDeferredSpan only reads from the context (trace ID, tracer, post‑hook span finalizer) and does not need to mutate it. Passing *context.Context here is unconventional in Go and unnecessary, since the local ctx value in the callers is already being updated via the pointer passed to postHookRunner.

You can simplify and align with Go context best practices by changing the helper to take ctx context.Context and updating the call sites accordingly.

Proposed refactor
-// completeDeferredSpan completes the deferred LLM span for streaming requests.
-// This is called when the final chunk is processed (when StreamEndIndicator is true).
-// It retrieves the deferred span handle from TraceStore using the trace ID from context,
-// populates response attributes from accumulated chunks, and ends the span.
-func completeDeferredSpan(ctx *context.Context, result *schemas.BifrostResponse, err *schemas.BifrostError) {
-	if ctx == nil {
-		return
-	}
+// completeDeferredSpan completes the deferred LLM span for streaming requests.
+// This is called when the final chunk is processed (when StreamEndIndicator is true).
+// It retrieves the deferred span handle from TraceStore using the trace ID from context,
+// populates response attributes from accumulated chunks, and ends the span.
+func completeDeferredSpan(ctx context.Context, result *schemas.BifrostResponse, err *schemas.BifrostError) {
+	if ctx == nil {
+		return
+	}
 
 	// Get the trace ID from context (this IS available in the provider's goroutine)
-	traceID, ok := (*ctx).Value(schemas.BifrostContextKeyTraceID).(string)
+	traceID, ok := ctx.Value(schemas.BifrostContextKeyTraceID).(string)
@@
-	tracerVal := (*ctx).Value(schemas.BifrostContextKeyTracer)
+	tracerVal := ctx.Value(schemas.BifrostContextKeyTracer)
@@
-	if finalizer, ok := (*ctx).Value(schemas.BifrostContextKeyPostHookSpanFinalizer).(func(context.Context)); ok && finalizer != nil {
+	if finalizer, ok := ctx.Value(schemas.BifrostContextKeyPostHookSpanFinalizer).(func(context.Context)); ok && finalizer != nil {
@@
-			finalizerCtx := context.WithValue(*ctx, schemas.BifrostContextKeySpanID, spanID)
+			finalizerCtx := context.WithValue(ctx, schemas.BifrostContextKeySpanID, spanID)
 			finalizer(finalizerCtx)
 		} else {
-			finalizer(*ctx)
+			finalizer(ctx)
 		}
 	}
@@
-	tracer.ClearDeferredSpan(traceID)
+	tracer.ClearDeferredSpan(traceID)
 }

And adjust the call sites:

-				completeDeferredSpan(&ctx, processedResponse, processedError)
+				completeDeferredSpan(ctx, processedResponse, processedError)

(similarly for all other completeDeferredSpan(&ctx, ...) calls).

In Go, what are the recommended conventions for passing `context.Context`—by value or by pointer—and are there any official guidelines discouraging `*context.Context` parameters?
plugins/jsonparser/version (1)

1-1: Re‑check version bump level vs documented breaking changes

The version is bumped to 1.3.53 even though the plugin API changed from TransportInterceptor to HTTPTransportMiddleware, which is called out as breaking in docs. If you intend to follow semver for this plugin, consider promoting this to at least a minor bump (e.g., 1.4.0) for clarity, and keep it consistent with other plugins in this stack.

#!/bin/bash
# Compare version files across plugins to ensure consistent bump strategy for the middleware v2 change.
fd --type f 'version$' plugins | xargs -I{} sh -c 'echo "{}: $(cat "{}")'
docs/plugins/migration-guide.mdx (1)

247-250: Version reference issue (duplicate concern).

Line 249 references @v1.4.0, but as noted in previous review comments, this tag does not exist in the repository yet. Before merging, either:

  1. Create the v1.4.0 release tag, or
  2. Update the documentation to reference an existing version tag, or
  3. Use a commit hash instead

This ensures the go get command works when users follow the guide.

framework/changelog.md (1)

1-1: Fix grammar error in the tracing feature entry.

The phrase "to enabling e2e tracing" should be "to enable e2e tracing".

🔎 Proposed fix
-- feat: adds new tracing framework for allowing plugins to enabling e2e tracing
+- feat: adds new tracing framework for allowing plugins to enable e2e tracing
examples/plugins/hello-world/main.go (1)

19-27: Context value propagation is broken between middleware and hooks.

The middleware sets a value on fasthttp.RequestCtx via SetUserValue, but PreHook (line 30) and PostHook (line 39) read from schemas.BifrostContext using ctx.Value(). These are different context types, so the value will always be nil in the hooks.

Either propagate the value through the Bifrost context (if the framework supports extracting it from RequestCtx user values), or remove the context reads from the hooks if the data flow demonstration is no longer needed.

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

508-508: Non-streaming path uses raw ctx instead of enriched bifrostCtx.

The non-streaming client calls pass ctx (*fasthttp.RequestCtx) while the comment at line 503 says "Use the cancellable context from ConvertToBifrostContext". This inconsistency was flagged in a previous review. The streaming and batch/file handlers correctly use the Bifrost context, but non-streaming inference calls do not.

Also applies to: 514-514, 534-534, 557-557, 580-580, 603-603, 626-626, 660-660

plugins/governance/main.go (1)

292-297: Fix error message: "marshal" should be "unmarshal".

Line 294 logs "failed to marshal" but the operation on line 292 is sonic.Unmarshal. The error message should reflect the actual operation.

🔎 Proposed fix
 			err = sonic.Unmarshal(ctx.Request.Body(), &payload)
 			if err != nil {
-				p.logger.Error("failed to marshal request body to check for virtual key: %v", err)
+				p.logger.Error("failed to unmarshal request body: %v", err)
 				next(ctx)
 				return
 			}
framework/tracing/store.go (1)

13-177: TraceStore implementation and streaming accumulation look correct; consider trimming unused fields

The TraceStore’s responsibilities — pooled trace/span allocation, TTL-based cleanup via ticker + stopOnce, deferred span storage, and TTFT calculation from StartTime/FirstChunkTime — are implemented in a thread-safe and straightforward way. The Stop() fix with sync.Once also avoids double-close panics.

One minor nit: in DeferredSpanInfo, the Tracer and RequestID fields are still never written or read anywhere in this file. Unless other packages rely on them (none were found in prior analysis), you can safely remove them to reduce confusion and per-entry footprint.

Also applies to: 244-371

plugins/otel/converter.go (1)

148-171: Guard uint64int64 cast in anyToKeyValue to avoid overflow

The case uint64 branch does a direct int64(v) cast, which will overflow for values larger than math.MaxInt64. Even if unlikely, this can silently corrupt attributes for large counters or IDs.

Consider clamping or logging when overflow would occur, e.g.:

Proposed fix
+import "math"
@@ func anyToKeyValue(key string, value any) *KeyValue {
-	case uint64:
-		return kvInt(key, int64(v))
+	case uint64:
+		if v > math.MaxInt64 {
+			// Optionally: log here via plugin logger
+			return kvInt(key, math.MaxInt64)
+		}
+		return kvInt(key, int64(v))
transports/bifrost-http/handlers/middlewares.go (1)

191-279: Fix TracingMiddleware to pass a context.Context into StartSpan (currently uses *fasthttp.RequestCtx)

TracingMiddleware.Middleware currently does:

spanCtx, rootSpan := m.tracer.Load().StartSpan(ctx, string(ctx.RequestURI()), schemas.SpanKindHTTPRequest)

Here ctx is *fasthttp.RequestCtx, but StartSpan is defined on the tracer as:

StartSpan(ctx context.Context, name string, kind SpanKind) (context.Context, SpanHandle)

*fasthttp.RequestCtx does not implement context.Context, so this will not compile and also breaks the GetTraceID-based lookup that expects a real context.Context.

A safe pattern is to build a proper context.Context seeded with the new trace ID and use that for the span, while still storing the trace ID on the fasthttp ctx for downstream handlers:

Proposed fix
@@ func (m *TracingMiddleware) Middleware() schemas.BifrostHTTPMiddleware {
-			// Extract parent trace ID from W3C headers (if present)
-			parentID := tracing.ExtractParentID(&ctx.Request.Header)
-			// Create trace in store - only ID returned (trace data stays in store)
-			traceID := m.tracer.Load().CreateTrace(parentID)
-			// Only trace ID goes into context (lightweight, no bloat)
-			ctx.SetUserValue(schemas.BifrostContextKeyTraceID, traceID)
+			// Extract parent trace ID from W3C headers (if present)
+			parentID := tracing.ExtractParentID(&ctx.Request.Header)
+			// Create trace in store - only ID returned (trace data stays in store)
+			traceID := m.tracer.Load().CreateTrace(parentID)
+			// Store trace ID on fasthttp context for downstream handlers
+			ctx.SetUserValue(schemas.BifrostContextKeyTraceID, traceID)
@@
-			// Create root span for the HTTP request
-			spanCtx, rootSpan := m.tracer.Load().StartSpan(ctx, string(ctx.RequestURI()), schemas.SpanKindHTTPRequest)
+			// Create root span for the HTTP request using a proper context.Context
+			spanBaseCtx := context.WithValue(context.Background(), schemas.BifrostContextKeyTraceID, traceID)
+			tr := m.tracer.Load()
+			spanCtx, rootSpan := tr.StartSpan(spanBaseCtx, string(ctx.RequestURI()), schemas.SpanKindHTTPRequest)
@@
-				if spanID, ok := spanCtx.Value(schemas.BifrostContextKeySpanID).(string); ok {
+				if spanID, ok := spanCtx.Value(schemas.BifrostContextKeySpanID).(string); ok {
 					ctx.SetUserValue(schemas.BifrostContextKeySpanID, spanID)
 				}
 			}

Also consider caching tr := m.tracer.Load() once at the top of the handler and reusing it to avoid repeated atomic loads and to simplify nil checks.

#!/bin/bash
# Verify Tracer.StartSpan signature and ensure it expects context.Context
rg -n "type Tracer interface" core/schemas/tracer.go -n -A10
rg -n "func (t \*Tracer) StartSpan" framework/tracing/tracer.go -n -A5

Also applies to: 281-307

framework/tracing/llmspan.go (1)

190-193: ExtraParams keys may collide with standard attribute names.

User-provided keys from ExtraParams are written directly to the attributes map without namespacing. If a user sets a key like "gen_ai.request.model" in ExtraParams, it would overwrite the standard attribute set earlier in the function.

This pattern repeats throughout the file at lines 298-300, 365-367, 575-577, 708-710, 738-740, 753-755, 768-770, 783-785, 958-960, 982-984, 997-999, 1012-1014, and 1027-1029.

🔎 Suggested approach

Consider namespacing ExtraParams keys to prevent collisions:

 	// ExtraParams
 	for k, v := range req.Params.ExtraParams {
-		attrs[k] = fmt.Sprintf("%v", v)
+		attrs["gen_ai.extra."+k] = fmt.Sprintf("%v", v)
 	}

Apply this pattern consistently across all ExtraParams handling locations.

core/schemas/trace.go (3)

40-49: Trace.Reset() lacks mutex protection.

This method modifies all fields including Spans without holding the mutex, creating a potential data race if Reset is called while another goroutine is accessing the trace (e.g., via AddSpan or GetSpan).


84-89: Span.End() method lacks mutex protection.

Unlike SetAttribute and AddEvent, the End method modifies EndTime, Status, and StatusMsg without holding the mutex. If End is called concurrently with reads or writes to these fields, a data race may occur.


91-104: Span.Reset() lacks mutex protection.

Similar to Trace.Reset(), this method modifies fields without holding the mutex. If reused from a pool while another goroutine still holds a reference, a data race could occur.

transports/bifrost-http/server/server.go (2)

1021-1026: Dev pprof handler created locally and never cleaned up.

When IsDevMode() is true, devPprofHandler is created locally and its RegisterRoutes starts the collector goroutine. Since the handler isn't stored on the server, Cleanup() is never called during shutdown, causing a resource leak.


1243-1252: Add TraceStore cleanup during server shutdown.

The TraceStore is initialized with a cleanup goroutine (line 1245) but is never cleaned up during shutdown. The traceStore variable is local-scoped and the underlying TraceStore.Stop() method is not called. This cleanup goroutine and its associated ticker will continue running indefinitely, causing a resource leak.

Store traceStore in the server struct and call Stop() in the shutdown sequence (lines 1299-1334), or add a public cleanup method to Tracer that calls store.Stop() and invoke it from Bifrost.Shutdown().

framework/tracing/tracer.go (1)

394-442: Choice and tool-call indices are not sorted, causing non-deterministic output.

Lines 394-398 build choiceIndices from map keys with a comment "Sort choice indices for deterministic output" but no sorting is performed. Similarly, lines 434-437 build tcIndices with a comment "Sort tool calls by index" but again no sorting occurs.

Map iteration order in Go is random, so the output order of choices and tool calls will vary across runs, making traces inconsistent.

🔎 Proposed fix
+	sort.Ints(choiceIndices)
+
 	for _, idx := range choiceIndices {
 		accum := choiceMap[idx]
 
 		// Build message
 		msg := &schemas.ChatMessage{
 			Role: accum.role,
 		}
 
 		// Set content
 		if accum.content != "" {
 			msg.Content = &schemas.ChatMessageContent{
 				ContentStr: &accum.content,
 			}
 		}
 
 		// Build assistant message fields
 		if accum.refusal != "" || accum.reasoning != "" || len(accum.reasoningDetails) > 0 ||
 			accum.audio != nil || len(accum.toolCalls) > 0 {
 			msg.ChatAssistantMessage = &schemas.ChatAssistantMessage{}
 
 			if accum.refusal != "" {
 				msg.ChatAssistantMessage.Refusal = &accum.refusal
 			}
 			if accum.reasoning != "" {
 				msg.ChatAssistantMessage.Reasoning = &accum.reasoning
 			}
 			if len(accum.reasoningDetails) > 0 {
 				msg.ChatAssistantMessage.ReasoningDetails = accum.reasoningDetails
 			}
 			if accum.audio != nil {
 				msg.ChatAssistantMessage.Audio = accum.audio
 			}
 			if len(accum.toolCalls) > 0 {
 				// Sort tool calls by index
 				tcIndices := make([]int, 0, len(accum.toolCalls))
 				for tcIdx := range accum.toolCalls {
 					tcIndices = append(tcIndices, tcIdx)
 				}
+				sort.Ints(tcIndices)
 				toolCalls := make([]schemas.ChatAssistantMessageToolCall, 0, len(accum.toolCalls))
 				for _, tcIdx := range tcIndices {
 					toolCalls = append(toolCalls, accum.toolCalls[tcIdx])
 				}
🧹 Nitpick comments (4)
ui/lib/store/apis/devApi.ts (1)

59-59: Clarify polling configuration in comment.

The comment "polls every 10 seconds" is misleading—the endpoint itself doesn't configure polling. The polling interval is set by the consumer when calling the hook (e.g., useGetDevPprofQuery({}, { pollingInterval: 10000 })).

🔎 Suggested comment clarification
-    // Get dev pprof data - polls every 10 seconds
+    // Get dev pprof data - consumers can configure polling via pollingInterval option
     getDevPprof: builder.query<PprofData, void>({
plugins/logging/changelog.md (1)

1-19: Changelog clearly documents the HTTPTransportMiddleware breaking change

The description of the old vs new signatures and the required core/framework versions is accurate and aligned with the updated Plugin interface. If you expect some consumers to have called TransportInterceptor directly, you might soften “no code changes are required” to clarify it applies to users configuring the plugin via Bifrost rather than invoking its methods directly.

framework/tracing/propagation.go (1)

150-162: Add validation for traceFlags parameter.

FormatTraceparent defaults empty traceFlags to "00" but doesn't validate non-empty inputs. Per W3C Trace Context spec, trace flags must be exactly 2 hex characters. Invalid traceFlags values will produce non-compliant headers.

🔎 Proposed fix
 func FormatTraceparent(traceID, spanID, traceFlags string) string {
 	normalizedTraceID := normalizeTraceID(traceID)
 	normalizedSpanID := normalizeSpanID(spanID)

 	if normalizedTraceID == "" || normalizedSpanID == "" {
 		return ""
 	}

-	if traceFlags == "" {
-		traceFlags = "00" // Default: not sampled
+	// Validate and normalize traceFlags
+	if traceFlags == "" || len(traceFlags) != 2 || !isHex(traceFlags) {
+		traceFlags = "00" // Default: not sampled (or invalid input)
 	}
+	traceFlags = strings.ToLower(traceFlags)
 	return "00-" + normalizedTraceID + "-" + normalizedSpanID + "-" + traceFlags
 }
core/bifrost.go (1)

70-82: Tracer storage via atomic.Value + tracerWrapper is consistent; consider minor defensive checks

Wiring the tracer as:

  • tracer field using atomic.Value,
  • storing a single concrete type *tracerWrapper,
  • defaulting to schemas.DefaultTracer() when unset,

is a clean way to avoid atomic.Value’s concrete-type panics and ensure a non-nil tracer implementation is always available. SetTracer and getTracer() are consistent with that pattern, and Shutdown now correctly calls Stop() on the underlying tracer.

If you expect Bifrost instances to ever be constructed outside Init, you might defensively:

  • Guard bifrost.tracer.Load() against nil in getTracer()/Shutdown, and
  • Log when falling back to DefaultTracer().

As written, it’s fine for the current Init-only construction flow.

Also applies to: 102-147, 253-265, 3893-3896

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3f130fd and e8ca91d.

⛔ Files ignored due to path filters (2)
  • examples/plugins/hello-world/go.sum is excluded by !**/*.sum
  • plugins/governance/go.sum is excluded by !**/*.sum
📒 Files selected for processing (96)
  • .github/workflows/release-pipeline.yml
  • .github/workflows/scripts/push-mintlify-changelog.sh
  • .github/workflows/scripts/release-bifrost-http.sh
  • core/bifrost.go
  • core/bifrost_test.go
  • core/changelog.md
  • core/mcp.go
  • core/providers/utils/utils.go
  • core/schemas/bifrost.go
  • core/schemas/context.go
  • core/schemas/plugin.go
  • core/schemas/trace.go
  • core/schemas/tracer.go
  • core/version
  • docs/docs.json
  • docs/plugins/getting-started.mdx
  • docs/plugins/migration-guide.mdx
  • docs/plugins/writing-plugin.mdx
  • examples/plugins/hello-world/go.mod
  • examples/plugins/hello-world/main.go
  • framework/changelog.md
  • framework/configstore/tables/mcp.go
  • framework/plugins/dynamicplugin.go
  • framework/plugins/dynamicplugin_test.go
  • framework/streaming/accumulator.go
  • framework/streaming/audio.go
  • framework/streaming/chat.go
  • framework/streaming/responses.go
  • framework/streaming/transcription.go
  • framework/streaming/types.go
  • framework/tracing/helpers.go
  • framework/tracing/llmspan.go
  • framework/tracing/propagation.go
  • framework/tracing/store.go
  • framework/tracing/tracer.go
  • framework/version
  • plugins/governance/changelog.md
  • plugins/governance/go.mod
  • plugins/governance/main.go
  • plugins/governance/version
  • plugins/jsonparser/changelog.md
  • plugins/jsonparser/main.go
  • plugins/jsonparser/version
  • plugins/logging/changelog.md
  • plugins/logging/main.go
  • plugins/logging/utils.go
  • plugins/logging/version
  • plugins/maxim/changelog.md
  • plugins/maxim/main.go
  • plugins/maxim/version
  • plugins/mocker/changelog.md
  • plugins/mocker/main.go
  • plugins/mocker/version
  • plugins/otel/changelog.md
  • plugins/otel/converter.go
  • plugins/otel/main.go
  • plugins/otel/ttlsyncmap.go
  • plugins/otel/version
  • plugins/semanticcache/changelog.md
  • plugins/semanticcache/main.go
  • plugins/semanticcache/version
  • plugins/telemetry/changelog.md
  • plugins/telemetry/main.go
  • plugins/telemetry/version
  • transports/bifrost-http/handlers/cache.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/devpprof.go
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/handlers/health.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/handlers/logging.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/handlers/mcpserver.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • transports/bifrost-http/handlers/plugins.go
  • transports/bifrost-http/handlers/providers.go
  • transports/bifrost-http/handlers/session.go
  • transports/bifrost-http/handlers/ui.go
  • transports/bifrost-http/handlers/websocket.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/lib/config_test.go
  • transports/bifrost-http/lib/middleware.go
  • transports/bifrost-http/server/server.go
  • transports/changelog.md
  • transports/version
  • ui/app/clientLayout.tsx
  • ui/app/workspace/logs/views/logDetailsSheet.tsx
  • ui/components/devProfiler.tsx
  • ui/components/sidebar.tsx
  • ui/components/ui/sheet.tsx
  • ui/components/ui/switch.tsx
  • ui/lib/store/apis/devApi.ts
  • ui/lib/store/apis/index.ts
💤 Files with no reviewable changes (3)
  • core/mcp.go
  • framework/configstore/tables/mcp.go
  • plugins/otel/ttlsyncmap.go
✅ Files skipped from review due to trivial changes (2)
  • plugins/otel/version
  • framework/version
🚧 Files skipped from review as they are similar to previous changes (35)
  • .github/workflows/release-pipeline.yml
  • .github/workflows/scripts/push-mintlify-changelog.sh
  • framework/streaming/audio.go
  • transports/bifrost-http/handlers/session.go
  • transports/bifrost-http/handlers/mcp.go
  • framework/streaming/types.go
  • transports/bifrost-http/handlers/health.go
  • ui/app/workspace/logs/views/logDetailsSheet.tsx
  • transports/bifrost-http/handlers/websocket.go
  • plugins/governance/version
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/handlers/providers.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/plugins.go
  • plugins/semanticcache/version
  • docs/docs.json
  • framework/tracing/helpers.go
  • framework/plugins/dynamicplugin_test.go
  • plugins/telemetry/main.go
  • plugins/telemetry/version
  • core/schemas/bifrost.go
  • core/version
  • ui/components/ui/sheet.tsx
  • .github/workflows/scripts/release-bifrost-http.sh
  • plugins/mocker/version
  • transports/bifrost-http/lib/config_test.go
  • ui/components/devProfiler.tsx
  • plugins/governance/go.mod
  • ui/components/sidebar.tsx
  • plugins/maxim/version
  • plugins/jsonparser/main.go
  • framework/streaming/chat.go
  • ui/components/ui/switch.tsx
  • transports/bifrost-http/handlers/ui.go
  • plugins/telemetry/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:

  • ui/lib/store/apis/index.ts
  • plugins/mocker/main.go
  • transports/bifrost-http/handlers/cache.go
  • plugins/logging/version
  • transports/changelog.md
  • transports/bifrost-http/integrations/router.go
  • docs/plugins/getting-started.mdx
  • docs/plugins/migration-guide.mdx
  • plugins/semanticcache/main.go
  • plugins/otel/converter.go
  • transports/bifrost-http/handlers/devpprof.go
  • plugins/maxim/changelog.md
  • plugins/jsonparser/changelog.md
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/lib/middleware.go
  • framework/tracing/store.go
  • core/providers/utils/utils.go
  • core/schemas/tracer.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • ui/app/clientLayout.tsx
  • plugins/semanticcache/changelog.md
  • framework/plugins/dynamicplugin.go
  • plugins/jsonparser/version
  • plugins/logging/main.go
  • plugins/governance/main.go
  • framework/streaming/transcription.go
  • core/changelog.md
  • transports/bifrost-http/handlers/inference.go
  • plugins/logging/utils.go
  • framework/tracing/propagation.go
  • ui/lib/store/apis/devApi.ts
  • docs/plugins/writing-plugin.mdx
  • plugins/governance/changelog.md
  • framework/streaming/accumulator.go
  • plugins/mocker/changelog.md
  • plugins/otel/changelog.md
  • transports/bifrost-http/handlers/integrations.go
  • core/bifrost.go
  • transports/bifrost-http/handlers/logging.go
  • examples/plugins/hello-world/main.go
  • core/schemas/trace.go
  • core/bifrost_test.go
  • transports/bifrost-http/handlers/mcpserver.go
  • framework/tracing/tracer.go
  • examples/plugins/hello-world/go.mod
  • transports/bifrost-http/lib/config.go
  • plugins/otel/main.go
  • plugins/logging/changelog.md
  • transports/version
  • core/schemas/plugin.go
  • core/schemas/context.go
  • plugins/maxim/main.go
  • framework/tracing/llmspan.go
  • framework/streaming/responses.go
  • framework/changelog.md
  • transports/bifrost-http/server/server.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:

  • plugins/mocker/main.go
  • transports/bifrost-http/handlers/cache.go
  • transports/bifrost-http/integrations/router.go
  • plugins/semanticcache/main.go
  • plugins/otel/converter.go
  • transports/bifrost-http/handlers/devpprof.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/lib/middleware.go
  • framework/tracing/store.go
  • core/providers/utils/utils.go
  • core/schemas/tracer.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • framework/plugins/dynamicplugin.go
  • plugins/logging/main.go
  • plugins/governance/main.go
  • framework/streaming/transcription.go
  • transports/bifrost-http/handlers/inference.go
  • plugins/logging/utils.go
  • framework/tracing/propagation.go
  • framework/streaming/accumulator.go
  • transports/bifrost-http/handlers/integrations.go
  • core/bifrost.go
  • transports/bifrost-http/handlers/logging.go
  • examples/plugins/hello-world/main.go
  • core/schemas/trace.go
  • core/bifrost_test.go
  • transports/bifrost-http/handlers/mcpserver.go
  • framework/tracing/tracer.go
  • transports/bifrost-http/lib/config.go
  • plugins/otel/main.go
  • core/schemas/plugin.go
  • core/schemas/context.go
  • plugins/maxim/main.go
  • framework/tracing/llmspan.go
  • framework/streaming/responses.go
  • transports/bifrost-http/server/server.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/cache.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/handlers/devpprof.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/lib/middleware.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/handlers/logging.go
  • transports/bifrost-http/handlers/mcpserver.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/server/server.go
📚 Learning: 2025-12-19T09:26:54.961Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/providers/utils/utils.go:1050-1051
Timestamp: 2025-12-19T09:26:54.961Z
Learning: Update streaming end-marker handling so HuggingFace is treated as a non-[DONE] provider for backends that do not emit a DONE marker (e.g., meta llama on novita). In core/providers/utils/utils.go, adjust ProviderSendsDoneMarker() (or related logic) to detect providers that may not emit DONE and avoid relying on DONE as the sole end signal. Add tests to cover both DONE-emitting and non-DONE backends, with clear documentation in code comments explaining the rationale and any fallback behavior.

Applied to files:

  • core/providers/utils/utils.go
📚 Learning: 2025-12-22T10:50:40.990Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1154
File: plugins/governance/store.go:1165-1186
Timestamp: 2025-12-22T10:50:40.990Z
Learning: In the Bifrost governance plugin, budgets and rate limits have 1:1 relationships with their parent entities (virtual keys, teams, customers). Do not assume sharing; ensure cascade deletion logic only deletes budgets/rate limits when there are no shared references. Enforce invariants in code and add tests to verify no cross-entity sharing and that cascade deletes only remove the specific child of the parent. If a counterexample arises, adjust data model or add guards.

Applied to files:

  • plugins/governance/main.go
📚 Learning: 2025-12-24T10:55:31.424Z
Learnt from: akshaydeo
Repo: maximhq/bifrost PR: 888
File: framework/tracing/propagation.go:24-36
Timestamp: 2025-12-24T10:55:31.424Z
Learning: In framework/tracing/propagation.go, ensure ExtractParentID() returns ctx.TraceID (not ctx.ParentID) because the parent request's traceparent header carries the trace ID used for continuing the trace.

Applied to files:

  • framework/tracing/propagation.go
🧬 Code graph analysis (28)
plugins/mocker/main.go (2)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-27)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/cache.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
plugins/semanticcache/main.go (2)
core/schemas/plugin.go (2)
  • Plugin (71-97)
  • BifrostHTTPMiddleware (38-38)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-27)
plugins/otel/converter.go (1)
core/schemas/trace.go (16)
  • Trace (10-19)
  • Span (52-65)
  • SpanKind (115-115)
  • SpanKindLLMCall (121-121)
  • SpanKindPlugin (123-123)
  • SpanKindInternal (139-139)
  • SpanKindRetry (127-127)
  • SpanKindFallback (129-129)
  • SpanKindMCPTool (125-125)
  • SpanKindEmbedding (133-133)
  • SpanKindSpeech (135-135)
  • SpanKindTranscription (137-137)
  • SpanStatus (143-143)
  • SpanStatusOk (149-149)
  • SpanStatusError (151-151)
  • SpanEvent (107-111)
transports/bifrost-http/handlers/devpprof.go (4)
ui/lib/store/apis/devApi.ts (6)
  • MemoryStats (4-10)
  • CPUStats (13-17)
  • RuntimeStats (20-26)
  • AllocationInfo (29-35)
  • HistoryPoint (38-45)
  • PprofData (48-55)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/lib/middleware.go (1)
  • ChainMiddlewares (11-23)
transports/bifrost-http/handlers/utils.go (1)
  • SendJSON (16-22)
transports/bifrost-http/handlers/middlewares.go (7)
core/schemas/plugin.go (3)
  • BifrostHTTPMiddleware (38-38)
  • ObservabilityPlugin (123-137)
  • Plugin (71-97)
core/schemas/tracer.go (1)
  • Tracer (38-116)
framework/tracing/tracer.go (1)
  • Tracer (17-20)
framework/tracing/propagation.go (1)
  • ExtractParentID (74-84)
core/schemas/bifrost.go (4)
  • BifrostContextKeyTraceID (143-143)
  • BifrostContextKeyTraceCompleter (148-148)
  • BifrostContextKeySpanID (144-144)
  • BifrostContextKeyDeferTraceCompletion (147-147)
core/schemas/trace.go (3)
  • SpanKindHTTPRequest (131-131)
  • SpanStatusError (151-151)
  • SpanStatusOk (149-149)
framework/tracing/helpers.go (1)
  • EndSpan (50-56)
transports/bifrost-http/lib/middleware.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
framework/tracing/store.go (5)
core/schemas/tracer.go (1)
  • Tracer (38-116)
framework/tracing/tracer.go (1)
  • Tracer (17-20)
core/schemas/bifrost.go (1)
  • BifrostResponse (335-355)
core/schemas/trace.go (6)
  • Trace (10-19)
  • Span (52-65)
  • SpanEvent (107-111)
  • SpanKind (115-115)
  • SpanStatusUnset (147-147)
  • SpanStatus (143-143)
framework/tracing/helpers.go (3)
  • GetTrace (23-29)
  • AddSpan (32-38)
  • EndSpan (50-56)
core/schemas/tracer.go (6)
core/schemas/bifrost.go (4)
  • ModelProvider (33-33)
  • BifrostError (474-483)
  • BifrostRequest (173-193)
  • BifrostResponse (335-355)
core/schemas/chatcompletions.go (1)
  • BifrostLLMUsage (845-852)
core/schemas/speech.go (1)
  • BifrostSpeechResponse (22-29)
core/schemas/transcriptions.go (1)
  • BifrostTranscriptionResponse (16-26)
core/schemas/trace.go (3)
  • Trace (10-19)
  • SpanKind (115-115)
  • SpanStatus (143-143)
core/schemas/context.go (1)
  • BifrostContext (32-42)
transports/bifrost-http/handlers/middlewares_test.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
ui/app/clientLayout.tsx (1)
ui/components/devProfiler.tsx (1)
  • DevProfiler (55-422)
plugins/logging/main.go (5)
core/utils.go (1)
  • IsStreamRequestType (197-199)
core/schemas/bifrost.go (3)
  • RequestType (89-89)
  • BifrostContextKeyTracer (146-146)
  • BifrostContextKeyTraceID (143-143)
core/schemas/tracer.go (1)
  • Tracer (38-116)
framework/tracing/tracer.go (1)
  • Tracer (17-20)
framework/streaming/types.go (1)
  • ProcessedStreamResponse (118-126)
plugins/governance/main.go (3)
core/utils.go (1)
  • Ptr (56-58)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-27)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
framework/streaming/transcription.go (1)
framework/streaming/types.go (1)
  • AccumulatedData (28-48)
transports/bifrost-http/handlers/inference.go (2)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
core/schemas/bifrost.go (2)
  • BifrostContextKeyDeferTraceCompletion (147-147)
  • BifrostContextKeyTraceCompleter (148-148)
ui/lib/store/apis/devApi.ts (1)
transports/bifrost-http/handlers/devpprof.go (6)
  • MemoryStats (27-33)
  • CPUStats (36-40)
  • RuntimeStats (43-49)
  • AllocationInfo (52-58)
  • HistoryPoint (61-68)
  • PprofData (71-78)
framework/streaming/accumulator.go (1)
framework/streaming/types.go (3)
  • StreamAccumulator (103-115)
  • ChatStreamChunk (77-87)
  • ResponsesStreamChunk (90-100)
transports/bifrost-http/handlers/integrations.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/logging.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
examples/plugins/hello-world/main.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
core/bifrost_test.go (1)
core/schemas/tracer.go (1)
  • DefaultTracer (181-183)
transports/bifrost-http/handlers/mcpserver.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
plugins/otel/main.go (2)
core/schemas/plugin.go (1)
  • ObservabilityPlugin (123-137)
core/schemas/trace.go (1)
  • Trace (10-19)
core/schemas/plugin.go (2)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-27)
core/schemas/trace.go (1)
  • Trace (10-19)
core/schemas/context.go (1)
core/schemas/bifrost.go (1)
  • BifrostContextKeyDeferTraceCompletion (147-147)
plugins/maxim/main.go (2)
core/schemas/plugin.go (2)
  • Plugin (71-97)
  • BifrostHTTPMiddleware (38-38)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-27)
framework/streaming/responses.go (1)
framework/streaming/types.go (1)
  • AccumulatedData (28-48)
transports/bifrost-http/server/server.go (8)
core/schemas/plugin.go (2)
  • Plugin (71-97)
  • ObservabilityPlugin (123-137)
plugins/governance/main.go (1)
  • PluginName (23-23)
plugins/logging/main.go (1)
  • PluginName (22-22)
plugins/telemetry/main.go (1)
  • PluginName (21-21)
transports/bifrost-http/handlers/devpprof.go (2)
  • IsDevMode (107-109)
  • NewDevPprofHandler (123-127)
transports/bifrost-http/handlers/middlewares.go (1)
  • NewTracingMiddleware (207-215)
framework/tracing/store.go (1)
  • NewTraceStore (40-67)
framework/tracing/tracer.go (1)
  • NewTracer (24-29)
🪛 markdownlint-cli2 (0.18.1)
transports/changelog.md

16-16: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

Comment thread plugins/logging/version Outdated
@akshaydeo akshaydeo force-pushed the 11-19-plugins_v2_architecture branch from e8ca91d to 5186ad3 Compare December 25, 2025 13:55
Copy link
Copy Markdown
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

♻️ Duplicate comments (18)
plugins/jsonparser/version (1)

1-1: Semver violation remains unresolved; stack targets v1.4.0.

The version 1.3.53 still represents a patch increment despite the breaking change (TransportInterceptor → HTTPTransportMiddleware) documented in the changelog. This was previously flagged and remains unaddressed.

Given that this PR stack targets the v1.4.0 branch and introduces framework-wide breaking changes across multiple plugins, consider whether:

  • The jsonparser plugin version should align with the Bifrost v1.4.0 release version, or
  • Individual plugin versions should independently reflect their breaking changes (e.g., bump to 2.0.0 or align with 1.4.0)

Verify the project's plugin versioning strategy across the entire PR stack (#1170, #1169, #1121, #1120, #1119, #888) to ensure consistency.

ui/components/ui/sheet.tsx (2)

103-103: Past review suggestion remains unaddressed.

The past review suggested moving mb-6 to the false branch of the ternary to avoid applying it when expandable is true (where p-0 could conflict). The current implementation still applies mb-6 unconditionally. If this deviation from the suggested fix is intentional, consider adding a brief comment explaining the decision.

🔎 Original suggested fix for reference
-		<div data-slot="sheet-header" className={cn("mb-6 flex items-center", sheetContext?.expandable ? "p-0" : "")} {...props}>
+		<div data-slot="sheet-header" className={cn("flex items-center", sheetContext?.expandable ? "p-0" : "mb-6")} {...props}>

114-114: Consider making left margin conditional on button presence.

The unconditional ml-3 creates left spacing even when the expand/collapse button isn't rendered (i.e., when expandable is false or side is not "right"). If consistent spacing is intentional regardless of button presence, the current implementation is fine. Otherwise, consider the conditional approach from the past review.

🔎 Original suggested fix for reference
-			<div className={cn("flex h-full min-w-0 flex-1 flex-row items-center ml-3", className)}>{children}</div>
+			<div className={cn("flex h-full min-w-0 flex-1 flex-row items-center", sheetContext?.expandable && sheetContext?.side === "right" ? "ml-3" : "", className)}>{children}</div>
plugins/otel/changelog.md (1)

19-19: Verify the migration guide file exists in this PR.

The changelog references /docs/plugins/migration-guide. Based on the AI summary, docs/plugins/migration-guide.mdx appears to be added in this PR. Ensure this file is included and the link resolves correctly after merge.

#!/bin/bash
# Verify migration guide file exists
fd "migration-guide" docs/
transports/changelog.md (2)

16-23: Version reference inconsistency with plugin changelogs.

The migration summary states v1.3.x (removed) and v1.4.x+ (new), but the plugin changelogs (maxim, jsonparser, semanticcache) consistently reference core v1.3.0+ for the new API. This inconsistency could confuse developers about which version to target for migration.

Consider aligning with the plugin changelogs: change v1.4.x+ (new) to v1.3.0+ (new) and adjust v1.3.x (removed) to the prior version series (e.g., v1.2.x (removed)).


16-16: Add language specifier to fenced code block.

The fenced code block lacks a language identifier, preventing proper syntax highlighting and violating MD040. Add text or go after the opening backticks:

🔎 Suggested fix
   **Migration summary:**
-  ```
+  ```text
   // v1.3.x (removed)
framework/changelog.md (1)

1-1: Fix grammar and expand tracing framework description.

Line 1 contains a grammar error: "to enabling e2e tracing" should be "to enable e2e tracing". Additionally, consider expanding this entry to briefly explain what the tracing framework provides to plugins (e.g., "enables plugins to report spans and traces for distributed tracing integration").

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

876-931: Streaming span completion logic looks correct; consider future cleanup of pointer‑context pattern.

The updated ProcessAndSendResponse / ProcessAndSendBifrostError now:

  • Record chunks into the tracer before post‑hooks.
  • Run postHookRunner first so completeDeferredSpan sees post‑processed data.
  • Complete the deferred span both on normal final chunks and on skip‑stream control, using the processed response/error.
  • Use tracer.GetAccumulatedChunks to populate response attributes, TTFT, and total chunks, then finalize plugin post‑hook spans before ending the llm.call span.

ProviderSendsDoneMarker now treating HuggingFace as non‑[DONE] is consistent with prior guidance for providers that terminate without the sentinel. Based on learnings, this is the desired behavior.

The remaining use of *context.Context in completeDeferredSpan follows the existing PostHookRunner pattern; if you revisit that API later, passing context.Context by value would better align with Go conventions, but it’s not blocking for this change.

Also applies to: 938-981, 1424-1513

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

502-685: Use the converted Bifrost context in non‑streaming client calls, not the HTTP request context.

handleNonStreamingRequest still passes the raw *fasthttp.RequestCtx (ctx) into Bifrost client methods:

listModelsResponse, bifrostErr := g.client.ListModelsRequest(ctx, ...)
textCompletionResponse, bifrostErr := g.client.TextCompletionRequest(ctx, ...)
chatResponse, bifrostErr := g.client.ChatCompletionRequest(ctx, ...)
responsesResponse, bifrostErr := g.client.ResponsesRequest(ctx, ...)
embeddingResponse, bifrostErr := g.client.EmbeddingRequest(ctx, ...)
speechResponse, bifrostErr := g.client.SpeechRequest(ctx, ...)
transcriptionResponse, bifrostErr := g.client.TranscriptionRequest(ctx, ...)

Yet the comment above and the rest of this file show the intent is to use the cancellable context from ConvertToBifrostContext:

  • Streaming path correctly uses streamCtx := *bifrostCtx for all *StreamRequest calls.
  • Batch and file handlers use requestCtx := *bifrostCtx for their Bifrost calls.

Passing ctx here bypasses the enriched Bifrost context (trace IDs, plugin values, timeouts), so non‑streaming requests won’t participate fully in tracing/governance/plugin flows and may not respect cancellation semantics.

Consider updating these calls to use *bifrostCtx instead:

- listModelsResponse, bifrostErr := g.client.ListModelsRequest(ctx, bifrostReq.ListModelsRequest)
+ listModelsResponse, bifrostErr := g.client.ListModelsRequest(*bifrostCtx, bifrostReq.ListModelsRequest)

- textCompletionResponse, bifrostErr := g.client.TextCompletionRequest(ctx, bifrostReq.TextCompletionRequest)
+ textCompletionResponse, bifrostErr := g.client.TextCompletionRequest(*bifrostCtx, bifrostReq.TextCompletionRequest)

- chatResponse, bifrostErr := g.client.ChatCompletionRequest(ctx, bifrostReq.ChatRequest)
+ chatResponse, bifrostErr := g.client.ChatCompletionRequest(*bifrostCtx, bifrostReq.ChatRequest)

- responsesResponse, bifrostErr := g.client.ResponsesRequest(ctx, bifrostReq.ResponsesRequest)
+ responsesResponse, bifrostErr := g.client.ResponsesRequest(*bifrostCtx, bifrostReq.ResponsesRequest)

- embeddingResponse, bifrostErr := g.client.EmbeddingRequest(ctx, bifrostReq.EmbeddingRequest)
+ embeddingResponse, bifrostErr := g.client.EmbeddingRequest(*bifrostCtx, bifrostReq.EmbeddingRequest)

- speechResponse, bifrostErr := g.client.SpeechRequest(ctx, bifrostReq.SpeechRequest)
+ speechResponse, bifrostErr := g.client.SpeechRequest(*bifrostCtx, bifrostReq.SpeechRequest)

- transcriptionResponse, bifrostErr := g.client.TranscriptionRequest(ctx, bifrostReq.TranscriptionRequest)
+ transcriptionResponse, bifrostErr := g.client.TranscriptionRequest(*bifrostCtx, bifrostReq.TranscriptionRequest)

This keeps non‑streaming behavior consistent with streaming, batch, and file flows, and ensures all observability/governance context travels through Bifrost.

framework/tracing/propagation.go (1)

10-29: W3C trace context helpers are well‑structured and align with the intended propagation semantics.

  • normalizeTraceID / normalizeSpanID enforce hex formatting and lengths, handling UUID inputs cleanly.
  • ParseTraceparent and FormatTraceparent correctly validate IDs and now normalize traceFlags, defaulting to "00" when invalid, which avoids emitting malformed headers.
  • ExtractParentID intentionally returns the parsed trace ID from the incoming traceparent for trace continuation, which matches the stored guidance for this file.

Overall this module provides a solid basis for W3C trace context handling in the tracing stack.

Also applies to: 58-84, 104-143, 145-182

plugins/governance/main.go (1)

233-261: Align virtual‑key validation across headers and fix misleading error logs in HTTPTransportMiddleware.

Two issues here:

  1. x-bf-vk is not validated against VirtualKeyPrefix.

    • parseVirtualKey returns x-bf-vk directly if non‑empty, without checking that it starts with VirtualKeyPrefix (unlike the Authorization / x-api-key / x-goog-api-key branches).
    • This makes VK validation inconsistent and can let malformed values bypass the usual prefix check, affecting governance enforcement and store lookups.

    Consider tightening this branch to mirror the other header sources:

    func parseVirtualKey(ctx *fasthttp.RequestCtx) *string {
        var virtualKeyValue string
  •  vkHeader := ctx.Request.Header.Peek("x-bf-vk")
    
  •  if string(vkHeader) != "" {
    
  •      return bifrost.Ptr(string(vkHeader))
    
  •  }
    
  •  vkHeader := string(ctx.Request.Header.Peek("x-bf-vk"))
    
  •  if vkHeader != "" {
    
  •      v := strings.TrimSpace(vkHeader)
    
  •      if strings.HasPrefix(strings.ToLower(v), VirtualKeyPrefix) {
    
  •          return bifrost.Ptr(v)
    
  •      }
    
  •  }
     // ... existing Authorization / x-api-key / x-goog-api-key logic ...
    
    }
    
    
  1. Unmarshal log message says "marshal".

    • In HTTPTransportMiddleware, the first error log in this block:

      err = sonic.Unmarshal(ctx.Request.Body(), &payload)
      if err != nil {
          p.logger.Error("failed to marshal request body to check for virtual key: %v", err)
      }

      is performing an unmarshal, not a marshal. This was already raised earlier; the wording is still misleading.

    A minimal fix:

    - p.logger.Error("failed to marshal request body to check for virtual key: %v", err)
    + p.logger.Error("failed to unmarshal request body to check for virtual key: %v", err)

The rest of the middleware (MCP tool header injection and provider load‑balancing) looks reasonable and safely falls back to next(ctx) on errors.

Also applies to: 263-314

framework/tracing/store.go (1)

13-22: Drop unused Tracer and RequestID fields from DeferredSpanInfo

DeferredSpanInfo.Tracer and .RequestID are not read or written anywhere in this file; only SpanID, StartTime, FirstChunkTime, AccumulatedChunks, and mu are used. Keeping dead fields here increases memory footprint and obscures the actual responsibilities of the struct.

Unless there is concrete usage elsewhere in the stack, consider removing these fields and updating the comment to match.

plugins/logging/main.go (1)

225-233: Verify that BifrostContextKeyTracer is actually present during PreHook for streaming

PreHook creates a stream accumulator only when both tracer and trace ID are found in the context:

if bifrost.IsStreamRequestType(req.RequestType) {
    if tracer, ok := ctx.Value(schemas.BifrostContextKeyTracer).(schemas.Tracer); ok && tracer != nil {
        if traceID, ok := ctx.Value(schemas.BifrostContextKeyTraceID).(string); ok && traceID != "" {
            tracer.CreateStreamAccumulator(traceID, createdTimestamp)
        }
    }
}

From core/bifrost.go, BifrostContextKeyTracer is set inside executeRequestWithRetries, which runs after plugin PreHooks. Unless some upstream layer injects the tracer into the context earlier, this block will never run on the standard HTTP → Bifrost path, and ProcessStreamingChunk will operate without a CreateStreamAccumulator having been called (or will rely on lazy creation inside the tracer).

Either:

  • Move accumulator initialization into the tracer (e.g. inside ProcessStreamingChunk if missing), or
  • Ensure BifrostContextKeyTracer (and trace ID) are set before RunPreHooks, or
  • Remove this PreHook branch if the tracer now owns accumulator lifecycle.

Also applies to: 423-438

plugins/otel/converter.go (1)

148-170: Avoid unsafe uint64int64 cast in anyToKeyValue

The uint64 branch casts directly to int64:

case uint64:
    return kvInt(key, int64(v))

For values > math.MaxInt64, this overflows and will produce a negative or otherwise invalid value in OTEL attributes.

A simple guard (clamp or log-and-cap) would make this safe.

Proposed fix
 import (
 	"encoding/hex"
 	"fmt"
 	"strings"
+	"math"
@@
 	case uint32:
 		return kvInt(key, int64(v))
 	case uint64:
-		return kvInt(key, int64(v))
+		if v > math.MaxInt64 {
+			// Clamp to MaxInt64 to avoid overflow
+			return kvInt(key, math.MaxInt64)
+		}
+		return kvInt(key, int64(v))
transports/bifrost-http/handlers/middlewares.go (1)

201-279: TracingMiddleware should call StartSpan with context.Context, not *fasthttp.RequestCtx

TracingMiddleware.Middleware currently does:

spanCtx, rootSpan := m.tracer.Load().StartSpan(ctx, string(ctx.RequestURI()), schemas.SpanKindHTTPRequest)

where ctx is *fasthttp.RequestCtx. However, the tracer implementation used elsewhere in the stack implements core/schemas.Tracer, whose StartSpan expects a context.Context. *fasthttp.RequestCtx does not implement context.Context, so this will not type-check unless the tracer has a different signature here.

You likely want to:

  • Build a proper context.Context (e.g. baseCtx := context.Background() or one derived from the request/trace ID), and
  • Pass that to StartSpan, then
  • Use the returned spanCtx only for span propagation (e.g. extracting BifrostContextKeySpanID as you already do).

For example:

baseCtx := context.Background()
spanCtx, rootSpan := m.tracer.Load().StartSpan(baseCtx, string(ctx.RequestURI()), schemas.SpanKindHTTPRequest)

and continue to read BifrostContextKeySpanID from spanCtx as you do today.

core/schemas/trace.go (1)

40-49: Protect Trace.Reset, Span.End, and Span.Reset with the existing mutexes to avoid data races.

Trace and Span both have mu sync.Mutex, and mutation helpers (AddSpan, SetAttribute, AddEvent) correctly lock it. However:

  • Trace.Reset() clears RootSpan, Spans, and timing/attribute fields without acquiring t.mu.
  • Span.End() updates EndTime, Status, and StatusMsg without acquiring s.mu.
  • Span.Reset() clears all span fields, including Events, without acquiring s.mu.

If traces/spans are reused from pools or accessed concurrently (e.g., via the tracer and plugins) while these methods run, Go’s race detector will flag data races and behavior becomes nondeterministic.

Consider wrapping these mutating methods in the same mutex used elsewhere, e.g.:

 func (t *Trace) Reset() {
-	t.TraceID = ""
-	t.ParentID = ""
-	t.RootSpan = nil
-	t.Spans = t.Spans[:0]
-	t.StartTime = time.Time{}
-	t.EndTime = time.Time{}
-	t.Attributes = nil
+	t.mu.Lock()
+	defer t.mu.Unlock()
+	t.TraceID = ""
+	t.ParentID = ""
+	t.RootSpan = nil
+	t.Spans = t.Spans[:0]
+	t.StartTime = time.Time{}
+	t.EndTime = time.Time{}
+	t.Attributes = nil
 }
 
 func (s *Span) End(status SpanStatus, statusMsg string) {
-	s.EndTime = time.Now()
-	s.Status = status
-	s.StatusMsg = statusMsg
+	s.mu.Lock()
+	defer s.mu.Unlock()
+	s.EndTime = time.Now()
+	s.Status = status
+	s.StatusMsg = statusMsg
 }
 
 func (s *Span) Reset() {
-	s.SpanID = ""
-	s.ParentID = ""
-	s.TraceID = ""
-	s.Name = ""
-	s.Kind = SpanKindUnspecified
-	s.StartTime = time.Time{}
-	s.EndTime = time.Time{}
-	s.Status = SpanStatusUnset
-	s.StatusMsg = ""
-	s.Attributes = nil
-	s.Events = s.Events[:0]
+	s.mu.Lock()
+	defer s.mu.Unlock()
+	s.SpanID = ""
+	s.ParentID = ""
+	s.TraceID = ""
+	s.Name = ""
+	s.Kind = SpanKindUnspecified
+	s.StartTime = time.Time{}
+	s.EndTime = time.Time{}
+	s.Status = SpanStatusUnset
+	s.StatusMsg = ""
+	s.Attributes = nil
+	s.Events = s.Events[:0]
 }

This matches the locking strategy already used by the other methods and keeps reuse via pools safe.

Also applies to: 84-104

framework/tracing/tracer.go (1)

233-258: Make buildCompleteResponseFromChunks deterministic by sorting choice and tool‑call indices.

The reconstruction logic still builds indices from maps but never sorts them:

  • choiceIndices is populated from choiceMap (map[int]*choiceAccumulator) and iterated in map order.
  • tcIndices is populated from acc.toolCalls (map[int]ChatAssistantMessageToolCall) and likewise iterated in map order.

Despite comments about “Sort choice indices for deterministic output” and “Sort tool calls by index”, no sort.Ints(...) calls are present. This means the order of Choices and ToolCalls in the final ChatResponse is nondeterministic, which can make logs and tests flaky.

Consider:

	import "sort"
	...
	choiceIndices := make([]int, 0, len(choiceMap))
	for idx := range choiceMap {
		choiceIndices = append(choiceIndices, idx)
	}
-	for _, idx := range choiceIndices {
+	sort.Ints(choiceIndices)
+	for _, idx := range choiceIndices {
		...
		if len(accum.toolCalls) > 0 {
-			tcIndices := make([]int, 0, len(accum.toolCalls))
-			for tcIdx := range accum.toolCalls {
-				tcIndices = append(tcIndices, tcIdx)
-			}
+			tcIndices := make([]int, 0, len(accum.toolCalls))
+			for tcIdx := range accum.toolCalls {
+				tcIndices = append(tcIndices, tcIdx)
+			}
+			sort.Ints(tcIndices)
			toolCalls := make([]schemas.ChatAssistantMessageToolCall, 0, len(accum.toolCalls))
			for _, tcIdx := range tcIndices {
				toolCalls = append(toolCalls, accum.toolCalls[tcIdx])
			}

This preserves existing behavior while making the output stable across runs.

Also applies to: 271-293, 393-455

framework/tracing/llmspan.go (1)

190-193: ExtraParams written directly to attrs map may cause attribute key collisions.

User-provided keys from ExtraParams are written directly to the attributes map without namespacing. If a user sets a key like "gen_ai.request.model" in ExtraParams, it would overwrite the standard attribute set on line 22.

This pattern repeats throughout the file at lines 298-300, 365-367, 575-577, 708-710, 738-740, 753-755, 768-770, 783-785, 958-960, 982-984, 997-999, 1012-1014, and 1027-1029.

🧹 Nitpick comments (12)
.github/workflows/scripts/push-mintlify-changelog.sh (2)

239-244: Branch resolution logic is sound for CI environments.

The dynamic branch detection with detached HEAD handling is appropriate. The fallback chain (current branch → GITHUB_REF_NAMEmain) correctly handles common CI scenarios where the repository is checked out in detached HEAD state.

💡 Optional: Add explanatory comment

For future maintainers, consider adding a brief comment:

 # Pulling again before committing
+# Determine current branch; in CI detached HEAD scenarios, use GITHUB_REF_NAME
 CURRENT_BRANCH="$(git rev-parse --abbrev-ref HEAD)"
 if [ "$CURRENT_BRANCH" = "HEAD" ]; then
   # In detached HEAD state (common in CI), use GITHUB_REF_NAME or default to main
   CURRENT_BRANCH="${GITHUB_REF_NAME:-main}"
 fi

244-255: Add error handling for git operations.

The script lacks error handling for critical git operations (pull, commit, push). If any of these commands fail, the script continues silently, which can lead to incomplete changelog deployments in CI/CD workflows.

🔎 Recommended improvements

Option 1: Enable strict error handling at script level

Add at the top of the script (after the shebang):

 #!/usr/bin/env bash
+set -e  # Exit on error
+set -o pipefail  # Exit on pipe failure

Option 2: Add explicit error checking for critical operations

-git pull origin "$CURRENT_BRANCH"
+if ! git pull origin "$CURRENT_BRANCH"; then
+  echo "❌ Failed to pull from $CURRENT_BRANCH"
+  exit 1
+fi
+
 # Commit and push changes
 git add docs/changelogs/$VERSION.mdx
 git add docs/docs.json
 # Add all cleaned changelog files
 for file in "${CLEANED_CHANGELOG_FILES[@]}"; do
   git add "$file"
 done
 git config user.name "github-actions[bot]"
 git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
-git commit -m "Adds changelog for $VERSION --skip-pipeline"
-git push origin "$CURRENT_BRANCH"
+if git commit -m "Adds changelog for $VERSION --skip-pipeline"; then
+  if ! git push origin "$CURRENT_BRANCH"; then
+    echo "❌ Failed to push to $CURRENT_BRANCH"
+    exit 1
+  fi
+  echo "✅ Successfully pushed changelog for $VERSION"
+else
+  echo "⚠️  No changes to commit"
+fi
ui/components/devProfiler.tsx (1)

139-145: Consider differentiating the minimize button icon.

Both the "Collapse" toggle (Line 134) and "Minimize" button (Line 144) use <ChevronDown> when expanded, which may confuse users about their different behaviors. Consider using a distinct icon for minimize (e.g., Minus from lucide-react).

ui/lib/store/apis/devApi.ts (1)

59-59: Nitpick: Comment is slightly misleading.

The comment "polls every 10 seconds" describes consumer behavior, not endpoint behavior. The polling interval is configured at the call site in devProfiler.tsx, not in this endpoint definition.

🔎 Suggested clarification
   endpoints: (builder) => ({
-    // Get dev pprof data - polls every 10 seconds
+    // Get dev pprof data (consumers typically poll this endpoint)
     getDevPprof: builder.query<PprofData, void>({
framework/plugins/dynamicplugin.go (1)

24-28: Dynamic plugin now correctly treats HTTPTransportMiddleware as a factory; only error text is slightly stale.

The change to store a func() schemas.BifrostHTTPMiddleware and assert the symbol to that type fixes the earlier runtime type‑assertion issue when loading plugins that export a factory. The accessor HTTPTransportMiddleware() then correctly returns the constructed middleware.

As a minor polish, the error message at line 146 still mentions func(next fasthttp.RequestHandler) fasthttp.RequestHandler; updating it to reflect func() schemas.BifrostHTTPMiddleware would make debugging clearer.

Also applies to: 36-39, 140-147

plugins/logging/main.go (1)

96-111: Make Cleanup idempotent to avoid double-close panics on done

Cleanup stops the ticker and unconditionally closes p.done. If Cleanup is ever called twice for the same LoggerPlugin instance (e.g., via tests or misuse), the second close(p.done) will panic.

Wrapping the shutdown path in a sync.Once keeps the API safer without changing normal behavior.

Proposed diff
 type LoggerPlugin struct {
 	ctx                   context.Context
 	store                 logstore.LogStore
 	disableContentLogging *bool
 	pricingManager        *modelcatalog.ModelCatalog
 	mu                    sync.Mutex
 	done                  chan struct{}
 	wg                    sync.WaitGroup
+	cleanupOnce           sync.Once
 	logger                schemas.Logger
 	logCallback           LogCallback
 	droppedRequests       atomic.Int64
 	cleanupTicker         *time.Ticker // Ticker for cleaning up old processing logs
 	logMsgPool            sync.Pool    // Pool for reusing LogMessage structs
 	updateDataPool        sync.Pool    // Pool for reusing UpdateLogData structs
@@
 func (p *LoggerPlugin) Cleanup() error {
-	// Stop the cleanup ticker
-	if p.cleanupTicker != nil {
-		p.cleanupTicker.Stop()
-	}
-	// Signal the background worker to stop
-	close(p.done)
-	// Wait for the background worker to finish processing remaining items
-	p.wg.Wait()
-	// Note: Accumulator cleanup is handled by the tracer, not the logging plugin
-	// GORM handles connection cleanup automatically
-	return nil
+	p.cleanupOnce.Do(func() {
+		// Stop the cleanup ticker
+		if p.cleanupTicker != nil {
+			p.cleanupTicker.Stop()
+		}
+		// Signal the background worker to stop
+		close(p.done)
+		// Wait for the background worker to finish processing remaining items
+		p.wg.Wait()
+		// Note: Accumulator cleanup is handled by the tracer, not the logging plugin
+		// GORM handles connection cleanup automatically
+	})
+	return nil
 }

Also applies to: 144-156, 158-180, 614-625

framework/tracing/store.go (1)

341-360: Be explicit that TTL cleanup may reclaim still-active traces

cleanupOldTraces deletes any trace whose StartTime is older than now - ttl, regardless of whether it’s still in active use:

cutoff := time.Now().Add(-s.ttl)
s.traces.Range(func(key, value any) bool {
    trace := value.(*schemas.Trace)
    if trace.StartTime.Before(cutoff) {
        if deleted, ok := s.traces.LoadAndDelete(key); ok {
            s.ReleaseTrace(deleted.(*schemas.Trace))
            count++
        }
    }
    return true
})

If a very long-lived request exceeds ttl, its trace could be reset and returned to the pool while spans are still being added, leading to missing telemetry or potential data races.

At minimum, this should be clearly documented and ttl configured well above any realistic request duration. Alternatively, consider tightening the predicate (e.g., only cleaning traces that have been explicitly completed or have an EndTime set).

transports/bifrost-http/lib/config.go (2)

276-356: LoadConfig signature and default/file paths look consistent with the new API.

The removal of EnterpriseOverrides and the updated calls to loadConfigFromDefaults and loadConfigFromFile are internally consistent, and LoadConfig now has a simpler surface that matches how Bootstrap uses it. Just ensure any other call sites in the stack have been updated to the new (ctx, configDirPath) signature.


1509-1542: Pricing manager initialization now uniformly uses default modelcatalog.Init (no overrides).

Both initFrameworkConfigFromFile and initDefaultFrameworkConfig initialize pricingConfig and call modelcatalog.Init(ctx, pricingConfig, config.ConfigStore, nil, logger), which aligns with the removal of enterprise overrides and with LoadPricingManager in the server. This keeps pricing behavior consistent across file-based and default config paths; the warning‑only handling of init errors is unchanged and acceptable given pricing is ancillary to core routing.

Also applies to: 1862-1915

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

214-324: Plugin loading, middleware type migration, tracing, and dev‑pprof lifecycle wiring look consistent.

  • LoadPlugin/LoadPlugins correctly use generics to return either concrete plugin types or schemas.Plugin, and built‑in cases (telemetry, logging, governance, maxim, semanticcache, otel) are wired through a single helper without EnterpriseOverrides.
  • All HTTP middleware signatures have been migrated to schemas.BifrostHTTPMiddleware (in RegisterInferenceRoutes, RegisterAPIRoutes, RegisterUIRoutes, and PrepareCommonMiddlewares), aligning server wiring with the new plugin v2 middleware type.
  • Bootstrap now uses lib.LoadConfig(ctx, configDir) and LoadPlugins(ctx, s.Config), then initializes tracing only when observability/logging plugins are present, injecting TracingMiddleware ahead of the inference middlewares and setting the tracer on the client.
  • The dev pprof handler is now stored on the server, registered only in dev mode, and cleaned up during shutdown, which fixes the earlier goroutine/lifecycle leak.

These changes are cohesive with the stated PR goal of a unified HTTP middleware pattern plus improved observability, with no additional correctness issues in the shown paths.

Also applies to: 327-443, 942-1042, 1084-1096, 1110-1163, 1220-1253, 1299-1333

framework/tracing/tracer.go (1)

13-52: Overall Tracer implementation and streaming integration look solid and align with the new tracing architecture.

  • Tracer cleanly wraps TraceStore and delegates trace/span lifecycle operations (CreateTrace, EndTrace, ReleaseTrace, StartSpan, EndSpan, attribute/event setters) via the spanHandle abstraction, matching the schemas.Tracer interface (enforced by the compile‑time assertion).
  • LLM request/response/error attribute population uses helper functions from llmspan.go, keeping schema logic centralized while keeping tracer lightweight.
  • Deferred span support (StoreDeferredSpan, GetDeferredSpanHandle, ClearDeferredSpan, GetDeferredSpanID) correctly keys by traceID and pairs with the store’s deferred span map.
  • Streaming integration via the embedded streaming.Accumulator (CreateStreamAccumulator, CleanupStreamAccumulator, ProcessStreamingChunk, GetAccumulator) is well‑designed: ProcessStreamingChunk wraps the original BifrostContext to inject traceID as requestID and preserve the stream end indicator, then maps accumulator results into schemas.StreamAccumulatorResult for plugins.

Aside from the accumulator cleanup and deterministic ordering concerns noted separately, this file is a good fit for the new tracing+plugins v2 stack.

Also applies to: 61-171, 172-231, 460-531

framework/tracing/llmspan.go (1)

1279-1283: Consider using strings.Builder for string concatenation in loops.

For consistency with line 1315 (which already uses strings.Builder), consider refactoring the string concatenation loops at lines 1279-1283 and 1289-1292 to use strings.Builder as well.

While the performance impact is likely minimal for typical workloads (small numbers of blocks), using strings.Builder throughout provides consistency and better performance characteristics for edge cases with many blocks.

🔎 Example refactor for lines 1279-1283
 		} else if msg.Content.ContentBlocks != nil {
+			var builder strings.Builder
 			for _, block := range msg.Content.ContentBlocks {
 				if block.Text != nil {
-					content += *block.Text
+					builder.WriteString(*block.Text)
 				}
 			}
+			content = builder.String()
 		}

Also applies to: 1289-1292

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e8ca91d and 5186ad3.

⛔ Files ignored due to path filters (3)
  • examples/plugins/hello-world/go.sum is excluded by !**/*.sum
  • plugins/governance/go.sum is excluded by !**/*.sum
  • transports/go.sum is excluded by !**/*.sum
📒 Files selected for processing (100)
  • .github/workflows/release-pipeline.yml
  • .github/workflows/scripts/push-mintlify-changelog.sh
  • .github/workflows/scripts/release-bifrost-http.sh
  • .github/workflows/scripts/release-framework.sh
  • core/bifrost.go
  • core/bifrost_test.go
  • core/changelog.md
  • core/mcp.go
  • core/providers/utils/utils.go
  • core/schemas/bifrost.go
  • core/schemas/context.go
  • core/schemas/plugin.go
  • core/schemas/trace.go
  • core/schemas/tracer.go
  • core/version
  • docs/docs.json
  • docs/plugins/getting-started.mdx
  • docs/plugins/migration-guide.mdx
  • docs/plugins/writing-plugin.mdx
  • examples/plugins/hello-world/go.mod
  • examples/plugins/hello-world/main.go
  • framework/changelog.md
  • framework/configstore/tables/mcp.go
  • framework/plugins/dynamicplugin.go
  • framework/plugins/dynamicplugin_test.go
  • framework/streaming/accumulator.go
  • framework/streaming/audio.go
  • framework/streaming/chat.go
  • framework/streaming/responses.go
  • framework/streaming/transcription.go
  • framework/streaming/types.go
  • framework/tracing/helpers.go
  • framework/tracing/llmspan.go
  • framework/tracing/propagation.go
  • framework/tracing/store.go
  • framework/tracing/tracer.go
  • framework/version
  • plugins/governance/changelog.md
  • plugins/governance/go.mod
  • plugins/governance/main.go
  • plugins/governance/version
  • plugins/jsonparser/changelog.md
  • plugins/jsonparser/main.go
  • plugins/jsonparser/version
  • plugins/logging/changelog.md
  • plugins/logging/main.go
  • plugins/logging/utils.go
  • plugins/logging/version
  • plugins/maxim/changelog.md
  • plugins/maxim/main.go
  • plugins/maxim/version
  • plugins/mocker/changelog.md
  • plugins/mocker/main.go
  • plugins/mocker/version
  • plugins/otel/changelog.md
  • plugins/otel/converter.go
  • plugins/otel/main.go
  • plugins/otel/ttlsyncmap.go
  • plugins/otel/version
  • plugins/semanticcache/changelog.md
  • plugins/semanticcache/main.go
  • plugins/semanticcache/version
  • plugins/telemetry/changelog.md
  • plugins/telemetry/main.go
  • plugins/telemetry/version
  • transports/bifrost-http/handlers/cache.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/devpprof.go
  • transports/bifrost-http/handlers/devpprof_unix.go
  • transports/bifrost-http/handlers/devpprof_windows.go
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/handlers/health.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/handlers/logging.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/handlers/mcpserver.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • transports/bifrost-http/handlers/plugins.go
  • transports/bifrost-http/handlers/providers.go
  • transports/bifrost-http/handlers/session.go
  • transports/bifrost-http/handlers/ui.go
  • transports/bifrost-http/handlers/websocket.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/lib/config_test.go
  • transports/bifrost-http/lib/middleware.go
  • transports/bifrost-http/server/server.go
  • transports/changelog.md
  • transports/go.mod
  • transports/version
  • ui/app/clientLayout.tsx
  • ui/app/workspace/logs/views/logDetailsSheet.tsx
  • ui/components/devProfiler.tsx
  • ui/components/sidebar.tsx
  • ui/components/ui/sheet.tsx
  • ui/components/ui/switch.tsx
  • ui/lib/store/apis/devApi.ts
  • ui/lib/store/apis/index.ts
💤 Files with no reviewable changes (3)
  • framework/configstore/tables/mcp.go
  • core/mcp.go
  • plugins/otel/ttlsyncmap.go
✅ Files skipped from review due to trivial changes (3)
  • plugins/semanticcache/version
  • plugins/otel/version
  • plugins/maxim/version
🚧 Files skipped from review as they are similar to previous changes (35)
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/handlers/cache.go
  • transports/bifrost-http/handlers/mcp.go
  • ui/components/ui/switch.tsx
  • plugins/telemetry/version
  • framework/streaming/audio.go
  • plugins/governance/version
  • core/bifrost_test.go
  • .github/workflows/scripts/release-bifrost-http.sh
  • plugins/mocker/version
  • framework/version
  • plugins/mocker/main.go
  • transports/bifrost-http/handlers/health.go
  • ui/app/clientLayout.tsx
  • plugins/logging/utils.go
  • transports/version
  • transports/bifrost-http/handlers/session.go
  • core/schemas/context.go
  • transports/bifrost-http/handlers/plugins.go
  • framework/streaming/chat.go
  • examples/plugins/hello-world/main.go
  • plugins/jsonparser/main.go
  • framework/streaming/types.go
  • ui/app/workspace/logs/views/logDetailsSheet.tsx
  • plugins/governance/go.mod
  • transports/bifrost-http/handlers/middlewares_test.go
  • examples/plugins/hello-world/go.mod
  • framework/streaming/transcription.go
  • transports/bifrost-http/lib/config_test.go
  • core/version
  • plugins/logging/version
  • core/schemas/bifrost.go
  • transports/bifrost-http/lib/middleware.go
  • docs/docs.json
  • transports/bifrost-http/handlers/integrations.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/plugins/migration-guide.mdx
  • ui/lib/store/apis/index.ts
  • ui/components/sidebar.tsx
  • transports/bifrost-http/handlers/ui.go
  • plugins/maxim/main.go
  • core/schemas/plugin.go
  • core/changelog.md
  • transports/bifrost-http/handlers/logging.go
  • transports/bifrost-http/handlers/devpprof_unix.go
  • framework/plugins/dynamicplugin.go
  • transports/bifrost-http/handlers/mcpserver.go
  • plugins/governance/main.go
  • transports/bifrost-http/integrations/router.go
  • framework/plugins/dynamicplugin_test.go
  • core/providers/utils/utils.go
  • ui/components/devProfiler.tsx
  • plugins/semanticcache/main.go
  • transports/bifrost-http/handlers/devpprof.go
  • plugins/otel/main.go
  • plugins/governance/changelog.md
  • ui/lib/store/apis/devApi.ts
  • plugins/semanticcache/changelog.md
  • plugins/telemetry/changelog.md
  • plugins/telemetry/main.go
  • transports/bifrost-http/handlers/providers.go
  • plugins/jsonparser/changelog.md
  • framework/tracing/store.go
  • framework/streaming/accumulator.go
  • transports/bifrost-http/handlers/inference.go
  • plugins/maxim/changelog.md
  • framework/tracing/propagation.go
  • core/bifrost.go
  • plugins/otel/converter.go
  • framework/tracing/helpers.go
  • transports/bifrost-http/handlers/middlewares.go
  • core/schemas/trace.go
  • transports/go.mod
  • ui/components/ui/sheet.tsx
  • transports/bifrost-http/server/server.go
  • plugins/mocker/changelog.md
  • docs/plugins/getting-started.mdx
  • framework/streaming/responses.go
  • plugins/logging/changelog.md
  • transports/changelog.md
  • core/schemas/tracer.go
  • framework/tracing/tracer.go
  • plugins/logging/main.go
  • plugins/otel/changelog.md
  • docs/plugins/writing-plugin.mdx
  • transports/bifrost-http/handlers/devpprof_windows.go
  • framework/changelog.md
  • transports/bifrost-http/handlers/config.go
  • framework/tracing/llmspan.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/handlers/websocket.go
  • plugins/jsonparser/version
🧠 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/handlers/ui.go
  • plugins/maxim/main.go
  • core/schemas/plugin.go
  • transports/bifrost-http/handlers/logging.go
  • transports/bifrost-http/handlers/devpprof_unix.go
  • framework/plugins/dynamicplugin.go
  • transports/bifrost-http/handlers/mcpserver.go
  • plugins/governance/main.go
  • transports/bifrost-http/integrations/router.go
  • framework/plugins/dynamicplugin_test.go
  • core/providers/utils/utils.go
  • plugins/semanticcache/main.go
  • transports/bifrost-http/handlers/devpprof.go
  • plugins/otel/main.go
  • plugins/telemetry/main.go
  • transports/bifrost-http/handlers/providers.go
  • framework/tracing/store.go
  • framework/streaming/accumulator.go
  • transports/bifrost-http/handlers/inference.go
  • framework/tracing/propagation.go
  • core/bifrost.go
  • plugins/otel/converter.go
  • framework/tracing/helpers.go
  • transports/bifrost-http/handlers/middlewares.go
  • core/schemas/trace.go
  • transports/bifrost-http/server/server.go
  • framework/streaming/responses.go
  • core/schemas/tracer.go
  • framework/tracing/tracer.go
  • plugins/logging/main.go
  • transports/bifrost-http/handlers/devpprof_windows.go
  • transports/bifrost-http/handlers/config.go
  • framework/tracing/llmspan.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/handlers/websocket.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/ui.go
  • transports/bifrost-http/handlers/logging.go
  • transports/bifrost-http/handlers/devpprof_unix.go
  • transports/bifrost-http/handlers/mcpserver.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/handlers/devpprof.go
  • transports/bifrost-http/handlers/providers.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/server/server.go
  • transports/bifrost-http/handlers/devpprof_windows.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/handlers/websocket.go
📚 Learning: 2025-12-22T10:50:40.990Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1154
File: plugins/governance/store.go:1165-1186
Timestamp: 2025-12-22T10:50:40.990Z
Learning: In the Bifrost governance plugin, budgets and rate limits have 1:1 relationships with their parent entities (virtual keys, teams, customers). Do not assume sharing; ensure cascade deletion logic only deletes budgets/rate limits when there are no shared references. Enforce invariants in code and add tests to verify no cross-entity sharing and that cascade deletes only remove the specific child of the parent. If a counterexample arises, adjust data model or add guards.

Applied to files:

  • plugins/governance/main.go
📚 Learning: 2025-12-19T09:26:54.961Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/providers/utils/utils.go:1050-1051
Timestamp: 2025-12-19T09:26:54.961Z
Learning: Update streaming end-marker handling so HuggingFace is treated as a non-[DONE] provider for backends that do not emit a DONE marker (e.g., meta llama on novita). In core/providers/utils/utils.go, adjust ProviderSendsDoneMarker() (or related logic) to detect providers that may not emit DONE and avoid relying on DONE as the sole end signal. Add tests to cover both DONE-emitting and non-DONE backends, with clear documentation in code comments explaining the rationale and any fallback behavior.

Applied to files:

  • core/providers/utils/utils.go
📚 Learning: 2025-12-24T10:55:31.424Z
Learnt from: akshaydeo
Repo: maximhq/bifrost PR: 888
File: framework/tracing/propagation.go:24-36
Timestamp: 2025-12-24T10:55:31.424Z
Learning: In framework/tracing/propagation.go, ensure ExtractParentID() returns ctx.TraceID (not ctx.ParentID) because the parent request's traceparent header carries the trace ID used for continuing the trace.

Applied to files:

  • framework/tracing/propagation.go
🧬 Code graph analysis (28)
transports/bifrost-http/handlers/ui.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
plugins/maxim/main.go (2)
core/schemas/plugin.go (2)
  • Plugin (71-97)
  • BifrostHTTPMiddleware (38-38)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-27)
core/schemas/plugin.go (2)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-27)
core/schemas/trace.go (1)
  • Trace (10-19)
transports/bifrost-http/handlers/logging.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
framework/plugins/dynamicplugin.go (2)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
examples/plugins/hello-world/main.go (2)
  • GetName (15-17)
  • HTTPTransportMiddleware (19-27)
transports/bifrost-http/handlers/mcpserver.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
plugins/governance/main.go (3)
core/utils.go (1)
  • Ptr (56-58)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-27)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/integrations/router.go (3)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
core/schemas/bifrost.go (6)
  • ResponsesRequest (97-97)
  • EmbeddingRequest (99-99)
  • SpeechRequest (100-100)
  • TranscriptionRequest (102-102)
  • BifrostContextKeyDeferTraceCompletion (147-147)
  • BifrostContextKeyTraceCompleter (148-148)
transports/bifrost-http/handlers/inference.go (5)
  • ChatRequest (172-176)
  • ResponsesRequest (266-270)
  • EmbeddingRequest (273-277)
  • SpeechRequest (279-283)
  • TranscriptionRequest (285-289)
framework/plugins/dynamicplugin_test.go (1)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-27)
ui/components/devProfiler.tsx (1)
ui/lib/utils/port.ts (1)
  • isDevelopmentMode (110-112)
plugins/semanticcache/main.go (2)
core/schemas/plugin.go (2)
  • Plugin (71-97)
  • BifrostHTTPMiddleware (38-38)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-27)
transports/bifrost-http/handlers/devpprof.go (3)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/lib/middleware.go (1)
  • ChainMiddlewares (11-23)
transports/bifrost-http/handlers/utils.go (1)
  • SendJSON (16-22)
plugins/otel/main.go (5)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-27)
core/schemas/plugin.go (2)
  • BifrostHTTPMiddleware (38-38)
  • ObservabilityPlugin (123-137)
core/schemas/context.go (1)
  • BifrostContext (32-42)
core/schemas/bifrost.go (3)
  • BifrostRequest (173-193)
  • BifrostResponse (335-355)
  • BifrostError (474-483)
core/schemas/trace.go (1)
  • Trace (10-19)
plugins/telemetry/main.go (2)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-27)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/providers.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
framework/tracing/store.go (2)
core/schemas/trace.go (6)
  • Trace (10-19)
  • Span (52-65)
  • SpanEvent (107-111)
  • SpanKind (115-115)
  • SpanStatusUnset (147-147)
  • SpanStatus (143-143)
framework/tracing/helpers.go (3)
  • GetTrace (23-29)
  • AddSpan (32-38)
  • EndSpan (50-56)
framework/streaming/accumulator.go (1)
framework/streaming/types.go (3)
  • StreamAccumulator (103-115)
  • ChatStreamChunk (77-87)
  • ResponsesStreamChunk (90-100)
transports/bifrost-http/handlers/inference.go (2)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
core/schemas/bifrost.go (2)
  • BifrostContextKeyDeferTraceCompletion (147-147)
  • BifrostContextKeyTraceCompleter (148-148)
core/bifrost.go (5)
core/schemas/tracer.go (1)
  • DefaultTracer (181-183)
core/schemas/trace.go (12)
  • SpanKindFallback (129-129)
  • AttrProviderName (159-159)
  • AttrRequestModel (160-160)
  • SpanStatusError (151-151)
  • SpanStatusOk (149-149)
  • SpanKind (115-115)
  • SpanKindLLMCall (121-121)
  • SpanKindPlugin (123-123)
  • AttrPluginInvocations (202-202)
  • AttrPluginAvgDurationMs (203-203)
  • AttrPluginTotalDurationMs (204-204)
  • AttrPluginErrorCount (205-205)
framework/tracing/helpers.go (1)
  • EndSpan (50-56)
core/utils.go (1)
  • IsStreamRequestType (197-199)
core/schemas/context.go (1)
  • NewBifrostContextWithTimeout (70-73)
plugins/otel/converter.go (2)
plugins/otel/main.go (1)
  • OtelPlugin (58-75)
core/schemas/trace.go (17)
  • Trace (10-19)
  • Span (52-65)
  • SpanKind (115-115)
  • SpanKindLLMCall (121-121)
  • SpanKindHTTPRequest (131-131)
  • SpanKindPlugin (123-123)
  • SpanKindInternal (139-139)
  • SpanKindRetry (127-127)
  • SpanKindFallback (129-129)
  • SpanKindMCPTool (125-125)
  • SpanKindEmbedding (133-133)
  • SpanKindSpeech (135-135)
  • SpanKindTranscription (137-137)
  • SpanStatus (143-143)
  • SpanStatusOk (149-149)
  • SpanStatusError (151-151)
  • SpanEvent (107-111)
framework/tracing/helpers.go (3)
core/schemas/bifrost.go (1)
  • BifrostContextKeyTraceID (143-143)
framework/tracing/store.go (1)
  • TraceStore (25-37)
core/schemas/trace.go (5)
  • Trace (10-19)
  • SpanKind (115-115)
  • Span (52-65)
  • SpanStatus (143-143)
  • SpanEvent (107-111)
transports/bifrost-http/handlers/middlewares.go (5)
core/schemas/plugin.go (3)
  • BifrostHTTPMiddleware (38-38)
  • ObservabilityPlugin (123-137)
  • Plugin (71-97)
transports/bifrost-http/lib/middleware.go (1)
  • ChainMiddlewares (11-23)
framework/tracing/propagation.go (1)
  • ExtractParentID (74-84)
core/schemas/bifrost.go (4)
  • BifrostContextKeyTraceID (143-143)
  • BifrostContextKeyTraceCompleter (148-148)
  • BifrostContextKeySpanID (144-144)
  • BifrostContextKeyDeferTraceCompletion (147-147)
core/schemas/trace.go (3)
  • SpanKindHTTPRequest (131-131)
  • SpanStatusError (151-151)
  • SpanStatusOk (149-149)
core/schemas/trace.go (2)
framework/tracing/helpers.go (1)
  • AddSpan (32-38)
ui/lib/constants/logs.ts (1)
  • Status (161-161)
framework/streaming/responses.go (1)
framework/streaming/types.go (1)
  • AccumulatedData (28-48)
core/schemas/tracer.go (6)
core/schemas/chatcompletions.go (1)
  • BifrostLLMUsage (845-852)
core/schemas/speech.go (1)
  • BifrostSpeechResponse (22-29)
core/schemas/transcriptions.go (1)
  • BifrostTranscriptionResponse (16-26)
core/schemas/trace.go (2)
  • SpanKind (115-115)
  • SpanStatus (143-143)
core/schemas/bifrost.go (2)
  • BifrostRequest (173-193)
  • BifrostResponse (335-355)
core/schemas/context.go (1)
  • BifrostContext (32-42)
framework/tracing/tracer.go (9)
framework/tracing/store.go (1)
  • TraceStore (25-37)
framework/streaming/accumulator.go (2)
  • Accumulator (14-30)
  • NewAccumulator (440-480)
core/schemas/tracer.go (1)
  • StreamAccumulatorResult (15-33)
framework/tracing/helpers.go (3)
  • GetTraceID (11-20)
  • EndSpan (50-56)
  • GetTrace (23-29)
core/schemas/bifrost.go (5)
  • BifrostContextKeySpanID (144-144)
  • BifrostRequest (173-193)
  • BifrostResponse (335-355)
  • BifrostContextKeyRequestID (124-124)
  • BifrostContextKeyStreamEndIndicator (131-131)
framework/tracing/llmspan.go (3)
  • PopulateRequestAttributes (14-60)
  • PopulateResponseAttributes (64-106)
  • PopulateErrorAttributes (109-124)
core/schemas/chatcompletions.go (6)
  • BifrostResponseChoice (736-744)
  • ChatReasoningDetails (755-762)
  • ChatAssistantMessageToolCall (710-716)
  • ChatStreamResponseChoice (783-785)
  • ChatAssistantMessage (649-656)
  • ChatNonStreamResponseChoice (777-780)
core/schemas/context.go (2)
  • BifrostContext (32-42)
  • NewBifrostContext (47-65)
framework/streaming/types.go (1)
  • StreamResponseTypeFinal (24-24)
transports/bifrost-http/handlers/config.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/websocket.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
🪛 markdownlint-cli2 (0.18.1)
transports/changelog.md

16-16: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

Comment thread framework/tracing/tracer.go
Comment thread transports/bifrost-http/server/server.go
Comment thread ui/components/devProfiler.tsx Outdated
@akshaydeo akshaydeo force-pushed the 11-19-plugins_v2_architecture branch from 5186ad3 to b12158e Compare December 25, 2025 14:25
Copy link
Copy Markdown
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 (2)
transports/bifrost-http/server/server.go (1)

203-212: Avoid returning Config.Providers map outside the lock (concurrent map access risk).

GovernanceInMemoryStore.GetConfiguredProviders returns s.Config.Providers while only briefly holding Config.Mu.RLock. Callers keep a reference to the underlying map after the lock is released, but writers (e.g., AddProvider/UpdateProviderConfig/RemoveProvider) mutate the same map under Config.Mu. Concurrent map read/write will panic at runtime.

Return a copy instead of the shared map, e.g.:

Proposed fix
 func (s *GovernanceInMemoryStore) GetConfiguredProviders() map[schemas.ModelProvider]configstore.ProviderConfig {
-	// Use read lock for thread-safe access - no need to copy on hot path
-	s.Config.Mu.RLock()
-	defer s.Config.Mu.RUnlock()
-	return s.Config.Providers
+	s.Config.Mu.RLock()
+	defer s.Config.Mu.RUnlock()
+
+	out := make(map[schemas.ModelProvider]configstore.ProviderConfig, len(s.Config.Providers))
+	for k, v := range s.Config.Providers {
+		out[k] = v
+	}
+	return out
 }
transports/bifrost-http/lib/config.go (1)

1509-1527: Guard against nil PricingSyncInterval in initFrameworkConfigFromFile.

When loading from file and no framework config exists in the store, you do:

if configData.FrameworkConfig != nil && configData.FrameworkConfig.Pricing != nil {
    pricingConfig.PricingURL = configData.FrameworkConfig.Pricing.PricingURL
    syncDuration := time.Duration(*configData.FrameworkConfig.Pricing.PricingSyncInterval) * time.Second
    pricingConfig.PricingSyncInterval = &syncDuration
}

If pricing.pricing_sync_interval is omitted from config.json, PricingSyncInterval will be nil and this panics at startup.

Align with the safer pattern you already use in initDefaultFrameworkConfig by checking for nil (and optionally >0) before dereferencing, e.g.:

Proposed fix
-} else if configData.FrameworkConfig != nil && configData.FrameworkConfig.Pricing != nil {
-    pricingConfig.PricingURL = configData.FrameworkConfig.Pricing.PricingURL
-    syncDuration := time.Duration(*configData.FrameworkConfig.Pricing.PricingSyncInterval) * time.Second
-    pricingConfig.PricingSyncInterval = &syncDuration
-}
+} else if cfg := configData.FrameworkConfig; cfg != nil && cfg.Pricing != nil {
+    pricingConfig.PricingURL = cfg.Pricing.PricingURL
+    if cfg.Pricing.PricingSyncInterval != nil && *cfg.Pricing.PricingSyncInterval > 0 {
+        syncDuration := time.Duration(*cfg.Pricing.PricingSyncInterval) * time.Second
+        pricingConfig.PricingSyncInterval = &syncDuration
+    }
+}
♻️ Duplicate comments (16)
plugins/logging/version (1)

1-1: Version bump inconsistent with breaking changes in this PR stack.

The patch-level bump to 1.3.53 does not reflect the breaking change introduced by the HTTPTransportMiddleware migration. Given that:

  • The transports package bumped to 1.4.0-prerelease1
  • This PR targets the v1.4.0 branch
  • Plugins implementing TransportInterceptor must be rewritten to use HTTPTransportMiddleware

Bump to 1.4.0 or 1.4.0-prerelease1 to signal the breaking change and maintain version consistency across the stack.

ui/app/workspace/logs/views/logDetailsSheet.tsx (1)

183-186: Add aria-label for accessibility.

The icon-only button lacks an accessible name for screen readers. Add an aria-label to describe the button's purpose.

🔎 Proposed fix
 <DropdownMenuTrigger asChild>
-  <Button variant="ghost" size="icon">
+  <Button variant="ghost" size="icon" aria-label="Open actions menu">
     <MoreVertical className="h-3 w-3" />
   </Button>
 </DropdownMenuTrigger>
plugins/otel/changelog.md (1)

1-20: Migration guide link requires verification.

The changelog references /docs/plugins/migration-guide on line 19. This has been flagged in previous reviews—ensure the migration guide exists at the referenced path or update the link to point to the correct documentation location.

core/schemas/trace.go (3)

40-49: Critical concurrency issue: Trace.Reset() still lacks mutex protection.

This method modifies all fields including the Spans slice without holding the mutex, creating a data race if called concurrently with AddSpan or GetSpan. This issue was previously flagged but remains unresolved.

🔎 Proposed fix
 // Reset clears the trace for reuse from pool
 func (t *Trace) Reset() {
+	t.mu.Lock()
+	defer t.mu.Unlock()
 	t.TraceID = ""
 	t.ParentID = ""
 	t.RootSpan = nil
 	t.Spans = t.Spans[:0]
 	t.StartTime = time.Time{}
 	t.EndTime = time.Time{}
 	t.Attributes = nil
 }

84-89: Critical concurrency issue: Span.End() lacks mutex protection.

Unlike SetAttribute and AddEvent, the End method modifies EndTime, Status, and StatusMsg without holding the mutex. This creates a data race if called concurrently with other span operations. This issue was previously flagged but remains unresolved.

🔎 Proposed fix
 // End marks the span as complete with the given status
 func (s *Span) End(status SpanStatus, statusMsg string) {
+	s.mu.Lock()
+	defer s.mu.Unlock()
 	s.EndTime = time.Now()
 	s.Status = status
 	s.StatusMsg = statusMsg
 }

91-104: Critical concurrency issue: Span.Reset() lacks mutex protection.

Similar to Trace.Reset(), this method modifies fields without acquiring the mutex, creating a data race when a span is reused from a pool while another goroutine holds a reference. This issue was previously flagged but remains unresolved.

🔎 Proposed fix
 // Reset clears the span for reuse from pool
 func (s *Span) Reset() {
+	s.mu.Lock()
+	defer s.mu.Unlock()
 	s.SpanID = ""
 	s.ParentID = ""
 	s.TraceID = ""
 	s.Name = ""
 	s.Kind = SpanKindUnspecified
 	s.StartTime = time.Time{}
 	s.EndTime = time.Time{}
 	s.Status = SpanStatusUnset
 	s.StatusMsg = ""
 	s.Attributes = nil
 	s.Events = s.Events[:0]
 }
examples/plugins/hello-world/main.go (1)

19-27: Example demonstrates broken data flow between middleware and hooks.

The middleware sets a value on fasthttp.RequestCtx (line 23) but PreHook and PostHook (lines 30, 39-40) attempt to read from *schemas.BifrostContext. These are different context types, so the value will always be nil.

Either remove the context value reads from the hooks (since the propagation doesn't work), or add a comment explaining that middleware operates on the HTTP transport context while hooks operate on the Bifrost internal context.

🔎 Suggested fix to remove broken context reads
 func PreHook(ctx *schemas.BifrostContext, req *schemas.BifrostRequest) (*schemas.BifrostRequest, *schemas.PluginShortCircuit, error) {
-	value1 := ctx.Value(schemas.BifrostContextKey("hello-world-plugin-transport-interceptor"))
-	fmt.Println("value1:", value1)
 	ctx.SetValue(schemas.BifrostContextKey("hello-world-plugin-pre-hook"), "pre-hook-value")
 	fmt.Println("PreHook called")
 	return req, nil, nil
 }

 func PostHook(ctx *schemas.BifrostContext, resp *schemas.BifrostResponse, bifrostErr *schemas.BifrostError) (*schemas.BifrostResponse, *schemas.BifrostError, error) {
 	fmt.Println("PostHook called")
-	value1 := ctx.Value(schemas.BifrostContextKey("hello-world-plugin-transport-interceptor"))
-	fmt.Println("value1:", value1)
 	value2 := ctx.Value(schemas.BifrostContextKey("hello-world-plugin-pre-hook"))
 	fmt.Println("value2:", value2)
 	return resp, bifrostErr, nil
 }
framework/changelog.md (1)

1-1: Fix grammar and expand tracing framework description.

Line 1 contains a grammar error: "to enabling e2e tracing" should be "to enable e2e tracing".

Additionally, consider briefly explaining what the tracing framework provides (e.g., "enables plugins to report spans and traces for distributed tracing integration") to match the detail level of the HTTPTransportMiddleware breaking change documentation below.

🔎 Suggested fix
-- feat: adds new tracing framework for allowing plugins to enable e2e tracing
+- feat: adds new tracing framework to enable e2e tracing. The framework allows plugins to report spans and traces for distributed tracing integration with observability backends.
docs/plugins/migration-guide.mdx (1)

247-250: Non-existent version tag will break user migration.

Line 249 references @v1.4.0 which doesn't exist yet (this PR is open and targets the v1.4.0 branch). Users following this guide will encounter an error.

Before merging, either:

  1. Create and push the v1.4.0 tag, or
  2. Update to reference an existing version/commit, or
  3. Add a note: "Replace @v1.4.0 with the latest release version after v1.4.0 is published"
🔎 Suggested fix with placeholder
 2. **Update Bifrost to v1.4.x:**
    ```bash
-   go get github.com/maximhq/bifrost/core@v1.4.0
+   # Replace @v1.4.0 with the actual release version once published
+   go get github.com/maximhq/bifrost/core@v1.4.0
    ```

Or reference the target branch until release:

-   go get github.com/maximhq/bifrost/core@v1.4.0
+   go get github.com/maximhq/bifrost/core@latest
transports/bifrost-http/integrations/router.go (1)

502-693: Non-streaming requests use wrong context - breaks tracing and plugin propagation.

All non-streaming Bifrost client calls (lines 514, 534, 557, 580, 603, 626, 660) pass ctx (*fasthttp.RequestCtx) instead of the converted Bifrost context (*bifrostCtx). The comment at line 503 acknowledges that the "cancellable context from ConvertToBifrostContext" should be used, but the code doesn't follow this.

This breaks:

  • Tracing (all BifrostContextKey* values are on bifrostCtx, not ctx)
  • Plugin context propagation
  • Timeout and cancellation behavior
🔎 Proposed fix

Apply this pattern to all non-streaming calls:

 	case bifrostReq.ListModelsRequest != nil:
-		listModelsResponse, bifrostErr := g.client.ListModelsRequest(ctx, bifrostReq.ListModelsRequest)
+		listModelsResponse, bifrostErr := g.client.ListModelsRequest(*bifrostCtx, bifrostReq.ListModelsRequest)
 
 	case bifrostReq.TextCompletionRequest != nil:
-		textCompletionResponse, bifrostErr := g.client.TextCompletionRequest(ctx, bifrostReq.TextCompletionRequest)
+		textCompletionResponse, bifrostErr := g.client.TextCompletionRequest(*bifrostCtx, bifrostReq.TextCompletionRequest)
 
 	case bifrostReq.ChatRequest != nil:
-		chatResponse, bifrostErr := g.client.ChatCompletionRequest(ctx, bifrostReq.ChatRequest)
+		chatResponse, bifrostErr := g.client.ChatCompletionRequest(*bifrostCtx, bifrostReq.ChatRequest)
 
 	case bifrostReq.ResponsesRequest != nil:
-		responsesResponse, bifrostErr := g.client.ResponsesRequest(ctx, bifrostReq.ResponsesRequest)
+		responsesResponse, bifrostErr := g.client.ResponsesRequest(*bifrostCtx, bifrostReq.ResponsesRequest)
 
 	case bifrostReq.EmbeddingRequest != nil:
-		embeddingResponse, bifrostErr := g.client.EmbeddingRequest(ctx, bifrostReq.EmbeddingRequest)
+		embeddingResponse, bifrostErr := g.client.EmbeddingRequest(*bifrostCtx, bifrostReq.EmbeddingRequest)
 
 	case bifrostReq.SpeechRequest != nil:
-		speechResponse, bifrostErr := g.client.SpeechRequest(ctx, bifrostReq.SpeechRequest)
+		speechResponse, bifrostErr := g.client.SpeechRequest(*bifrostCtx, bifrostReq.SpeechRequest)
 
 	case bifrostReq.TranscriptionRequest != nil:
-		transcriptionResponse, bifrostErr := g.client.TranscriptionRequest(ctx, bifrostReq.TranscriptionRequest)
+		transcriptionResponse, bifrostErr := g.client.TranscriptionRequest(*bifrostCtx, bifrostReq.TranscriptionRequest)
core/providers/utils/utils.go (1)

1424-1513: Pass context by value, not by pointer (Go convention).

The function signature completeDeferredSpan(ctx *context.Context, ...) violates Go conventions. Contexts should always be passed by value, never by pointer. The context.Context type is designed to be passed by value and is safe for concurrent use.

🔎 Proposed fix
-func completeDeferredSpan(ctx *context.Context, result *schemas.BifrostResponse, err *schemas.BifrostError) {
-	if ctx == nil {
+func completeDeferredSpan(ctx context.Context, result *schemas.BifrostResponse, err *schemas.BifrostError) {
+	if ctx == nil || ctx == context.Background() || ctx == context.TODO() {
 		return
 	}
 
-	traceID, ok := (*ctx).Value(schemas.BifrostContextKeyTraceID).(string)
+	traceID, ok := ctx.Value(schemas.BifrostContextKeyTraceID).(string)
 	if !ok || traceID == "" {
 		return
 	}
 
-	tracerVal := (*ctx).Value(schemas.BifrostContextKeyTracer)
+	tracerVal := ctx.Value(schemas.BifrostContextKeyTracer)
 	// ... continue pattern for all (*ctx).Value calls

And update all call sites (lines 901, 928, 952, 978):

-			completeDeferredSpan(&ctx, processedResponse, processedError)
+			completeDeferredSpan(ctx, processedResponse, processedError)
plugins/governance/main.go (1)

233-261: Add prefix validation for x-bf-vk header for consistency.

The x-bf-vk header on lines 236-238 is returned without validating that it starts with VirtualKeyPrefix, unlike the other headers (Authorization, x-api-key, x-goog-api-key) which all validate the prefix. This inconsistency could allow invalid virtual keys to pass through when provided via the x-bf-vk header.

🔎 Proposed fix
 	vkHeader := ctx.Request.Header.Peek("x-bf-vk")
 	if string(vkHeader) != "" {
-		return bifrost.Ptr(string(vkHeader))
+		vkValue := string(vkHeader)
+		if strings.HasPrefix(strings.ToLower(vkValue), VirtualKeyPrefix) {
+			return bifrost.Ptr(vkValue)
+		}
 	}
transports/bifrost-http/handlers/middlewares.go (1)

247-256: TracingMiddleware still passes *fasthttp.RequestCtx into StartSpan, which expects context.Context

StartSpan in core/schemas.Tracer takes a context.Context, but here you call:

spanCtx, rootSpan := m.tracer.Load().StartSpan(ctx, string(ctx.RequestURI()), schemas.SpanKindHTTPRequest)

where ctx is *fasthttp.RequestCtx. This won’t type‑check and also prevents StartSpan from seeing the trace ID via ctx.Value(...).

You likely want to construct a proper context.Context that carries the W3C trace ID, for example:

goCtx := context.Background()
if v := ctx.UserValue(schemas.BifrostContextKeyTraceID); v != nil {
    if traceID, ok := v.(string); ok && traceID != "" {
        goCtx = context.WithValue(goCtx, schemas.BifrostContextKeyTraceID, traceID)
    }
}
spanCtx, rootSpan := m.tracer.Load().StartSpan(goCtx, string(ctx.RequestURI()), schemas.SpanKindHTTPRequest)

and then use spanCtx only for span propagation (e.g., to extract the span ID), keeping *fasthttp.RequestCtx for HTTP.

plugins/otel/converter.go (1)

148-171: Avoid unsafe uint64int64 cast in anyToKeyValue

The uint64 branch does:

case uint64:
    return kvInt(key, int64(v))

If v exceeds math.MaxInt64, this overflows and produces a negative OTEL attribute value. Even if unlikely, it makes debugging harder.

Consider clamping or preserving unsigned semantics, for example:

case uint64:
    if v > math.MaxInt64 {
        // optionally log a warning
        return kvInt(key, math.MaxInt64)
    }
    return kvInt(key, int64(v))
framework/tracing/llmspan.go (1)

191-193: Namespace ExtraParams keys to avoid clobbering core attributes.

Across the request helpers you write user‑supplied ExtraParams directly into attrs:

for k, v := range req.Params.ExtraParams {
    attrs[k] = fmt.Sprintf("%v", v)
}

This allows callers to overwrite standard attributes (e.g., gen_ai.request.model, gen_ai.provider) just by choosing the same key, which was already noted in a previous review.

Consider prefixing these keys (and doing it consistently in all helpers) to avoid collisions, e.g.:

Proposed pattern
- for k, v := range req.Params.ExtraParams {
-     attrs[k] = fmt.Sprintf("%v", v)
- }
+ for k, v := range req.Params.ExtraParams {
+     key := "gen_ai.extra." + k // or a similar, well-documented prefix
+     if _, exists := attrs[key]; !exists {
+         attrs[key] = fmt.Sprintf("%v", v)
+     }
+ }

Apply the same pattern to ExtraParams in chat, text, embedding, responses, batch, and file helpers.

Also applies to: 298-300, 365-367, 575-577, 708-710, 738-740, 753-755, 769-770, 783-785, 958-960, 982-984, 997-999, 1012-1014, 1027-1029

framework/tracing/tracer.go (1)

393-455: Make buildCompleteResponseFromChunks deterministic by sorting indices.

The comments say “Sort choice indices for deterministic output” and “Sort tool calls by index”, but:

  • choiceIndices is built from a map and then iterated without sorting.
  • tcIndices is also built from a map and iterated without sorting.

Map iteration order is random, so choice and tool‑call ordering will vary between runs, which can make traces and logs flaky.

Add explicit sorts before the loops:

Proposed fix
 import (
 	"context"
 	"time"

 	"github.com/maximhq/bifrost/core/schemas"
 	"github.com/maximhq/bifrost/framework/modelcatalog"
 	"github.com/maximhq/bifrost/framework/streaming"
+	"sort"
 )

@@
 	// Build final choices from accumulated data
 	// Sort choice indices for deterministic output
 	choiceIndices := make([]int, 0, len(choiceMap))
 	for idx := range choiceMap {
 		choiceIndices = append(choiceIndices, idx)
 	}
+	sort.Ints(choiceIndices)

@@
 			if len(accum.toolCalls) > 0 {
 				// Sort tool calls by index
 				tcIndices := make([]int, 0, len(accum.toolCalls))
 				for tcIdx := range accum.toolCalls {
 					tcIndices = append(tcIndices, tcIdx)
 				}
+				sort.Ints(tcIndices)
 				toolCalls := make([]schemas.ChatAssistantMessageToolCall, 0, len(accum.toolCalls))
 				for _, tcIdx := range tcIndices {
 					toolCalls = append(toolCalls, accum.toolCalls[tcIdx])
 				}
 				msg.ChatAssistantMessage.ToolCalls = toolCalls

Also consider clarifying the function comment that this reconstruction currently only handles chat streaming responses (it bails out when lastChunk.ChatResponse == nil).

🧹 Nitpick comments (4)
transports/bifrost-http/lib/config_test.go (1)

6663-12271: LoadConfig signature updates across SQLite integration tests look correct

All updated call sites now use LoadConfig(ctx, tempDir) consistently, check err immediately, and ensure ConfigStore.Close(ctx) is invoked (either explicitly between loads or via defer on the final load). There’s no remaining dependency on the old third parameter, and the tests’ semantics around hash/merge behavior and DB state are unchanged.

If you touch this area again, you could optionally DRY up the repeated ctx := context.Background() + LoadConfig(ctx, tempDir) + Close pattern into a small helper (e.g., mustLoadConfig(t, dir) returning the config and registering t.Cleanup), but that’s purely a readability/maintenance nicety, not required for this change.

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

272-313: Defensively handle missing alloc_objects / alloc_space sample types in getTopAllocations

Right now allocObjectsIdx / allocSpaceIdx default to zero and are used unconditionally. If the parsed profile ever lacks those sample types, you’ll silently read whatever is at index 0, which makes the “top allocations” inaccurate.

Consider tracking booleans when you discover each index and returning an empty slice (or skipping aggregation) if either type is not found, instead of assuming index 0 is correct.

framework/tracing/store.go (1)

13-22: Clarify or drop unused Tracer / RequestID on DeferredSpanInfo

Within this file, only SpanID, StartTime, FirstChunkTime, AccumulatedChunks, and mu are used. If Tracer and RequestID are no longer part of the streaming/deferred‑span design, consider removing them (or explicitly marking them as reserved) to keep the struct lean and avoid confusion about lifecycles.

plugins/logging/main.go (1)

614-625: Make Cleanup idempotent to avoid double‑close panics on done

Cleanup stops the ticker and unconditionally close(p.done) then waits. If Cleanup is ever called twice (tests, repeated shutdown, or RemovePlugin + Shutdown wiring), the second call will panic on closing an already‑closed channel.

Consider guarding with a sync.Once or an atomic flag so the close + wait sequence runs only once.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5186ad3 and b12158e.

⛔ Files ignored due to path filters (3)
  • examples/plugins/hello-world/go.sum is excluded by !**/*.sum
  • plugins/governance/go.sum is excluded by !**/*.sum
  • transports/go.sum is excluded by !**/*.sum
📒 Files selected for processing (100)
  • .github/workflows/release-pipeline.yml
  • .github/workflows/scripts/push-mintlify-changelog.sh
  • .github/workflows/scripts/release-bifrost-http.sh
  • .github/workflows/scripts/release-framework.sh
  • core/bifrost.go
  • core/bifrost_test.go
  • core/changelog.md
  • core/mcp.go
  • core/providers/utils/utils.go
  • core/schemas/bifrost.go
  • core/schemas/context.go
  • core/schemas/plugin.go
  • core/schemas/trace.go
  • core/schemas/tracer.go
  • core/version
  • docs/docs.json
  • docs/plugins/getting-started.mdx
  • docs/plugins/migration-guide.mdx
  • docs/plugins/writing-plugin.mdx
  • examples/plugins/hello-world/go.mod
  • examples/plugins/hello-world/main.go
  • framework/changelog.md
  • framework/configstore/tables/mcp.go
  • framework/plugins/dynamicplugin.go
  • framework/plugins/dynamicplugin_test.go
  • framework/streaming/accumulator.go
  • framework/streaming/audio.go
  • framework/streaming/chat.go
  • framework/streaming/responses.go
  • framework/streaming/transcription.go
  • framework/streaming/types.go
  • framework/tracing/helpers.go
  • framework/tracing/llmspan.go
  • framework/tracing/propagation.go
  • framework/tracing/store.go
  • framework/tracing/tracer.go
  • framework/version
  • plugins/governance/changelog.md
  • plugins/governance/go.mod
  • plugins/governance/main.go
  • plugins/governance/version
  • plugins/jsonparser/changelog.md
  • plugins/jsonparser/main.go
  • plugins/jsonparser/version
  • plugins/logging/changelog.md
  • plugins/logging/main.go
  • plugins/logging/utils.go
  • plugins/logging/version
  • plugins/maxim/changelog.md
  • plugins/maxim/main.go
  • plugins/maxim/version
  • plugins/mocker/changelog.md
  • plugins/mocker/main.go
  • plugins/mocker/version
  • plugins/otel/changelog.md
  • plugins/otel/converter.go
  • plugins/otel/main.go
  • plugins/otel/ttlsyncmap.go
  • plugins/otel/version
  • plugins/semanticcache/changelog.md
  • plugins/semanticcache/main.go
  • plugins/semanticcache/version
  • plugins/telemetry/changelog.md
  • plugins/telemetry/main.go
  • plugins/telemetry/version
  • transports/bifrost-http/handlers/cache.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/devpprof.go
  • transports/bifrost-http/handlers/devpprof_unix.go
  • transports/bifrost-http/handlers/devpprof_windows.go
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/handlers/health.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/handlers/logging.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/handlers/mcpserver.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • transports/bifrost-http/handlers/plugins.go
  • transports/bifrost-http/handlers/providers.go
  • transports/bifrost-http/handlers/session.go
  • transports/bifrost-http/handlers/ui.go
  • transports/bifrost-http/handlers/websocket.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/lib/config_test.go
  • transports/bifrost-http/lib/middleware.go
  • transports/bifrost-http/server/server.go
  • transports/changelog.md
  • transports/go.mod
  • transports/version
  • ui/app/clientLayout.tsx
  • ui/app/workspace/logs/views/logDetailsSheet.tsx
  • ui/components/devProfiler.tsx
  • ui/components/sidebar.tsx
  • ui/components/ui/sheet.tsx
  • ui/components/ui/switch.tsx
  • ui/lib/store/apis/devApi.ts
  • ui/lib/store/apis/index.ts
💤 Files with no reviewable changes (3)
  • core/mcp.go
  • framework/configstore/tables/mcp.go
  • plugins/otel/ttlsyncmap.go
✅ Files skipped from review due to trivial changes (2)
  • plugins/mocker/version
  • plugins/governance/version
🚧 Files skipped from review as they are similar to previous changes (30)
  • core/version
  • .github/workflows/release-pipeline.yml
  • ui/components/sidebar.tsx
  • .github/workflows/scripts/release-bifrost-http.sh
  • transports/bifrost-http/handlers/plugins.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • framework/version
  • transports/bifrost-http/handlers/health.go
  • transports/bifrost-http/handlers/cache.go
  • core/schemas/context.go
  • plugins/maxim/main.go
  • plugins/governance/changelog.md
  • ui/components/devProfiler.tsx
  • transports/bifrost-http/handlers/config.go
  • .github/workflows/scripts/release-framework.sh
  • docs/docs.json
  • ui/components/ui/sheet.tsx
  • transports/go.mod
  • docs/plugins/writing-plugin.mdx
  • plugins/maxim/version
  • transports/version
  • transports/changelog.md
  • ui/lib/store/apis/devApi.ts
  • plugins/logging/utils.go
  • transports/bifrost-http/handlers/devpprof_unix.go
  • ui/components/ui/switch.tsx
  • plugins/jsonparser/version
  • .github/workflows/scripts/push-mintlify-changelog.sh
  • plugins/semanticcache/version
  • ui/lib/store/apis/index.ts
🧰 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/devpprof_windows.go
  • transports/bifrost-http/handlers/mcp.go
  • examples/plugins/hello-world/main.go
  • examples/plugins/hello-world/go.mod
  • framework/streaming/transcription.go
  • plugins/maxim/changelog.md
  • plugins/mocker/main.go
  • plugins/semanticcache/changelog.md
  • transports/bifrost-http/lib/middleware.go
  • docs/plugins/getting-started.mdx
  • transports/bifrost-http/handlers/logging.go
  • transports/bifrost-http/handlers/ui.go
  • transports/bifrost-http/handlers/providers.go
  • framework/streaming/types.go
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/handlers/inference.go
  • framework/streaming/chat.go
  • core/bifrost.go
  • plugins/telemetry/main.go
  • transports/bifrost-http/handlers/mcpserver.go
  • core/schemas/plugin.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/handlers/websocket.go
  • ui/app/workspace/logs/views/logDetailsSheet.tsx
  • framework/changelog.md
  • plugins/governance/main.go
  • plugins/semanticcache/main.go
  • core/changelog.md
  • plugins/logging/main.go
  • framework/plugins/dynamicplugin_test.go
  • plugins/otel/version
  • framework/streaming/audio.go
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/handlers/devpprof.go
  • plugins/jsonparser/changelog.md
  • plugins/logging/version
  • framework/streaming/accumulator.go
  • plugins/otel/changelog.md
  • transports/bifrost-http/handlers/session.go
  • plugins/telemetry/version
  • core/schemas/tracer.go
  • plugins/mocker/changelog.md
  • docs/plugins/migration-guide.mdx
  • core/providers/utils/utils.go
  • plugins/jsonparser/main.go
  • core/schemas/bifrost.go
  • framework/tracing/store.go
  • framework/streaming/responses.go
  • framework/plugins/dynamicplugin.go
  • transports/bifrost-http/lib/config_test.go
  • plugins/governance/go.mod
  • plugins/otel/converter.go
  • plugins/telemetry/changelog.md
  • framework/tracing/helpers.go
  • plugins/logging/changelog.md
  • ui/app/clientLayout.tsx
  • plugins/otel/main.go
  • transports/bifrost-http/integrations/router.go
  • core/schemas/trace.go
  • framework/tracing/llmspan.go
  • transports/bifrost-http/server/server.go
  • framework/tracing/tracer.go
  • transports/bifrost-http/lib/config.go
  • framework/tracing/propagation.go
  • core/bifrost_test.go
🧠 Learnings (6)
📚 Learning: 2025-12-09T17:07:42.007Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/schemas/account.go:9-18
Timestamp: 2025-12-09T17:07:42.007Z
Learning: In core/schemas/account.go, the HuggingFaceKeyConfig field within the Key struct is currently unused and reserved for future Hugging Face inference endpoint deployments. Do not flag this field as missing from OpenAPI documentation or require its presence in the API spec until the feature is actively implemented and used. When the feature is added, update the OpenAPI docs accordingly; otherwise, treat this field as non-breaking and not part of the current API surface.

Applied to files:

  • transports/bifrost-http/handlers/devpprof_windows.go
  • transports/bifrost-http/handlers/mcp.go
  • examples/plugins/hello-world/main.go
  • framework/streaming/transcription.go
  • plugins/mocker/main.go
  • transports/bifrost-http/lib/middleware.go
  • transports/bifrost-http/handlers/logging.go
  • transports/bifrost-http/handlers/ui.go
  • transports/bifrost-http/handlers/providers.go
  • framework/streaming/types.go
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/handlers/inference.go
  • framework/streaming/chat.go
  • core/bifrost.go
  • plugins/telemetry/main.go
  • transports/bifrost-http/handlers/mcpserver.go
  • core/schemas/plugin.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/handlers/websocket.go
  • plugins/governance/main.go
  • plugins/semanticcache/main.go
  • plugins/logging/main.go
  • framework/plugins/dynamicplugin_test.go
  • framework/streaming/audio.go
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/handlers/devpprof.go
  • framework/streaming/accumulator.go
  • transports/bifrost-http/handlers/session.go
  • core/schemas/tracer.go
  • core/providers/utils/utils.go
  • plugins/jsonparser/main.go
  • core/schemas/bifrost.go
  • framework/tracing/store.go
  • framework/streaming/responses.go
  • framework/plugins/dynamicplugin.go
  • transports/bifrost-http/lib/config_test.go
  • plugins/otel/converter.go
  • framework/tracing/helpers.go
  • plugins/otel/main.go
  • transports/bifrost-http/integrations/router.go
  • core/schemas/trace.go
  • framework/tracing/llmspan.go
  • transports/bifrost-http/server/server.go
  • framework/tracing/tracer.go
  • transports/bifrost-http/lib/config.go
  • framework/tracing/propagation.go
  • core/bifrost_test.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/devpprof_windows.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/lib/middleware.go
  • transports/bifrost-http/handlers/logging.go
  • transports/bifrost-http/handlers/ui.go
  • transports/bifrost-http/handlers/providers.go
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/handlers/mcpserver.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/handlers/websocket.go
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/handlers/devpprof.go
  • transports/bifrost-http/handlers/session.go
  • transports/bifrost-http/lib/config_test.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/server/server.go
  • transports/bifrost-http/lib/config.go
📚 Learning: 2025-12-24T04:36:57.698Z
Learnt from: danpiths
Repo: maximhq/bifrost PR: 1169
File: transports/bifrost-http/handlers/governance.go:1708-1878
Timestamp: 2025-12-24T04:36:57.698Z
Learning: In governance update handlers (e.g., updateModelConfig, updateProviderGovernance), design updates to support clearing individual fields by sending null/empty values (e.g., {"rate_limit": {"token_max_limit": null}} clears token_max_limit). Follow this pattern for partial updates so users can remove specific governance settings without deleting the whole entity. Ensure budget updates follow the same approach using direct field assignment. Review input validation, JSON decoding (e.g., pointers vs values in Go structs), and API documentation to reflect nullable fields and expected behavior.

Applied to files:

  • transports/bifrost-http/handlers/governance.go
📚 Learning: 2025-12-22T10:50:40.990Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1154
File: plugins/governance/store.go:1165-1186
Timestamp: 2025-12-22T10:50:40.990Z
Learning: In the Bifrost governance plugin, budgets and rate limits have 1:1 relationships with their parent entities (virtual keys, teams, customers). Do not assume sharing; ensure cascade deletion logic only deletes budgets/rate limits when there are no shared references. Enforce invariants in code and add tests to verify no cross-entity sharing and that cascade deletes only remove the specific child of the parent. If a counterexample arises, adjust data model or add guards.

Applied to files:

  • plugins/governance/main.go
📚 Learning: 2025-12-19T09:26:54.961Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/providers/utils/utils.go:1050-1051
Timestamp: 2025-12-19T09:26:54.961Z
Learning: Update streaming end-marker handling so HuggingFace is treated as a non-[DONE] provider for backends that do not emit a DONE marker (e.g., meta llama on novita). In core/providers/utils/utils.go, adjust ProviderSendsDoneMarker() (or related logic) to detect providers that may not emit DONE and avoid relying on DONE as the sole end signal. Add tests to cover both DONE-emitting and non-DONE backends, with clear documentation in code comments explaining the rationale and any fallback behavior.

Applied to files:

  • core/providers/utils/utils.go
📚 Learning: 2025-12-24T10:55:31.424Z
Learnt from: akshaydeo
Repo: maximhq/bifrost PR: 888
File: framework/tracing/propagation.go:24-36
Timestamp: 2025-12-24T10:55:31.424Z
Learning: In framework/tracing/propagation.go, ensure ExtractParentID() returns ctx.TraceID (not ctx.ParentID) because the parent request's traceparent header carries the trace ID used for continuing the trace.

Applied to files:

  • framework/tracing/propagation.go

@akshaydeo akshaydeo force-pushed the 11-19-plugins_v2_architecture branch from b12158e to 828d3d7 Compare December 26, 2025 03:38
Copy link
Copy Markdown
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

Caution

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

⚠️ Outside diff range comments (3)
plugins/governance/main.go (1)

42-49: Remove TransportInterceptor from BaseGovernancePlugin interface or implement it in GovernancePlugin.

The BaseGovernancePlugin interface declares TransportInterceptor (line 44), but GovernancePlugin implements HTTPTransportMiddleware (line 264) instead. The interface is actively used in transports/bifrost-http/server/server.go for type assertions and method calls. The TransportInterceptor method in the interface is never called and appears to be a leftover from the migration to the new v2 plugin architecture.

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

203-212: Avoid returning the shared providers map after releasing the lock (data race risk).

GovernanceInMemoryStore.GetConfiguredProviders returns s.Config.Providers directly while only holding Mu during the read of the field. Callers then iterate/use the same map after the lock is released, while other goroutines (e.g., AddProvider/UpdateProviderConfig/RemoveProvider) may mutate it under Mu. That’s a classic concurrent map read/write hazard.

Make this method return a copy of the map (or a safely immutable view) constructed under the read lock, so callers never share the live internal map.

Proposed fix: copy the providers map under the lock
 type GovernanceInMemoryStore struct {
 	Config *lib.Config
 }

 func (s *GovernanceInMemoryStore) GetConfiguredProviders() map[schemas.ModelProvider]configstore.ProviderConfig {
-	// Use read lock for thread-safe access - no need to copy on hot path
-	s.Config.Mu.RLock()
-	defer s.Config.Mu.RUnlock()
-	return s.Config.Providers
+	// Copy under read lock to avoid exposing the shared map to concurrent writers
+	s.Config.Mu.RLock()
+	defer s.Config.Mu.RUnlock()
+
+	out := make(map[schemas.ModelProvider]configstore.ProviderConfig, len(s.Config.Providers))
+	for k, v := range s.Config.Providers {
+		out[k] = v
+	}
+	return out
 }
transports/bifrost-http/lib/config.go (1)

1509-1527: Guard against nil PricingSyncInterval when initializing framework config from file.

In initFrameworkConfigFromFile, the else-branch:

} else if configData.FrameworkConfig != nil && configData.FrameworkConfig.Pricing != nil {
    pricingConfig.PricingURL = configData.FrameworkConfig.Pricing.PricingURL
    syncDuration := time.Duration(*configData.FrameworkConfig.Pricing.PricingSyncInterval) * time.Second
    pricingConfig.PricingSyncInterval = &syncDuration
}

assumes PricingSyncInterval is non-nil. If a user provides framework.pricing without pricing_sync_interval, this will panic during config load.

Add a nil (and optional >0) guard, and either fall back to defaults or leave the field nil so modelcatalog.Init can apply its defaults.

Suggested defensive change
 func initFrameworkConfigFromFile(ctx context.Context, config *Config, configData *ConfigData) {
 	pricingConfig := &modelcatalog.Config{}
@@
-	} else if configData.FrameworkConfig != nil && configData.FrameworkConfig.Pricing != nil {
-		pricingConfig.PricingURL = configData.FrameworkConfig.Pricing.PricingURL
-		syncDuration := time.Duration(*configData.FrameworkConfig.Pricing.PricingSyncInterval) * time.Second
-		pricingConfig.PricingSyncInterval = &syncDuration
+	} else if configData.FrameworkConfig != nil && configData.FrameworkConfig.Pricing != nil {
+		pricingConfig.PricingURL = configData.FrameworkConfig.Pricing.PricingURL
+		if configData.FrameworkConfig.Pricing.PricingSyncInterval != nil &&
+			*configData.FrameworkConfig.Pricing.PricingSyncInterval > 0 {
+			syncDuration := time.Duration(*configData.FrameworkConfig.Pricing.PricingSyncInterval) * time.Second
+			pricingConfig.PricingSyncInterval = &syncDuration
+		}
 	}

If you want to enforce a default when nil, you can set pricingConfig.PricingSyncInterval to bifrost.Ptr(modelcatalog.DefaultPricingSyncInterval) in the else case.

♻️ Duplicate comments (15)
ui/components/ui/sheet.tsx (1)

114-114: Make left margin conditional on button presence.

The ml-3 class is applied unconditionally, but the expand/collapse button only renders when expandable is true AND side is "right". When the button is absent, this creates unwanted left margin.

🔎 Suggested fix
-			<div className={cn("flex h-full min-w-0 flex-1 flex-row items-center ml-3", className)}>{children}</div>
+			<div className={cn("flex h-full min-w-0 flex-1 flex-row items-center", sheetContext?.expandable && sheetContext?.side === "right" ? "ml-3" : "", className)}>{children}</div>
transports/changelog.md (1)

15-15: Add language specifier to fenced code block.

The fenced code block is missing a language specifier, which prevents proper syntax highlighting.

🔎 Suggested fix
   **Migration summary:**
-  ```
+  ```go
   // v1.3.x (removed)
   TransportInterceptor(ctx *BifrostContext, url string, headers map[string]string, body map[string]any) (map[string]string, map[string]any, error)

Based on static analysis hints.

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

1428-1431: Context should be passed by value, not by pointer.

The function signature completeDeferredSpan(ctx *context.Context, ...) violates Go conventions. The context.Context type is designed to be passed by value and is safe for concurrent use. Passing it by pointer can lead to subtle bugs and is considered an anti-pattern.

🔎 Proposed fix
-func completeDeferredSpan(ctx *context.Context, result *schemas.BifrostResponse, err *schemas.BifrostError) {
-	if ctx == nil {
+func completeDeferredSpan(ctx context.Context, result *schemas.BifrostResponse, err *schemas.BifrostError) {
+	if ctx == nil {
 		return
 	}
 
-	traceID, ok := (*ctx).Value(schemas.BifrostContextKeyTraceID).(string)
+	traceID, ok := ctx.Value(schemas.BifrostContextKeyTraceID).(string)

Update all call sites accordingly:

-completeDeferredSpan(&ctx, processedResponse, processedError)
+completeDeferredSpan(ctx, processedResponse, processedError)
plugins/otel/changelog.md (1)

1-19: LGTM: Changelog comprehensively documents the breaking change.

The changelog clearly documents the migration from TransportInterceptor to HTTPTransportMiddleware with old/new API signatures and consumer guidance. The past review flagged the missing migration guide link, which should be addressed separately.

framework/changelog.md (1)

1-1: Grammar and detail issues in tracing feature entry remain unaddressed.

The changelog entry still has awkward phrasing ("for allowing plugins to enable") and lacks detail about what the tracing framework provides to plugins. The breaking change section below is much more detailed, creating an imbalance.

Consider revising to:

-- feat: adds new tracing framework for allowing plugins to enable e2e tracing
+- feat: adds tracing framework to enable end-to-end distributed tracing. Plugins can now report spans and traces for observability integration via the ObservabilityPlugin interface.

This improves grammar and briefly describes the capability without being overly verbose.

examples/plugins/hello-world/main.go (1)

19-27: Context value propagation still broken between middleware and hooks.

The HTTPTransportMiddleware sets a value on the fasthttp.RequestCtx (line 23), but PreHook (line 30) and PostHook (lines 39-40) attempt to read from *schemas.BifrostContext. These are different context types, so the value will always be nil in the hooks, breaking the example's demonstration of data flow.

As noted in the previous review, you need to either:

  1. Extract the *schemas.BifrostContext from the fasthttp context and set values on it, or
  2. Remove the context value reads from PreHook/PostHook if cross-hook data flow is not needed for this example
ui/app/workspace/logs/views/logDetailsSheet.tsx (1)

183-187: Accessibility: Icon-only dropdown trigger still missing aria-label.

The icon-only Button inside the DropdownMenuTrigger lacks an accessible name for screen reader users. This issue was flagged in a previous review and remains unaddressed.

🔎 Add aria-label for accessibility
 <DropdownMenuTrigger asChild>
-  <Button variant="ghost" size="icon">
+  <Button variant="ghost" size="icon" aria-label="Open log actions menu">
     <MoreVertical className="h-3 w-3" />
   </Button>
 </DropdownMenuTrigger>
transports/bifrost-http/integrations/router.go (1)

517-519: Non-streaming calls pass wrong context type — breaks tracing and plugin propagation.

The non-streaming request handlers call Bifrost client methods with ctx (a *fasthttp.RequestCtx) instead of the enriched *bifrostCtx. The comment on line 508-512 says "Use the cancellable context from ConvertToBifrostContext", yet all the calls below pass ctx:

listModelsResponse, bifrostErr := g.client.ListModelsRequest(ctx, ...)
textCompletionResponse, bifrostErr := g.client.TextCompletionRequest(ctx, ...)
chatResponse, bifrostErr := g.client.ChatCompletionRequest(ctx, ...)
// ... etc

This breaks tracing context propagation and plugin hooks since all BifrostContextKey* values live on bifrostCtx. The streaming path correctly uses streamCtx := *bifrostCtx.

This issue was flagged in a previous review. Please verify that these calls are updated to use *bifrostCtx instead of ctx.

plugins/governance/main.go (1)

292-297: Fix error message accuracy.

Line 294: The error message says "failed to marshal" but the operation is unmarshaling (sonic.Unmarshal).

🔎 Proposed fix
 		err = sonic.Unmarshal(ctx.Request.Body(), &payload)
 		if err != nil {
-			p.logger.Error("failed to marshal request body to check for virtual key: %v", err)
+			p.logger.Error("failed to unmarshal request body: %v", err)
 			next(ctx)
 			return
 		}
framework/tracing/store.go (1)

13-22: Trim unused fields on DeferredSpanInfo and consider cleaning deferredSpans in TTL sweep.

  • DeferredSpanInfo.Tracer and DeferredSpanInfo.RequestID (lines 17–18) are never set or read anywhere; they add complexity and memory overhead without value. Either wire them up or remove them.
  • cleanupOldTraces only evicts from traces (lines 341–355) but leaves any corresponding deferredSpans entries alive; for long‑running processes this can accumulate stale streaming state for traces that have already been TTL‑purged.

You can:

  • Drop the unused fields from DeferredSpanInfo, and
  • In cleanupOldTraces, also s.deferredSpans.Delete(trace.TraceID) when a trace is evicted.
Example cleanup tweaks
 type DeferredSpanInfo struct {
 	SpanID             string
 	StartTime          time.Time
-	Tracer             schemas.Tracer // Reference to tracer for completing the span
-	RequestID          string         // Request ID for accumulator lookup
 	FirstChunkTime     time.Time      // Timestamp of first chunk (for TTFT calculation)
 	AccumulatedChunks  []*schemas.BifrostResponse // Accumulated streaming chunks
 	mu                 sync.Mutex     // Mutex for thread-safe chunk accumulation
 }
@@
 func (s *TraceStore) cleanupOldTraces() {
 	cutoff := time.Now().Add(-s.ttl)
@@
 	s.traces.Range(func(key, value any) bool {
 		trace := value.(*schemas.Trace)
 		if trace.StartTime.Before(cutoff) {
 			if deleted, ok := s.traces.LoadAndDelete(key); ok {
+				// Best-effort cleanup of any deferred span for this trace
+				if t := deleted.(*schemas.Trace); t != nil {
+					s.deferredSpans.Delete(t.TraceID)
+				}
 				s.ReleaseTrace(deleted.(*schemas.Trace))
 				count++
 			}
 		}
 		return true
 	})

Also applies to: 119-177, 341-360

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

191-279: TracingMiddleware.StartSpan still passes *fasthttp.RequestCtx where context.Context is required.

Middleware() calls:

spanCtx, rootSpan := m.tracer.Load().StartSpan(ctx, string(ctx.RequestURI()), schemas.SpanKindHTTPRequest)

Here ctx is *fasthttp.RequestCtx, but StartSpan is defined as:

StartSpan(ctx context.Context, name string, kind schemas.SpanKind) (context.Context, schemas.SpanHandle)

*fasthttp.RequestCtx does not implement context.Context, so this will not compile. You need to construct a real context.Context (and attach the trace ID) before calling StartSpan, then propagate the returned spanCtx as needed.

Suggested fix using a Go context carrying the trace ID
 func (m *TracingMiddleware) Middleware() schemas.BifrostHTTPMiddleware {
 	return func(next fasthttp.RequestHandler) fasthttp.RequestHandler {
 		return func(ctx *fasthttp.RequestCtx) {
@@
-			// Create trace in store - only ID returned (trace data stays in store)
-			traceID := m.tracer.Load().CreateTrace(parentID)
-			// Only trace ID goes into context (lightweight, no bloat)
-			ctx.SetUserValue(schemas.BifrostContextKeyTraceID, traceID)
+			// Create trace in store - only ID returned (trace data stays in store)
+			traceID := m.tracer.Load().CreateTrace(parentID)
+			// Expose trace ID to downstream handlers via fasthttp user values
+			ctx.SetUserValue(schemas.BifrostContextKeyTraceID, traceID)
@@
-			// Create root span for the HTTP request
-			spanCtx, rootSpan := m.tracer.Load().StartSpan(ctx, string(ctx.RequestURI()), schemas.SpanKindHTTPRequest)
+			// Build a standard context for tracer usage, seeded with the trace ID
+			baseCtx := context.WithValue(context.Background(), schemas.BifrostContextKeyTraceID, traceID)
+			// Create root span for the HTTP request
+			spanCtx, rootSpan := m.tracer.Load().StartSpan(baseCtx, string(ctx.RequestURI()), schemas.SpanKindHTTPRequest)
@@
-				// Set root span ID in context for child span creation
+				// Set root span ID in fasthttp context for child span creation downstream
 				if spanID, ok := spanCtx.Value(schemas.BifrostContextKeySpanID).(string); ok {
 					ctx.SetUserValue(schemas.BifrostContextKeySpanID, spanID)
 				}

This keeps tracing on a proper context.Context while still exposing IDs via ctx.UserValue for HTTP handlers and Bifrost to consume.

core/schemas/trace.go (1)

40-49: Guard Trace.Reset, Span.End, and Span.Reset with mutexes or document exclusive-use assumptions.

Trace.Reset (lines 40–49) and Span.End / Span.Reset (lines 84–104) mutate shared state without taking their respective mutexes, while other mutators (AddSpan, SetAttribute, AddEvent) are locked. If spans or traces are read/updated concurrently (e.g., while exporters or middleware run), this can lead to data races.

Either:

  • Protect these methods with the existing mutexes, or
  • Clearly document that they may only be called once no other goroutine can access the trace/span and ensure all call sites respect that contract.
Illustrative locking changes
 func (t *Trace) Reset() {
-	t.TraceID = ""
-	t.ParentID = ""
-	t.RootSpan = nil
-	t.Spans = t.Spans[:0]
-	t.StartTime = time.Time{}
-	t.EndTime = time.Time{}
-	t.Attributes = nil
+	t.mu.Lock()
+	defer t.mu.Unlock()
+	t.TraceID = ""
+	t.ParentID = ""
+	t.RootSpan = nil
+	t.Spans = t.Spans[:0]
+	t.StartTime = time.Time{}
+	t.EndTime = time.Time{}
+	t.Attributes = nil
 }
@@
 func (s *Span) End(status SpanStatus, statusMsg string) {
-	s.EndTime = time.Now()
-	s.Status = status
-	s.StatusMsg = statusMsg
+	s.mu.Lock()
+	defer s.mu.Unlock()
+	s.EndTime = time.Now()
+	s.Status = status
+	s.StatusMsg = statusMsg
 }
@@
 func (s *Span) Reset() {
-	s.SpanID = ""
-	s.ParentID = ""
-	s.TraceID = ""
-	s.Name = ""
-	s.Kind = SpanKindUnspecified
-	s.StartTime = time.Time{}
-	s.EndTime = time.Time{}
-	s.Status = SpanStatusUnset
-	s.StatusMsg = ""
-	s.Attributes = nil
-	s.Events = s.Events[:0]
+	s.mu.Lock()
+	defer s.mu.Unlock()
+	s.SpanID = ""
+	s.ParentID = ""
+	s.TraceID = ""
+	s.Name = ""
+	s.Kind = SpanKindUnspecified
+	s.StartTime = time.Time{}
+	s.EndTime = time.Time{}
+	s.Status = SpanStatusUnset
+	s.StatusMsg = ""
+	s.Attributes = nil
+	s.Events = s.Events[:0]
 }

Also applies to: 84-104

plugins/otel/converter.go (1)

148-228: Guard against uint64int64 overflow in anyToKeyValue.

The uint64 case in anyToKeyValue casts directly to int64:

case uint64:
    return kvInt(key, int64(v))

For values > math.MaxInt64 this overflows and will produce negative numbers. That’s rare but possible for counters/IDs.

Example overflow-safe handling
 import (
 	"encoding/hex"
 	"fmt"
 	"strings"
+	"math"
@@
 	case uint64:
-		return kvInt(key, int64(v))
+		if v > math.MaxInt64 {
+			// Clamp and preserve sign; optionally log via plugin logger if available
+			return kvInt(key, math.MaxInt64)
+		}
+		return kvInt(key, int64(v))

If you’d rather not clamp silently, you could instead convert large uint64 values to strings (kvStr) to preserve the full value.

In Go, does converting a uint64 value larger than math.MaxInt64 to int64 overflow, and is clamping or string-encoding preferred when exporting such values to OpenTelemetry integer attributes?
framework/tracing/tracer.go (1)

233-455: Ensure deterministic ordering of choices and tool calls in buildCompleteResponseFromChunks.

You build choiceIndices and tcIndices from maps but never sort them, so choice order and tool-call order will vary between runs despite the “Sort … for deterministic output” comments.

Sorting these index slices before iterating will make reconstructed responses stable (useful for tests and for downstream observability tooling that expects consistent ordering).

Proposed ordering fix
@@
 	// Build final choices from accumulated data
 	// Sort choice indices for deterministic output
 	choiceIndices := make([]int, 0, len(choiceMap))
 	for idx := range choiceMap {
 		choiceIndices = append(choiceIndices, idx)
 	}
+	sort.Ints(choiceIndices)
@@
 			if len(accum.toolCalls) > 0 {
 				// Sort tool calls by index
 				tcIndices := make([]int, 0, len(accum.toolCalls))
 				for tcIdx := range accum.toolCalls {
 					tcIndices = append(tcIndices, tcIdx)
 				}
+				sort.Ints(tcIndices)
 				toolCalls := make([]schemas.ChatAssistantMessageToolCall, 0, len(accum.toolCalls))
 				for _, tcIdx := range tcIndices {
 					toolCalls = append(toolCalls, accum.toolCalls[tcIdx])
 				}

Remember to add:

import "sort"

at the top of the file.

framework/tracing/llmspan.go (1)

190-193: ExtraParams still written without namespacing (duplicate concern).

User-provided keys from ExtraParams continue to be written directly into the attributes map across all request types without a namespace prefix. This can cause collisions with standard attribute keys (e.g., if a user sets "gen_ai.request.model" in ExtraParams, it would overwrite line 22's value).

This pattern appears 14 times throughout the file in all request attribute population functions.

🔎 Suggested approach: namespace ExtraParams consistently

Apply a prefix to all ExtraParams keys before insertion. Example for one location:

 	// ExtraParams
 	for k, v := range req.Params.ExtraParams {
-		attrs[k] = fmt.Sprintf("%v", v)
+		attrs["gen_ai.extra."+k] = fmt.Sprintf("%v", v)
 	}

Repeat this pattern at all 14 occurrences to prevent attribute key collisions.

Also applies to: 298-300, 365-367, 575-577, 708-710, 738-740, 753-755, 768-770, 783-785, 958-960, 982-984, 997-999, 1012-1014, 1027-1029

🧹 Nitpick comments (3)
ui/lib/store/apis/devApi.ts (1)

59-59: Misleading comment about polling.

The comment "polls every 10 seconds" suggests automatic polling, but RTK Query's builder.query doesn't configure polling by default. Polling is typically configured at the hook usage site via the pollingInterval option. Consider updating the comment to reflect this, or remove it to avoid confusion.

🔎 Proposed fix
   endpoints: (builder) => ({
-    // Get dev pprof data - polls every 10 seconds
+    // Get dev pprof data
     getDevPprof: builder.query<PprofData, void>({
transports/bifrost-http/handlers/devpprof.go (1)

104-122: Global collector singleton is safe but consider handler lifecycle.

The sync.Once pattern ensures thread-safe singleton initialization. However, since globalCollector is package-level, multiple DevPprofHandler instances will share the same collector. This is likely intentional but worth noting.

Consider documenting this behavior or ensuring Cleanup() is only called once during server shutdown.

framework/tracing/llmspan.go (1)

1279-1293: Consider strings.Builder for string concatenation in loops (optional optimization).

Lines 1279-1283 and 1289-1293 use += for string concatenation in loops, which can be less efficient for multiple blocks. Using strings.Builder (as done in extractMessageContent at lines 1315-1321) would be more efficient.

However, given typical message sizes in practice, the current approach is acceptable.

🔎 Optional refactor using strings.Builder
 		content := ""
+		var contentBuilder strings.Builder
 		if msg.Content != nil {
 			if msg.Content.ContentStr != nil && *msg.Content.ContentStr != "" {
 				content = *msg.Content.ContentStr
 			} else if msg.Content.ContentBlocks != nil {
 				for _, block := range msg.Content.ContentBlocks {
 					if block.Text != nil {
-						content += *block.Text
+						contentBuilder.WriteString(*block.Text)
 					}
 				}
+				content = contentBuilder.String()
 			}
 		}
 		// Extract reasoning text
 		reasoning := ""
+		var reasoningBuilder strings.Builder
 		if msg.ResponsesReasoning != nil && msg.ResponsesReasoning.Summary != nil {
 			for _, block := range msg.ResponsesReasoning.Summary {
 				if block.Text != "" {
-					reasoning += block.Text
+					reasoningBuilder.WriteString(block.Text)
 				}
 			}
+			reasoning = reasoningBuilder.String()
 		}
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b12158e and 828d3d7.

⛔ Files ignored due to path filters (2)
  • examples/plugins/hello-world/go.sum is excluded by !**/*.sum
  • transports/go.sum is excluded by !**/*.sum
📒 Files selected for processing (92)
  • .github/workflows/release-pipeline.yml
  • .github/workflows/scripts/push-mintlify-changelog.sh
  • .github/workflows/scripts/release-bifrost-http.sh
  • .github/workflows/scripts/release-framework.sh
  • core/bifrost.go
  • core/bifrost_test.go
  • core/changelog.md
  • core/mcp.go
  • core/providers/utils/utils.go
  • core/schemas/bifrost.go
  • core/schemas/context.go
  • core/schemas/plugin.go
  • core/schemas/trace.go
  • core/schemas/tracer.go
  • core/version
  • docs/docs.json
  • docs/plugins/getting-started.mdx
  • docs/plugins/migration-guide.mdx
  • docs/plugins/writing-plugin.mdx
  • examples/plugins/hello-world/go.mod
  • examples/plugins/hello-world/main.go
  • framework/changelog.md
  • framework/configstore/tables/mcp.go
  • framework/plugins/dynamicplugin.go
  • framework/plugins/dynamicplugin_test.go
  • framework/streaming/accumulator.go
  • framework/streaming/audio.go
  • framework/streaming/chat.go
  • framework/streaming/responses.go
  • framework/streaming/transcription.go
  • framework/streaming/types.go
  • framework/tracing/helpers.go
  • framework/tracing/llmspan.go
  • framework/tracing/propagation.go
  • framework/tracing/store.go
  • framework/tracing/tracer.go
  • framework/version
  • plugins/governance/changelog.md
  • plugins/governance/go.mod
  • plugins/governance/main.go
  • plugins/jsonparser/changelog.md
  • plugins/jsonparser/main.go
  • plugins/logging/changelog.md
  • plugins/logging/main.go
  • plugins/logging/utils.go
  • plugins/maxim/changelog.md
  • plugins/maxim/main.go
  • plugins/mocker/changelog.md
  • plugins/mocker/main.go
  • plugins/otel/changelog.md
  • plugins/otel/converter.go
  • plugins/otel/main.go
  • plugins/otel/ttlsyncmap.go
  • plugins/semanticcache/changelog.md
  • plugins/semanticcache/main.go
  • plugins/telemetry/changelog.md
  • plugins/telemetry/main.go
  • transports/bifrost-http/handlers/cache.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/devpprof.go
  • transports/bifrost-http/handlers/devpprof_unix.go
  • transports/bifrost-http/handlers/devpprof_windows.go
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/handlers/health.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/handlers/logging.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/handlers/mcpserver.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • transports/bifrost-http/handlers/plugins.go
  • transports/bifrost-http/handlers/providers.go
  • transports/bifrost-http/handlers/session.go
  • transports/bifrost-http/handlers/ui.go
  • transports/bifrost-http/handlers/websocket.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/lib/config_test.go
  • transports/bifrost-http/lib/middleware.go
  • transports/bifrost-http/server/server.go
  • transports/changelog.md
  • transports/go.mod
  • transports/version
  • ui/app/clientLayout.tsx
  • ui/app/workspace/logs/views/logDetailsSheet.tsx
  • ui/components/devProfiler.tsx
  • ui/components/sidebar.tsx
  • ui/components/ui/sheet.tsx
  • ui/components/ui/switch.tsx
  • ui/lib/store/apis/devApi.ts
  • ui/lib/store/apis/index.ts
💤 Files with no reviewable changes (3)
  • core/mcp.go
  • framework/configstore/tables/mcp.go
  • plugins/otel/ttlsyncmap.go
✅ Files skipped from review due to trivial changes (1)
  • docs/plugins/migration-guide.mdx
🚧 Files skipped from review as they are similar to previous changes (26)
  • core/version
  • ui/components/sidebar.tsx
  • .github/workflows/scripts/release-framework.sh
  • framework/streaming/responses.go
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/handlers/logging.go
  • transports/version
  • transports/bifrost-http/handlers/providers.go
  • transports/go.mod
  • transports/bifrost-http/handlers/devpprof_windows.go
  • core/bifrost_test.go
  • docs/plugins/writing-plugin.mdx
  • core/schemas/bifrost.go
  • .github/workflows/scripts/release-bifrost-http.sh
  • framework/streaming/chat.go
  • plugins/maxim/main.go
  • docs/docs.json
  • plugins/telemetry/main.go
  • framework/version
  • transports/bifrost-http/handlers/plugins.go
  • framework/plugins/dynamicplugin.go
  • framework/tracing/helpers.go
  • transports/bifrost-http/handlers/websocket.go
  • transports/bifrost-http/handlers/health.go
  • transports/bifrost-http/handlers/mcpserver.go
  • transports/bifrost-http/handlers/devpprof_unix.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:

  • plugins/mocker/changelog.md
  • core/changelog.md
  • ui/components/devProfiler.tsx
  • transports/bifrost-http/handlers/config.go
  • framework/streaming/transcription.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • plugins/logging/utils.go
  • ui/app/workspace/logs/views/logDetailsSheet.tsx
  • framework/tracing/propagation.go
  • plugins/logging/changelog.md
  • plugins/governance/main.go
  • core/providers/utils/utils.go
  • transports/changelog.md
  • transports/bifrost-http/handlers/devpprof.go
  • transports/bifrost-http/handlers/ui.go
  • plugins/otel/changelog.md
  • transports/bifrost-http/handlers/cache.go
  • plugins/logging/main.go
  • docs/plugins/getting-started.mdx
  • transports/bifrost-http/handlers/session.go
  • framework/streaming/audio.go
  • framework/changelog.md
  • ui/components/ui/sheet.tsx
  • transports/bifrost-http/integrations/router.go
  • plugins/mocker/main.go
  • transports/bifrost-http/handlers/inference.go
  • plugins/semanticcache/changelog.md
  • transports/bifrost-http/lib/middleware.go
  • examples/plugins/hello-world/go.mod
  • framework/plugins/dynamicplugin_test.go
  • core/schemas/tracer.go
  • transports/bifrost-http/server/server.go
  • plugins/maxim/changelog.md
  • ui/components/ui/switch.tsx
  • plugins/jsonparser/main.go
  • plugins/jsonparser/changelog.md
  • plugins/otel/main.go
  • examples/plugins/hello-world/main.go
  • core/bifrost.go
  • plugins/governance/go.mod
  • transports/bifrost-http/handlers/mcp.go
  • ui/app/clientLayout.tsx
  • framework/tracing/store.go
  • ui/lib/store/apis/devApi.ts
  • core/schemas/trace.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/handlers/middlewares.go
  • framework/streaming/types.go
  • plugins/telemetry/changelog.md
  • transports/bifrost-http/handlers/integrations.go
  • framework/tracing/tracer.go
  • core/schemas/plugin.go
  • plugins/governance/changelog.md
  • plugins/otel/converter.go
  • transports/bifrost-http/lib/config_test.go
  • framework/tracing/llmspan.go
  • framework/streaming/accumulator.go
  • core/schemas/context.go
  • plugins/semanticcache/main.go
  • ui/lib/store/apis/index.ts
🧠 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/handlers/config.go
  • framework/streaming/transcription.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • plugins/logging/utils.go
  • framework/tracing/propagation.go
  • plugins/governance/main.go
  • core/providers/utils/utils.go
  • transports/bifrost-http/handlers/devpprof.go
  • transports/bifrost-http/handlers/ui.go
  • transports/bifrost-http/handlers/cache.go
  • plugins/logging/main.go
  • transports/bifrost-http/handlers/session.go
  • framework/streaming/audio.go
  • transports/bifrost-http/integrations/router.go
  • plugins/mocker/main.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/lib/middleware.go
  • framework/plugins/dynamicplugin_test.go
  • core/schemas/tracer.go
  • transports/bifrost-http/server/server.go
  • plugins/jsonparser/main.go
  • plugins/otel/main.go
  • examples/plugins/hello-world/main.go
  • core/bifrost.go
  • transports/bifrost-http/handlers/mcp.go
  • framework/tracing/store.go
  • core/schemas/trace.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/handlers/middlewares.go
  • framework/streaming/types.go
  • transports/bifrost-http/handlers/integrations.go
  • framework/tracing/tracer.go
  • core/schemas/plugin.go
  • plugins/otel/converter.go
  • transports/bifrost-http/lib/config_test.go
  • framework/tracing/llmspan.go
  • framework/streaming/accumulator.go
  • core/schemas/context.go
  • plugins/semanticcache/main.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/config.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • transports/bifrost-http/handlers/devpprof.go
  • transports/bifrost-http/handlers/ui.go
  • transports/bifrost-http/handlers/cache.go
  • transports/bifrost-http/handlers/session.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/lib/middleware.go
  • transports/bifrost-http/server/server.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/lib/config_test.go
📚 Learning: 2025-12-24T10:55:31.424Z
Learnt from: akshaydeo
Repo: maximhq/bifrost PR: 888
File: framework/tracing/propagation.go:24-36
Timestamp: 2025-12-24T10:55:31.424Z
Learning: In framework/tracing/propagation.go, ensure ExtractParentID() returns ctx.TraceID (not ctx.ParentID) because the parent request's traceparent header carries the trace ID used for continuing the trace.

Applied to files:

  • framework/tracing/propagation.go
📚 Learning: 2025-12-22T10:50:40.990Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1154
File: plugins/governance/store.go:1165-1186
Timestamp: 2025-12-22T10:50:40.990Z
Learning: In the Bifrost governance plugin, budgets and rate limits have 1:1 relationships with their parent entities (virtual keys, teams, customers). Do not assume sharing; ensure cascade deletion logic only deletes budgets/rate limits when there are no shared references. Enforce invariants in code and add tests to verify no cross-entity sharing and that cascade deletes only remove the specific child of the parent. If a counterexample arises, adjust data model or add guards.

Applied to files:

  • plugins/governance/main.go
📚 Learning: 2025-12-19T09:26:54.961Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/providers/utils/utils.go:1050-1051
Timestamp: 2025-12-19T09:26:54.961Z
Learning: Update streaming end-marker handling so HuggingFace is treated as a non-[DONE] provider for backends that do not emit a DONE marker (e.g., meta llama on novita). In core/providers/utils/utils.go, adjust ProviderSendsDoneMarker() (or related logic) to detect providers that may not emit DONE and avoid relying on DONE as the sole end signal. Add tests to cover both DONE-emitting and non-DONE backends, with clear documentation in code comments explaining the rationale and any fallback behavior.

Applied to files:

  • core/providers/utils/utils.go
🧬 Code graph analysis (33)
ui/components/devProfiler.tsx (3)
npx/bin.js (3)
  • k (178-178)
  • sizes (179-179)
  • data (172-172)
ui/lib/utils/port.ts (1)
  • isDevelopmentMode (110-112)
ui/components/ui/tooltip.tsx (1)
  • Tooltip (43-43)
transports/bifrost-http/handlers/config.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
framework/streaming/transcription.go (1)
framework/streaming/types.go (1)
  • AccumulatedData (28-48)
transports/bifrost-http/handlers/middlewares_test.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
ui/app/workspace/logs/views/logDetailsSheet.tsx (4)
ui/components/ui/sheet.tsx (1)
  • SheetContent (137-137)
ui/components/ui/alertDialog.tsx (9)
  • AlertDialog (83-83)
  • AlertDialogTrigger (93-93)
  • AlertDialogContent (86-86)
  • AlertDialogHeader (89-89)
  • AlertDialogTitle (92-92)
  • AlertDialogDescription (87-87)
  • AlertDialogFooter (88-88)
  • AlertDialogCancel (85-85)
  • AlertDialogAction (84-84)
ui/components/ui/dropdownMenu.tsx (5)
  • DropdownMenu (193-193)
  • DropdownMenuTrigger (207-207)
  • DropdownMenuContent (195-195)
  • DropdownMenuItem (197-197)
  • DropdownMenuSeparator (202-202)
ui/components/ui/button.tsx (1)
  • Button (70-70)
plugins/governance/main.go (3)
core/utils.go (1)
  • Ptr (56-58)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-27)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/devpprof.go (4)
ui/lib/types/logs.ts (1)
  • Function (152-157)
ui/lib/store/apis/devApi.ts (6)
  • MemoryStats (4-10)
  • CPUStats (13-17)
  • RuntimeStats (20-26)
  • AllocationInfo (29-35)
  • HistoryPoint (38-45)
  • PprofData (48-55)
transports/bifrost-http/lib/middleware.go (1)
  • ChainMiddlewares (11-23)
transports/bifrost-http/handlers/utils.go (1)
  • SendJSON (16-22)
transports/bifrost-http/handlers/ui.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/cache.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/session.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
framework/streaming/audio.go (1)
framework/streaming/types.go (1)
  • AccumulatedData (28-48)
transports/bifrost-http/integrations/router.go (2)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
core/schemas/bifrost.go (2)
  • BifrostContextKeyDeferTraceCompletion (148-148)
  • BifrostContextKeyTraceCompleter (149-149)
plugins/mocker/main.go (2)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-27)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/inference.go (2)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
core/schemas/bifrost.go (1)
  • BifrostContextKeyDeferTraceCompletion (148-148)
transports/bifrost-http/lib/middleware.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
framework/plugins/dynamicplugin_test.go (1)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-27)
core/schemas/tracer.go (7)
core/schemas/bifrost.go (2)
  • ModelProvider (33-33)
  • BifrostError (490-499)
core/schemas/chatcompletions.go (1)
  • BifrostLLMUsage (854-861)
core/schemas/speech.go (1)
  • BifrostSpeechResponse (22-29)
core/schemas/transcriptions.go (1)
  • BifrostTranscriptionResponse (16-26)
core/schemas/trace.go (3)
  • Trace (10-19)
  • SpanKind (115-115)
  • SpanStatus (143-143)
core/schemas/context.go (1)
  • BifrostContext (32-42)
framework/tracing/tracer.go (1)
  • Tracer (17-20)
transports/bifrost-http/server/server.go (8)
transports/bifrost-http/handlers/devpprof.go (3)
  • DevPprofHandler (100-102)
  • IsDevMode (109-111)
  • NewDevPprofHandler (125-129)
core/schemas/plugin.go (3)
  • Plugin (71-97)
  • BifrostHTTPMiddleware (38-38)
  • ObservabilityPlugin (123-137)
plugins/governance/main.go (2)
  • Config (34-36)
  • PluginName (23-23)
plugins/telemetry/main.go (2)
  • Config (64-66)
  • PluginName (21-21)
transports/bifrost-http/lib/config.go (2)
  • Config (189-220)
  • LoadConfig (276-356)
framework/configstore/clientconfig.go (1)
  • ClientConfig (37-54)
framework/tracing/store.go (1)
  • NewTraceStore (40-67)
framework/tracing/tracer.go (1)
  • NewTracer (24-29)
plugins/jsonparser/main.go (2)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-27)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
plugins/otel/main.go (2)
core/schemas/plugin.go (1)
  • ObservabilityPlugin (123-137)
core/schemas/trace.go (1)
  • Trace (10-19)
examples/plugins/hello-world/main.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/mcp.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
ui/app/clientLayout.tsx (1)
ui/components/devProfiler.tsx (1)
  • DevProfiler (55-422)
framework/tracing/store.go (4)
core/schemas/tracer.go (1)
  • Tracer (38-116)
core/schemas/bifrost.go (1)
  • BifrostResponse (348-369)
core/schemas/trace.go (6)
  • Trace (10-19)
  • Span (52-65)
  • SpanEvent (107-111)
  • SpanKind (115-115)
  • SpanStatusUnset (147-147)
  • SpanStatus (143-143)
framework/tracing/helpers.go (3)
  • GetTrace (23-29)
  • AddSpan (32-38)
  • EndSpan (50-56)
core/schemas/trace.go (2)
framework/tracing/helpers.go (1)
  • AddSpan (32-38)
ui/lib/constants/logs.ts (1)
  • Status (164-164)
transports/bifrost-http/handlers/middlewares.go (5)
core/schemas/plugin.go (2)
  • BifrostHTTPMiddleware (38-38)
  • Plugin (71-97)
transports/bifrost-http/lib/middleware.go (1)
  • ChainMiddlewares (11-23)
core/schemas/tracer.go (1)
  • Tracer (38-116)
framework/tracing/propagation.go (1)
  • ExtractParentID (74-84)
core/schemas/trace.go (3)
  • SpanKindHTTPRequest (131-131)
  • SpanStatusError (151-151)
  • SpanStatusOk (149-149)
transports/bifrost-http/handlers/integrations.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
framework/tracing/tracer.go (7)
framework/tracing/store.go (1)
  • TraceStore (25-37)
framework/streaming/accumulator.go (2)
  • Accumulator (14-30)
  • NewAccumulator (440-480)
core/schemas/tracer.go (3)
  • Tracer (38-116)
  • SpanHandle (11-11)
  • StreamAccumulatorResult (15-33)
framework/tracing/helpers.go (3)
  • GetTraceID (11-20)
  • EndSpan (50-56)
  • GetTrace (23-29)
core/schemas/bifrost.go (5)
  • BifrostContextKeySpanID (145-145)
  • BifrostRequest (175-196)
  • BifrostResponse (348-369)
  • BifrostError (490-499)
  • BifrostContextKeyRequestID (125-125)
framework/tracing/llmspan.go (1)
  • PopulateRequestAttributes (14-60)
framework/streaming/types.go (1)
  • StreamResponseTypeFinal (24-24)
core/schemas/plugin.go (2)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-27)
core/schemas/trace.go (1)
  • Trace (10-19)
transports/bifrost-http/lib/config_test.go (1)
transports/bifrost-http/lib/config.go (1)
  • LoadConfig (276-356)
framework/streaming/accumulator.go (1)
framework/streaming/types.go (3)
  • StreamAccumulator (103-115)
  • ChatStreamChunk (77-87)
  • ResponsesStreamChunk (90-100)
core/schemas/context.go (1)
core/schemas/bifrost.go (1)
  • BifrostContextKeyDeferTraceCompletion (148-148)
plugins/semanticcache/main.go (2)
core/schemas/plugin.go (2)
  • Plugin (71-97)
  • BifrostHTTPMiddleware (38-38)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-27)
🪛 markdownlint-cli2 (0.18.1)
transports/changelog.md

15-15: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

Comment thread examples/plugins/hello-world/go.mod Outdated
Comment thread framework/tracing/tracer.go Outdated
@akshaydeo akshaydeo force-pushed the 11-19-plugins_v2_architecture branch from ee5875b to 74c473c Compare December 29, 2025 07:56
Copy link
Copy Markdown
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

♻️ Duplicate comments (23)
ui/components/ui/switch.tsx (2)

13-13: UI breaking change unrelated to PR objective—consider moving to separate PR.

The previous reviewer correctly identified this as a breaking change: switching the default size from "default" to "md" reduces switch dimensions from h-6 w-11 to h-5 w-9 for all existing instances without explicit size props.

Additionally, this visual change appears unrelated to the PR's stated objective (plugins v2 architecture migration). UI component styling updates should typically be in dedicated PRs with proper visual regression testing and design approval.

Recommendation: Either revert this change and keep it out of scope, or move all Switch styling updates to a separate PR that includes:

  • Visual regression tests
  • Design system documentation updates
  • Migration guide for affected components
  • Stack context explaining why UI changes accompany the plugin architecture work

16-16: Critical design change unrelated to PR scope—requires design approval and separate PR.

The previous reviewer correctly flagged this: changing from rounded-full to rounded-sm fundamentally alters the Switch component from a traditional pill-shaped toggle to a rectangular design with slight rounding. This breaks established UI patterns where toggle switches are expected to be fully rounded.

This change is doubly problematic:

  1. Breaking design language: Affects all 20+ Switch usages across the codebase with a non-standard appearance
  2. Out of scope: A plugins v2 architecture PR should not contain UI component design changes

Without understanding the full stack context (PRs #1170, #1169, #1121, #1120, #1119), this appears to be an accidental inclusion or requires explicit justification.

Action required: Revert to rounded-full or move to a separate UI-focused PR with:

  • Design system approval
  • Visual snapshots/regression tests
  • Clear documentation of the new design direction
  • Explanation of why it's bundled with backend plugin architecture changes

If this is intentional across the stack, please clarify the design rationale:

Are there design system changes in Bifrost v1.4.0 that standardize switch components to use rounded-sm instead of rounded-full?

Also applies to: 26-26

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

1428-1431: Critical: Context must be passed by value, not by pointer.

Despite the past review comment marked as "✅ Addressed in commit 330f7f3", this function still violates Go conventions by accepting ctx *context.Context. The context.Context type is designed to be passed by value and is safe for concurrent use. Passing it by pointer is incorrect and can lead to subtle bugs.

🔎 Required fix
-func completeDeferredSpan(ctx *context.Context, result *schemas.BifrostResponse, err *schemas.BifrostError) {
-	if ctx == nil {
+func completeDeferredSpan(ctx context.Context, result *schemas.BifrostResponse, err *schemas.BifrostError) {
+	if ctx == context.Background() || ctx == context.TODO() {
 		return
 	}
 
-	traceID, ok := (*ctx).Value(schemas.BifrostContextKeyTraceID).(string)
+	traceID, ok := ctx.Value(schemas.BifrostContextKeyTraceID).(string)

And update all call sites on lines 901, 928, 952, 978:

-			completeDeferredSpan(&ctx, processedResponse, processedError)
+			completeDeferredSpan(ctx, processedResponse, processedError)
plugins/otel/changelog.md (1)

19-19: Verify migration guide exists before merge.

The changelog references /docs/plugins/migration-guide which was previously flagged as not existing in the repository. Ensure the migration guide is created or update the link to point to the correct documentation location before merging.

plugins/governance/changelog.md (1)

21-21: Verify migration guide exists before merge.

The changelog references /docs/plugins/migration-guide. As flagged in the OTEL changelog review, ensure this documentation file exists or update the link to the correct location.

framework/changelog.md (2)

25-25: Verify migration guide exists before merge.

The changelog references /docs/plugins/migration-guide. As flagged in other plugin changelogs, ensure this documentation file exists or update the link to the correct location.


1-1: Fix grammar error and expand tracing feature description.

Line 1 contains a grammar error: "to enabling e2e tracing" should be "to enable e2e tracing".

Additionally, the tracing framework entry is minimal compared to the detailed HTTPTransportMiddleware breaking change below. Consider expanding this entry to briefly describe what the framework provides (e.g., "Introduces a tracing framework that enables plugins to report spans and traces for distributed tracing integration with observability backends").

🔎 Suggested fix
-- feat: adds new tracing framework for allowing plugins to enable e2e tracing
+- feat: adds new tracing framework to enable e2e tracing, allowing plugins to report spans and traces for distributed tracing integration with observability backends
examples/plugins/hello-world/main.go (1)

19-27: Context value propagation issue previously identified.

The past review correctly identified that HTTPTransportMiddleware stores values in fasthttp.RequestCtx (line 23) but PreHook (line 30) and PostHook (lines 39-40) attempt to read from schemas.BifrostContext, breaking the data flow demonstration. The suggested fixes remain valid: either bridge the contexts or remove the reads.

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

519-688: Verify resolution of context type mismatch flagged in past review.

The past review identified that non-streaming handlers pass ctx (*fasthttp.RequestCtx) instead of *bifrostCtx (context.Context) to Bifrost client methods (lines 519, 539, 562, 585, 608, 631, 665, 688). This breaks tracing and plugin context propagation. While the past review suggested the fix was "Addressed in commit 5186ad3", the current diff still shows ctx being passed. Verify that the correct context is being used.

Run the following verification to confirm the context type being passed:

#!/bin/bash
# Check what context variable is passed to Bifrost client methods in handleNonStreamingRequest
rg -n "ListModelsRequest|TextCompletionRequest|ChatCompletionRequest|ResponsesRequest|EmbeddingRequest|SpeechRequest|TranscriptionRequest|CountTokensRequest" transports/bifrost-http/integrations/router.go -B2 -A1 | grep -A1 "func (g \*GenericRouter) handleNonStreamingRequest"
plugins/governance/main.go (2)

292-297: Fix error message accuracy.

Line 294 says "failed to marshal request body" but the operation at line 292 is sonic.Unmarshal. The error message should reflect the actual operation performed.

🔎 Proposed fix
 	err = sonic.Unmarshal(ctx.Request.Body(), &payload)
 	if err != nil {
-		p.logger.Error("failed to marshal request body to check for virtual key: %v", err)
+		p.logger.Error("failed to unmarshal request body: %v", err)
 		next(ctx)
 		return
 	}

233-261: Inconsistent prefix validation for x-bf-vk header.

While Authorization, x-api-key, and x-goog-api-key headers (lines 241-243, 252-253, 257-258) validate that values start with VirtualKeyPrefix, the x-bf-vk header (lines 236-238) returns the value directly without prefix validation. This inconsistency could allow invalid virtual keys when provided via x-bf-vk.

🔎 Proposed fix
 func parseVirtualKey(ctx *fasthttp.RequestCtx) *string {
 	var virtualKeyValue string
 	vkHeader := ctx.Request.Header.Peek("x-bf-vk")
-	if string(vkHeader) != "" {
-		return bifrost.Ptr(string(vkHeader))
+	if string(vkHeader) != "" {
+		vkValue := string(vkHeader)
+		if strings.HasPrefix(strings.ToLower(vkValue), VirtualKeyPrefix) {
+			return bifrost.Ptr(vkValue)
+		}
 	}
 	authHeader := string(ctx.Request.Header.Peek("Authorization"))
framework/tracing/store.go (1)

13-22: Consider removing unused Tracer and RequestID fields from DeferredSpanInfo.

Past review flagged that Tracer (line 17) and RequestID (line 18) are never assigned or used. StoreDeferredSpan() only initializes SpanID and StartTime. If these fields are intended for future use, consider adding a comment; otherwise, remove them to reduce memory overhead and improve clarity.

🔎 Proposed fix
 // DeferredSpanInfo stores information about a deferred span for streaming requests
 type DeferredSpanInfo struct {
 	SpanID             string
 	StartTime          time.Time
-	Tracer             schemas.Tracer // Reference to tracer for completing the span
-	RequestID          string         // Request ID for accumulator lookup
 	FirstChunkTime     time.Time      // Timestamp of first chunk (for TTFT calculation)
 	AccumulatedChunks  []*schemas.BifrostResponse // Accumulated streaming chunks
 	mu                 sync.Mutex     // Mutex for thread-safe chunk accumulation
 }
plugins/otel/converter.go (1)

165-170: Potential data loss when converting large uint64 to int64.

Lines 169-170 cast uint64 to int64, which can cause overflow for values greater than math.MaxInt64. While uncommon in practice, this could affect large counter metrics or timestamps.

🔎 Proposed fix - clamp value to prevent silent overflow
+import "math"
+
 case uint64:
+	if v > math.MaxInt64 {
+		logger.Warn("uint64 value %d exceeds int64 max, clamping", v)
+		return kvInt(key, math.MaxInt64)
+	}
 	return kvInt(key, int64(v))
core/schemas/trace.go (3)

40-49: Trace.Reset() lacks mutex protection - potential data race.

This method modifies all fields including Spans without holding the mutex. If Reset is called while another goroutine accesses the trace (e.g., via AddSpan or GetSpan), a data race could occur.

Either add mutex protection or document that Reset must only be called when exclusive access is guaranteed (i.e., after the trace is removed from the store).

🔎 Proposed fix
 // Reset clears the trace for reuse from pool
 func (t *Trace) Reset() {
+	t.mu.Lock()
+	defer t.mu.Unlock()
 	t.TraceID = ""
 	t.ParentID = ""
 	t.RootSpan = nil
 	t.Spans = t.Spans[:0]
 	t.StartTime = time.Time{}
 	t.EndTime = time.Time{}
 	t.Attributes = nil
 }

84-89: Span.End() lacks mutex protection - potential data race.

Unlike SetAttribute and AddEvent, the End method modifies EndTime, Status, and StatusMsg without holding the mutex. If End is called concurrently with reads or writes to these fields, a data race may occur.

🔎 Proposed fix
 // End marks the span as complete with the given status
 func (s *Span) End(status SpanStatus, statusMsg string) {
+	s.mu.Lock()
+	defer s.mu.Unlock()
 	s.EndTime = time.Now()
 	s.Status = status
 	s.StatusMsg = statusMsg
 }

91-104: Span.Reset() lacks mutex protection - potential data race.

Similar to Trace.Reset(), this method modifies fields without holding the mutex. If a span is reused from a pool while another goroutine still holds a reference, a data race could occur.

🔎 Proposed fix
 // Reset clears the span for reuse from pool
 func (s *Span) Reset() {
+	s.mu.Lock()
+	defer s.mu.Unlock()
 	s.SpanID = ""
 	s.ParentID = ""
 	s.TraceID = ""
 	s.Name = ""
 	s.Kind = SpanKindUnspecified
 	s.StartTime = time.Time{}
 	s.EndTime = time.Time{}
 	s.Status = SpanStatusUnset
 	s.StatusMsg = ""
 	s.Attributes = nil
 	s.Events = s.Events[:0]
 }
transports/bifrost-http/server/server.go (2)

113-114: Dev pprof handler lifecycle is now correctly scoped and cleaned up

Storing devPprofHandler on BifrostHTTPServer, registering its routes only when IsDevMode() is true, and invoking Cleanup() during shutdown ensures the profiling collector goroutine is not leaked.

Also applies to: 1040-1045, 1349-1352


1290-1295: Plugin status logging now respects mutex discipline

Wrapping the startup logging loop with pluginStatusMutex.RLock()/RUnlock() aligns with how updates are synchronized elsewhere and removes the previous data race risk when plugin status changes during startup logs.

core/bifrost.go (1)

3917-3950: Tracer shutdown is now wired into Bifrost lifecycle

Invoking tracerWrapper.tracer.Stop() from Bifrost.Shutdown() ensures TraceStore cleanup and the embedded streaming accumulator shutdown happen together with core shutdown, so background goroutines and tickers from tracing don’t outlive the Bifrost instance.

framework/tracing/tracer.go (3)

460-477: Stopping both TraceStore and streaming accumulator completes the tracing lifecycle

Tracer.Stop() now:

  • Calls t.store.Stop() to stop the cleanup ticker/goroutine.
  • Calls t.accumulator.Cleanup() to shut down the embedded streaming accumulator.

This addresses the earlier goroutine-leak concern and keeps tracing’s lifetime aligned with Bifrost’s shutdown.

Also applies to: 482-531, 533-537, 539-548


54-59: Fix stale “StoreTracer” wording in spanHandle comment

The type comment still refers to “StoreTracer”, but the concrete type is Tracer. Updating the comment avoids confusion when skimming the tracing code.

Proposed doc tweak
-// spanHandle is the concrete implementation of schemas.SpanHandle for StoreTracer.
+// spanHandle is the concrete implementation of schemas.SpanHandle for Tracer.
 // It contains the trace and span IDs needed to reference the span in the store.
 type spanHandle struct {

233-258: Make buildCompleteResponseFromChunks ordering deterministic and document chat-only behavior

Two minor issues remain in buildCompleteResponseFromChunks:

  1. Choice and tool‑call ordering is built from maps without sorting, so the final Choices slice and tool‑call order can vary between runs.
  2. The function implicitly assumes chat-stream chunks (lastChunk.ChatResponse != nil) but the comment doesn’t state that it’s chat-only, which can mislead future callers.
Suggested fix for deterministic ordering and docs
@@
-import (
-	"context"
-	"time"
-
-	"github.com/maximhq/bifrost/core/schemas"
-	"github.com/maximhq/bifrost/framework/modelcatalog"
-	"github.com/maximhq/bifrost/framework/streaming"
-)
+import (
+	"context"
+	"sort"
+	"time"
+
+	"github.com/maximhq/bifrost/core/schemas"
+	"github.com/maximhq/bifrost/framework/modelcatalog"
+	"github.com/maximhq/bifrost/framework/streaming"
+)
@@
-// buildCompleteResponseFromChunks reconstructs a complete BifrostResponse from streaming chunks.
-// This accumulates content, tool calls, reasoning, audio, and other fields.
+// buildCompleteResponseFromChunks reconstructs a complete chat BifrostResponse from
+// streaming chat chunks. It accumulates content, tool calls, reasoning, audio,
+// and other fields. Non-chat streaming types are currently ignored.
 func buildCompleteResponseFromChunks(chunks []*schemas.BifrostResponse) *schemas.BifrostResponse {
@@
-	// Build final choices from accumulated data
-	// Sort choice indices for deterministic output
+	// Build final choices from accumulated data
+	// Sort choice indices for deterministic output
 	choiceIndices := make([]int, 0, len(choiceMap))
 	for idx := range choiceMap {
 		choiceIndices = append(choiceIndices, idx)
 	}
+	sort.Ints(choiceIndices)
@@
 			if len(accum.toolCalls) > 0 {
-				// Sort tool calls by index
+				// Sort tool calls by index for deterministic output
 				tcIndices := make([]int, 0, len(accum.toolCalls))
 				for tcIdx := range accum.toolCalls {
 					tcIndices = append(tcIndices, tcIdx)
 				}
+				sort.Ints(tcIndices)

Also applies to: 260-271, 273-399, 432-443

framework/tracing/llmspan.go (1)

190-193: ExtraParams collision risk persists throughout the file.

User-provided ExtraParams keys are still written directly to the attrs map without namespacing (lines 190-193, 298-300, 365-367, 575-577, 708-710, 738-740, 753-755, 768-770, 783-785, 958-960, 982-984, 997-999, 1012-1014, 1027-1029). This issue was previously flagged in past review comments but remains unresolved.

A user could inadvertently or maliciously set ExtraParams["gen_ai.request.model"] which would overwrite the standard attribute set earlier in the function, corrupting trace data.

Consider namespacing all user-provided parameters with a consistent prefix (e.g., "gen_ai.extra." or "user.extra.") before insertion to prevent attribute key collisions.

Also applies to: 298-300, 365-367, 575-577, 708-710, 738-740, 753-755, 768-770, 783-785, 958-960, 982-984, 997-999, 1012-1014, 1027-1029

🧹 Nitpick comments (3)
ui/lib/store/apis/devApi.ts (1)

57-66: Clarify polling configuration location.

The comment mentions polling every 10 seconds, but RTK Query polling is configured by the hook consumer via the pollingInterval option, not in the endpoint definition. Consider updating the comment to reflect this, e.g., "consumers should poll every 10 seconds".

🔎 Suggested comment clarification
 export const devApi = baseApi.injectEndpoints({
   endpoints: (builder) => ({
-    // Get dev pprof data - polls every 10 seconds
+    // Get dev pprof data - consumers should use pollingInterval: 10000
     getDevPprof: builder.query<PprofData, void>({
       query: () => ({
         url: '/dev/pprof',
       }),
     }),
   }),
 })
transports/bifrost-http/server/server.go (1)

513-527: Consider reusing getGovernancePlugin() in GetGovernanceData

getGovernancePlugin already encapsulates locking, lookup, and the BaseGovernancePlugin assertion. GetGovernanceData reimplements a similar pattern manually. For consistency and one source of truth, you could delegate to getGovernancePlugin() here and just call GetGovernanceStore().GetGovernanceData() on the returned interface.

Also applies to: 659-671

framework/tracing/helpers.go (1)

22-29: Consider nil-guarding *TraceStore in helpers for defensive use

All helpers (GetTrace, AddSpan, AddChildSpan, EndSpan) assume store is non-nil. That’s true for the current Tracer wiring, but these are exported and may be reused elsewhere. Adding a quick if store == nil { return ... } would make them safer against accidental nil use without affecting existing call sites.

Also applies to: 31-38, 40-47, 49-56

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ee5875b and 74c473c.

⛔ Files ignored due to path filters (3)
  • core/go.sum is excluded by !**/*.sum
  • examples/plugins/hello-world/go.sum is excluded by !**/*.sum
  • transports/go.sum is excluded by !**/*.sum
📒 Files selected for processing (92)
  • .github/workflows/release-pipeline.yml
  • .github/workflows/scripts/push-mintlify-changelog.sh
  • .github/workflows/scripts/release-bifrost-http.sh
  • .github/workflows/scripts/release-framework.sh
  • core/bifrost.go
  • core/bifrost_test.go
  • core/changelog.md
  • core/go.mod
  • core/mcp.go
  • core/providers/utils/utils.go
  • core/schemas/bifrost.go
  • core/schemas/context.go
  • core/schemas/plugin.go
  • core/schemas/trace.go
  • core/schemas/tracer.go
  • core/version
  • docs/docs.json
  • docs/plugins/getting-started.mdx
  • docs/plugins/migration-guide.mdx
  • docs/plugins/writing-plugin.mdx
  • examples/plugins/hello-world/go.mod
  • examples/plugins/hello-world/main.go
  • framework/changelog.md
  • framework/configstore/tables/mcp.go
  • framework/plugins/dynamicplugin.go
  • framework/plugins/dynamicplugin_test.go
  • framework/streaming/accumulator.go
  • framework/streaming/audio.go
  • framework/streaming/chat.go
  • framework/streaming/responses.go
  • framework/streaming/transcription.go
  • framework/streaming/types.go
  • framework/tracing/helpers.go
  • framework/tracing/llmspan.go
  • framework/tracing/propagation.go
  • framework/tracing/store.go
  • framework/tracing/tracer.go
  • framework/version
  • plugins/governance/changelog.md
  • plugins/governance/go.mod
  • plugins/governance/main.go
  • plugins/jsonparser/changelog.md
  • plugins/jsonparser/main.go
  • plugins/logging/changelog.md
  • plugins/logging/main.go
  • plugins/logging/utils.go
  • plugins/maxim/changelog.md
  • plugins/maxim/main.go
  • plugins/mocker/changelog.md
  • plugins/mocker/main.go
  • plugins/otel/changelog.md
  • plugins/otel/converter.go
  • plugins/otel/main.go
  • plugins/otel/ttlsyncmap.go
  • plugins/semanticcache/changelog.md
  • plugins/semanticcache/main.go
  • plugins/telemetry/changelog.md
  • plugins/telemetry/main.go
  • transports/bifrost-http/handlers/cache.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/devpprof.go
  • transports/bifrost-http/handlers/devpprof_unix.go
  • transports/bifrost-http/handlers/devpprof_windows.go
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/handlers/health.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/handlers/logging.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/handlers/mcpserver.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • transports/bifrost-http/handlers/plugins.go
  • transports/bifrost-http/handlers/providers.go
  • transports/bifrost-http/handlers/session.go
  • transports/bifrost-http/handlers/ui.go
  • transports/bifrost-http/handlers/websocket.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/lib/config_test.go
  • transports/bifrost-http/lib/middleware.go
  • transports/bifrost-http/server/server.go
  • transports/changelog.md
  • transports/go.mod
  • transports/version
  • ui/app/clientLayout.tsx
  • ui/app/workspace/logs/views/logDetailsSheet.tsx
  • ui/components/devProfiler.tsx
  • ui/components/sidebar.tsx
  • ui/components/ui/switch.tsx
  • ui/lib/store/apis/devApi.ts
  • ui/lib/store/apis/index.ts
💤 Files with no reviewable changes (3)
  • framework/configstore/tables/mcp.go
  • plugins/otel/ttlsyncmap.go
  • core/mcp.go
🚧 Files skipped from review as they are similar to previous changes (39)
  • .github/workflows/release-pipeline.yml
  • transports/bifrost-http/handlers/governance.go
  • core/bifrost_test.go
  • transports/bifrost-http/handlers/cache.go
  • framework/streaming/chat.go
  • core/go.mod
  • framework/streaming/audio.go
  • plugins/semanticcache/main.go
  • ui/app/workspace/logs/views/logDetailsSheet.tsx
  • transports/bifrost-http/handlers/devpprof_windows.go
  • core/schemas/plugin.go
  • transports/bifrost-http/handlers/health.go
  • plugins/governance/go.mod
  • transports/bifrost-http/handlers/inference.go
  • docs/plugins/getting-started.mdx
  • ui/components/sidebar.tsx
  • .github/workflows/scripts/release-framework.sh
  • .github/workflows/scripts/push-mintlify-changelog.sh
  • core/schemas/context.go
  • plugins/mocker/main.go
  • docs/docs.json
  • docs/plugins/writing-plugin.mdx
  • core/schemas/bifrost.go
  • docs/plugins/migration-guide.mdx
  • plugins/jsonparser/main.go
  • transports/bifrost-http/handlers/plugins.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • transports/go.mod
  • ui/components/devProfiler.tsx
  • framework/streaming/responses.go
  • transports/changelog.md
  • plugins/jsonparser/changelog.md
  • framework/streaming/types.go
  • transports/version
  • framework/version
  • plugins/telemetry/main.go
  • examples/plugins/hello-world/go.mod
  • plugins/telemetry/changelog.md
  • core/version
🧰 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:

  • framework/streaming/transcription.go
  • transports/bifrost-http/lib/middleware.go
  • framework/plugins/dynamicplugin.go
  • transports/bifrost-http/handlers/mcpserver.go
  • core/providers/utils/utils.go
  • ui/components/ui/switch.tsx
  • transports/bifrost-http/handlers/ui.go
  • plugins/otel/main.go
  • plugins/logging/changelog.md
  • transports/bifrost-http/integrations/router.go
  • ui/lib/store/apis/index.ts
  • transports/bifrost-http/handlers/providers.go
  • plugins/mocker/changelog.md
  • framework/changelog.md
  • plugins/logging/utils.go
  • transports/bifrost-http/handlers/integrations.go
  • framework/tracing/propagation.go
  • examples/plugins/hello-world/main.go
  • transports/bifrost-http/handlers/logging.go
  • framework/tracing/store.go
  • core/changelog.md
  • plugins/semanticcache/changelog.md
  • framework/streaming/accumulator.go
  • plugins/logging/main.go
  • transports/bifrost-http/handlers/middlewares.go
  • ui/app/clientLayout.tsx
  • transports/bifrost-http/handlers/devpprof_unix.go
  • plugins/otel/changelog.md
  • transports/bifrost-http/lib/config_test.go
  • transports/bifrost-http/handlers/devpprof.go
  • plugins/governance/main.go
  • plugins/maxim/main.go
  • ui/lib/store/apis/devApi.ts
  • core/schemas/tracer.go
  • core/schemas/trace.go
  • core/bifrost.go
  • framework/plugins/dynamicplugin_test.go
  • framework/tracing/helpers.go
  • plugins/governance/changelog.md
  • transports/bifrost-http/handlers/websocket.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/mcp.go
  • plugins/otel/converter.go
  • framework/tracing/tracer.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/server/server.go
  • plugins/maxim/changelog.md
  • transports/bifrost-http/handlers/session.go
  • framework/tracing/llmspan.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:

  • framework/streaming/transcription.go
  • transports/bifrost-http/lib/middleware.go
  • framework/plugins/dynamicplugin.go
  • transports/bifrost-http/handlers/mcpserver.go
  • core/providers/utils/utils.go
  • transports/bifrost-http/handlers/ui.go
  • plugins/otel/main.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/handlers/providers.go
  • plugins/logging/utils.go
  • transports/bifrost-http/handlers/integrations.go
  • framework/tracing/propagation.go
  • examples/plugins/hello-world/main.go
  • transports/bifrost-http/handlers/logging.go
  • framework/tracing/store.go
  • framework/streaming/accumulator.go
  • plugins/logging/main.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/handlers/devpprof_unix.go
  • transports/bifrost-http/lib/config_test.go
  • transports/bifrost-http/handlers/devpprof.go
  • plugins/governance/main.go
  • plugins/maxim/main.go
  • core/schemas/tracer.go
  • core/schemas/trace.go
  • core/bifrost.go
  • framework/plugins/dynamicplugin_test.go
  • framework/tracing/helpers.go
  • transports/bifrost-http/handlers/websocket.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/mcp.go
  • plugins/otel/converter.go
  • framework/tracing/tracer.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/server/server.go
  • transports/bifrost-http/handlers/session.go
  • framework/tracing/llmspan.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/lib/middleware.go
  • transports/bifrost-http/handlers/mcpserver.go
  • transports/bifrost-http/handlers/ui.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/handlers/providers.go
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/handlers/logging.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/handlers/devpprof_unix.go
  • transports/bifrost-http/lib/config_test.go
  • transports/bifrost-http/handlers/devpprof.go
  • transports/bifrost-http/handlers/websocket.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/server/server.go
  • transports/bifrost-http/handlers/session.go
📚 Learning: 2025-12-19T09:26:54.961Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/providers/utils/utils.go:1050-1051
Timestamp: 2025-12-19T09:26:54.961Z
Learning: Update streaming end-marker handling so HuggingFace is treated as a non-[DONE] provider for backends that do not emit a DONE marker (e.g., meta llama on novita). In core/providers/utils/utils.go, adjust ProviderSendsDoneMarker() (or related logic) to detect providers that may not emit DONE and avoid relying on DONE as the sole end signal. Add tests to cover both DONE-emitting and non-DONE backends, with clear documentation in code comments explaining the rationale and any fallback behavior.

Applied to files:

  • core/providers/utils/utils.go
📚 Learning: 2025-12-24T10:55:31.424Z
Learnt from: akshaydeo
Repo: maximhq/bifrost PR: 888
File: framework/tracing/propagation.go:24-36
Timestamp: 2025-12-24T10:55:31.424Z
Learning: In framework/tracing/propagation.go, ensure ExtractParentID() returns ctx.TraceID (not ctx.ParentID) because the parent request's traceparent header carries the trace ID used for continuing the trace.

Applied to files:

  • framework/tracing/propagation.go
📚 Learning: 2025-12-22T10:50:40.990Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1154
File: plugins/governance/store.go:1165-1186
Timestamp: 2025-12-22T10:50:40.990Z
Learning: In the Bifrost governance plugin, budgets and rate limits have 1:1 relationships with their parent entities (virtual keys, teams, customers). Do not assume sharing; ensure cascade deletion logic only deletes budgets/rate limits when there are no shared references. Enforce invariants in code and add tests to verify no cross-entity sharing and that cascade deletes only remove the specific child of the parent. If a counterexample arises, adjust data model or add guards.

Applied to files:

  • plugins/governance/main.go
🧬 Code graph analysis (30)
framework/streaming/transcription.go (1)
framework/streaming/types.go (1)
  • AccumulatedData (28-48)
transports/bifrost-http/lib/middleware.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
framework/plugins/dynamicplugin.go (2)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
examples/plugins/hello-world/main.go (2)
  • GetName (15-17)
  • HTTPTransportMiddleware (19-27)
transports/bifrost-http/handlers/ui.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
plugins/otel/main.go (3)
core/schemas/plugin.go (2)
  • BifrostHTTPMiddleware (38-38)
  • ObservabilityPlugin (123-137)
core/schemas/context.go (1)
  • BifrostContext (32-42)
core/schemas/trace.go (1)
  • Trace (10-19)
transports/bifrost-http/integrations/router.go (2)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
core/schemas/bifrost.go (2)
  • BifrostContextKeyDeferTraceCompletion (148-148)
  • BifrostContextKeyTraceCompleter (149-149)
transports/bifrost-http/handlers/providers.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
plugins/logging/utils.go (4)
core/schemas/tracer.go (1)
  • StreamAccumulatorResult (15-33)
core/schemas/bifrost.go (6)
  • RequestType (89-89)
  • TextCompletionStreamRequest (94-94)
  • ChatCompletionStreamRequest (96-96)
  • ResponsesStreamRequest (98-98)
  • SpeechStreamRequest (101-101)
  • TranscriptionStreamRequest (103-103)
framework/streaming/types.go (11)
  • ProcessedStreamResponse (118-126)
  • StreamType (10-10)
  • StreamTypeText (13-13)
  • StreamTypeChat (14-14)
  • StreamTypeResponses (17-17)
  • StreamTypeAudio (15-15)
  • StreamTypeTranscription (16-16)
  • StreamResponseType (20-20)
  • StreamResponseTypeFinal (24-24)
  • StreamResponseTypeDelta (23-23)
  • AccumulatedData (28-48)
core/schemas/chatcompletions.go (1)
  • ChatAssistantMessage (659-666)
transports/bifrost-http/handlers/integrations.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
examples/plugins/hello-world/main.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/logging.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
framework/tracing/store.go (3)
framework/tracing/tracer.go (1)
  • Tracer (17-20)
core/schemas/trace.go (6)
  • Trace (10-19)
  • Span (52-65)
  • SpanEvent (107-111)
  • SpanKind (115-115)
  • SpanStatusUnset (147-147)
  • SpanStatus (143-143)
framework/tracing/helpers.go (3)
  • GetTrace (23-29)
  • AddSpan (32-38)
  • EndSpan (50-56)
framework/streaming/accumulator.go (2)
framework/streaming/types.go (3)
  • StreamAccumulator (103-115)
  • ChatStreamChunk (77-87)
  • ResponsesStreamChunk (90-100)
plugins/semanticcache/main.go (1)
  • StreamAccumulator (116-126)
plugins/logging/main.go (5)
core/utils.go (1)
  • IsStreamRequestType (197-199)
core/schemas/bifrost.go (3)
  • RequestType (89-89)
  • BifrostContextKeyTracer (147-147)
  • BifrostContextKeyTraceID (144-144)
core/schemas/tracer.go (1)
  • Tracer (38-116)
framework/tracing/tracer.go (1)
  • Tracer (17-20)
framework/streaming/types.go (2)
  • ProcessedStreamResponse (118-126)
  • StreamResponseTypeFinal (24-24)
transports/bifrost-http/handlers/middlewares.go (5)
core/schemas/plugin.go (3)
  • BifrostHTTPMiddleware (38-38)
  • ObservabilityPlugin (123-137)
  • Plugin (71-97)
transports/bifrost-http/lib/middleware.go (1)
  • ChainMiddlewares (11-23)
framework/tracing/tracer.go (1)
  • Tracer (17-20)
framework/tracing/propagation.go (1)
  • ExtractParentID (74-84)
core/schemas/bifrost.go (1)
  • BifrostContextKeyTraceID (144-144)
ui/app/clientLayout.tsx (1)
ui/components/devProfiler.tsx (1)
  • DevProfiler (55-422)
transports/bifrost-http/lib/config_test.go (1)
transports/bifrost-http/lib/config.go (1)
  • LoadConfig (278-358)
transports/bifrost-http/handlers/devpprof.go (4)
ui/lib/store/apis/devApi.ts (6)
  • MemoryStats (4-10)
  • CPUStats (13-17)
  • RuntimeStats (20-26)
  • AllocationInfo (29-35)
  • HistoryPoint (38-45)
  • PprofData (48-55)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/lib/middleware.go (1)
  • ChainMiddlewares (11-23)
transports/bifrost-http/handlers/utils.go (1)
  • SendJSON (16-22)
plugins/governance/main.go (4)
ui/components/header.tsx (1)
  • Header (4-14)
core/utils.go (1)
  • Ptr (56-58)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-27)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
plugins/maxim/main.go (2)
core/schemas/plugin.go (2)
  • Plugin (71-97)
  • BifrostHTTPMiddleware (38-38)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-27)
ui/lib/store/apis/devApi.ts (2)
transports/bifrost-http/handlers/devpprof.go (6)
  • MemoryStats (29-35)
  • CPUStats (38-42)
  • RuntimeStats (45-51)
  • AllocationInfo (54-60)
  • HistoryPoint (63-70)
  • PprofData (73-80)
ui/lib/store/apis/index.ts (1)
  • baseApi (2-2)
core/schemas/trace.go (2)
framework/tracing/helpers.go (1)
  • AddSpan (32-38)
ui/lib/constants/logs.ts (1)
  • Status (171-171)
framework/plugins/dynamicplugin_test.go (1)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-27)
framework/tracing/helpers.go (3)
core/schemas/bifrost.go (1)
  • BifrostContextKeyTraceID (144-144)
framework/tracing/store.go (1)
  • TraceStore (25-37)
core/schemas/trace.go (5)
  • Trace (10-19)
  • SpanKind (115-115)
  • Span (52-65)
  • SpanStatus (143-143)
  • SpanEvent (107-111)
transports/bifrost-http/handlers/websocket.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/config.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/mcp.go (2)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/lib/ctx.go (1)
  • ConvertToBifrostContext (80-409)
plugins/otel/converter.go (2)
plugins/otel/main.go (1)
  • OtelPlugin (58-75)
core/schemas/trace.go (15)
  • Trace (10-19)
  • Span (52-65)
  • SpanKind (115-115)
  • SpanKindLLMCall (121-121)
  • SpanKindInternal (139-139)
  • SpanKindRetry (127-127)
  • SpanKindFallback (129-129)
  • SpanKindMCPTool (125-125)
  • SpanKindEmbedding (133-133)
  • SpanKindSpeech (135-135)
  • SpanKindTranscription (137-137)
  • SpanStatus (143-143)
  • SpanStatusOk (149-149)
  • SpanStatusError (151-151)
  • SpanEvent (107-111)
transports/bifrost-http/lib/config.go (2)
framework/modelcatalog/main.go (1)
  • Init (88-138)
framework/configstore/store.go (1)
  • ConfigStore (18-162)
transports/bifrost-http/handlers/session.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)

Comment thread framework/plugins/dynamicplugin.go
Comment thread transports/bifrost-http/handlers/middlewares.go
@akshaydeo akshaydeo force-pushed the 11-19-plugins_v2_architecture branch from 74c473c to 408a05b Compare December 29, 2025 09:18
Copy link
Copy Markdown
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

♻️ Duplicate comments (2)
framework/plugins/dynamicplugin.go (1)

146-146: Fix the error message to match the actual cast type.

The error message on line 146 states "func() fasthttp.RequestHandler", but the actual cast on line 145 is to func() schemas.BifrostHTTPMiddleware. Update the error message to reflect the correct type being validated.

🔎 Suggested fix
-		return nil, fmt.Errorf("failed to cast HTTPTransportMiddleware to func() fasthttp.RequestHandler")
+		return nil, fmt.Errorf("failed to cast HTTPTransportMiddleware to func() schemas.BifrostHTTPMiddleware")
framework/tracing/tracer.go (1)

394-398: Missing sort implementation despite comments indicating sorting intent.

The comments on lines 394 and 433 state "Sort choice indices for deterministic output" and "Sort tool calls by index" respectively, but the actual sort.Ints() calls are missing. Without sorting, the order of choices and tool calls will be nondeterministic when iterating over the map-derived slices.

🔎 Add missing sort calls
 // Sort choice indices for deterministic output
 choiceIndices := make([]int, 0, len(choiceMap))
 for idx := range choiceMap {
 	choiceIndices = append(choiceIndices, idx)
 }
+sort.Ints(choiceIndices)

 for _, idx := range choiceIndices {

And for tool calls:

 // Sort tool calls by index
 tcIndices := make([]int, 0, len(accum.toolCalls))
 for tcIdx := range accum.toolCalls {
 	tcIndices = append(tcIndices, tcIdx)
 }
+sort.Ints(tcIndices)
 toolCalls := make([]schemas.ChatAssistantMessageToolCall, 0, len(accum.toolCalls))

Note: You'll need to add "sort" to the imports at the top of the file.

Also applies to: 433-437

🧹 Nitpick comments (2)
core/go.mod (1)

50-50: Secure the /pprof endpoint if exposed in production.

The pprof version bump to v0.0.0-20251213031049-b05bdaca462f is compatible with Go 1.25.5 and has no known security vulnerabilities. However, if the new /pprof endpoint is exposed beyond localhost, restrict access via firewall, authentication, or reverse proxy, as profiling endpoints can be exploited for information gathering or DoS attacks.

framework/tracing/tracer.go (1)

469-476: Consider logging cleanup errors for observability.

Line 475 silently ignores the error returned by CleanupStreamAccumulator. While this may be acceptable in cleanup paths, logging the error could help diagnose issues with accumulator lifecycle management.

💡 Optional: Log cleanup errors
 func (t *Tracer) CleanupStreamAccumulator(traceID string) {
 	if traceID == "" || t.accumulator == nil {
 		return
 	}
-	_ = t.accumulator.CleanupStreamAccumulator(traceID)
+	if err := t.accumulator.CleanupStreamAccumulator(traceID); err != nil && t.store.logger != nil {
+		t.store.logger.Warn("Failed to cleanup stream accumulator", "traceID", traceID, "error", err)
+	}
 }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 74c473c and 408a05b.

⛔ Files ignored due to path filters (3)
  • core/go.sum is excluded by !**/*.sum
  • examples/plugins/hello-world/go.sum is excluded by !**/*.sum
  • transports/go.sum is excluded by !**/*.sum
📒 Files selected for processing (5)
  • core/go.mod
  • examples/plugins/hello-world/go.mod
  • framework/plugins/dynamicplugin.go
  • framework/tracing/tracer.go
  • transports/go.mod
🚧 Files skipped from review as they are similar to previous changes (1)
  • transports/go.mod
🧰 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/go.mod
  • framework/plugins/dynamicplugin.go
  • examples/plugins/hello-world/go.mod
  • framework/tracing/tracer.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:

  • framework/plugins/dynamicplugin.go
  • framework/tracing/tracer.go
🧬 Code graph analysis (2)
framework/plugins/dynamicplugin.go (2)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
examples/plugins/hello-world/main.go (2)
  • GetName (15-17)
  • HTTPTransportMiddleware (19-27)
framework/tracing/tracer.go (8)
framework/tracing/store.go (1)
  • TraceStore (25-37)
framework/streaming/accumulator.go (2)
  • Accumulator (14-30)
  • NewAccumulator (440-480)
core/schemas/tracer.go (3)
  • Tracer (38-116)
  • SpanHandle (11-11)
  • StreamAccumulatorResult (15-33)
framework/tracing/helpers.go (3)
  • GetTraceID (11-20)
  • EndSpan (50-56)
  • GetTrace (23-29)
framework/tracing/llmspan.go (1)
  • PopulateRequestAttributes (14-60)
core/schemas/chatcompletions.go (2)
  • BifrostChatResponse (27-42)
  • BifrostResponseChoice (745-753)
core/schemas/context.go (2)
  • BifrostContext (32-42)
  • NewBifrostContext (47-65)
framework/streaming/types.go (1)
  • StreamResponseTypeFinal (24-24)
🔇 Additional comments (9)
framework/plugins/dynamicplugin.go (2)

24-28: LGTM! Field declarations correctly updated for middleware pattern.

The httpTransportMiddleware field type correctly stores a function that returns schemas.BifrostHTTPMiddleware, matching the new plugin interface and the example implementation pattern.


36-39: LGTM! Accessor correctly implements the new interface.

The accessor properly invokes the stored middleware factory function and returns the middleware, matching the updated Plugin interface signature.

examples/plugins/hello-world/go.mod (2)

5-8: ✓ Direct dependency on fasthttp v1.68.0 properly added for HTTPTransportMiddleware implementation.

The addition of github.com/valyala/fasthttp v1.68.0 as a direct dependency is appropriate for the new middleware pattern introduced in this PR. The version is now consistent with the rest of the codebase, addressing the earlier inconsistency noted in previous reviews. The dependency structure supports the shift from TransportInterceptor to HTTPTransportMiddleware.


10-22: ✓ Transitive dependencies updated for consistency across plugin stack.

The indirect dependencies have been properly updated to align with other plugins (governance, logging, telemetry):

  • klauspost/compress v1.18.2 ✓
  • golang.org/x/arch v0.23.0 ✓ (single entry, no duplicates)
  • golang.org/x/sys v0.39.0 ✓

All shared transitive dependencies are now consistent across the plugin stack.

framework/tracing/tracer.go (5)

13-29: LGTM! Clean struct design and constructor.

The Tracer struct properly wraps both the TraceStore and streaming.Accumulator to provide centralized tracing and streaming functionality. The constructor correctly initializes both components with the required dependencies.


31-128: LGTM! Solid trace and span lifecycle implementation.

The trace and span lifecycle methods properly handle:

  • Context-based span hierarchy with parent-child relationships
  • Safe type assertions with nil checks
  • Appropriate delegation to the underlying store
  • Proper context propagation for distributed tracing

130-206: LGTM! Proper attribute population and deferred span handling.

The LLM attribute methods correctly delegate to helper functions and populate span attributes. The deferred span methods safely handle type conversions and provide clean access to the underlying store's deferred span functionality.


478-531: Well-designed context handling for streaming accumulation.

ProcessStreamingChunk properly handles the challenge of mapping trace IDs to request IDs in the accumulator through careful context manipulation. The implementation:

  • Clearly documents why the workaround is needed (reserved key limitation)
  • Preserves critical context values like the stream end indicator
  • Comprehensively converts between the accumulator's result type and the tracer's result type
  • Includes appropriate nil checks

533-551: LGTM! Proper resource cleanup and interface compliance.

The Stop() method now correctly releases resources from both the store and the accumulator, preventing goroutine and ticker leaks. The compile-time interface assertion ensures the implementation stays in sync with the schemas.Tracer interface.

@akshaydeo akshaydeo force-pushed the 11-19-plugins_v2_architecture branch from 408a05b to 5456aad Compare December 29, 2025 09:58
Copy link
Copy Markdown
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

Caution

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

⚠️ Outside diff range comments (1)
transports/bifrost-http/lib/config.go (1)

1527-1559: Avoid potential nil deref on PricingSyncInterval and align pricing init behavior

In initFrameworkConfigFromFile, the config-store path guards frameworkConfig.PricingSyncInterval for nil, but the config-file fallback does:

syncDuration := time.Duration(*configData.FrameworkConfig.Pricing.PricingSyncInterval) * time.Second
pricingConfig.PricingSyncInterval = &syncDuration

without checking that PricingSyncInterval is non-nil. If framework / pricing is present in config.json but pricing_sync_interval is omitted or left null, this will panic during startup.

It would be safer to mirror the guard pattern used in initDefaultFrameworkConfig (and optionally fall back to modelcatalog.DefaultPricingSyncInterval) so misconfigured or partial configs don’t crash the server.

Additionally, initFrameworkConfigFromFile now only logs pricing-manager initialization failures, while initDefaultFrameworkConfig still returns an error and causes LoadConfig to fail. If this divergence is intentional (config-file path being best-effort, default path being strict), consider documenting it; otherwise, you may want to make the behavior consistent.

Suggested nil-safety tweak for config-file pricing interval
-   } else if configData.FrameworkConfig != nil && configData.FrameworkConfig.Pricing != nil {
-       pricingConfig.PricingURL = configData.FrameworkConfig.Pricing.PricingURL
-       syncDuration := time.Duration(*configData.FrameworkConfig.Pricing.PricingSyncInterval) * time.Second
-       pricingConfig.PricingSyncInterval = &syncDuration
-   }
+   } else if configData.FrameworkConfig != nil && configData.FrameworkConfig.Pricing != nil {
+       pricingConfig.PricingURL = configData.FrameworkConfig.Pricing.PricingURL
+       if configData.FrameworkConfig.Pricing.PricingSyncInterval != nil &&
+           *configData.FrameworkConfig.Pricing.PricingSyncInterval > 0 {
+           syncDuration := time.Duration(*configData.FrameworkConfig.Pricing.PricingSyncInterval) * time.Second
+           pricingConfig.PricingSyncInterval = &syncDuration
+       }
+       // Optionally: else default to modelcatalog.DefaultPricingSyncInterval for symmetry
+   }

Also applies to: 1590-1647, 1885-1937

♻️ Duplicate comments (8)
ui/components/ui/switch.tsx (1)

13-16: Breaking changes already flagged in previous review.

The default size change from "default" to "md" and the border radius change from rounded-full to rounded-sm were already identified as breaking changes in previous review comments. Please ensure these visual changes are intentional and that all affected Switch usages have been audited.

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

513-513: Non-streaming requests pass ctx directly to Bifrost client.

These lines pass ctx (the *fasthttp.RequestCtx) to the Bifrost client methods. Based on the retrieved learnings, this is intentional: "fasthttp.RequestCtx is the primary context carrier and should be passed directly to functions that expect a context.Context."

The bifrostCtx is used for enriched context values (like BifrostContextKeyIntegrationType), while ctx provides the actual request context for the Bifrost client calls.

Also applies to: 519-519, 539-539, 562-562, 585-585, 608-608, 631-631, 665-665, 688-688

framework/plugins/dynamicplugin.go (1)

145-147: Error message mismatch with actual type.

The error message states "failed to cast HTTPTransportMiddleware to func() fasthttp.RequestHandler" but the actual cast is to func() schemas.BifrostHTTPMiddleware. This discrepancy can confuse plugin developers when debugging cast failures.

🔎 Proposed fix
-	if dp.httpTransportMiddleware, ok = httpTransportMiddlewareSym.(func() schemas.BifrostHTTPMiddleware); !ok {
-		return nil, fmt.Errorf("failed to cast HTTPTransportMiddleware to func() fasthttp.RequestHandler")
+	if dp.httpTransportMiddleware, ok = httpTransportMiddlewareSym.(func() schemas.BifrostHTTPMiddleware); !ok {
+		return nil, fmt.Errorf("failed to cast HTTPTransportMiddleware to func() schemas.BifrostHTTPMiddleware")
	}
core/providers/utils/utils.go (1)

1428-1431: Context should be passed by value, not pointer.

The function signature completeDeferredSpan(ctx *context.Context, ...) violates Go conventions. Contexts should always be passed by value as the context.Context type is designed to be passed by value and is safe for concurrent use.

🔎 Proposed fix
-func completeDeferredSpan(ctx *context.Context, result *schemas.BifrostResponse, err *schemas.BifrostError) {
-	if ctx == nil {
+func completeDeferredSpan(ctx context.Context, result *schemas.BifrostResponse, err *schemas.BifrostError) {
+	if ctx == nil {
 		return
 	}
 
-	traceID, ok := (*ctx).Value(schemas.BifrostContextKeyTraceID).(string)
+	traceID, ok := ctx.Value(schemas.BifrostContextKeyTraceID).(string)

And update call sites:

-			completeDeferredSpan(&ctx, processedResponse, processedError)
+			completeDeferredSpan(ctx, processedResponse, processedError)
framework/tracing/store.go (1)

13-22: Remove unused Tracer and RequestID fields from DeferredSpanInfo.

The Tracer (line 17) and RequestID (line 18) fields are never assigned. StoreDeferredSpan() only initializes SpanID and StartTime (lines 121-124). These fields add memory overhead without being used.

🔎 Proposed fix
 // DeferredSpanInfo stores information about a deferred span for streaming requests
 type DeferredSpanInfo struct {
 	SpanID             string
 	StartTime          time.Time
-	Tracer             schemas.Tracer // Reference to tracer for completing the span
-	RequestID          string         // Request ID for accumulator lookup
 	FirstChunkTime     time.Time      // Timestamp of first chunk (for TTFT calculation)
 	AccumulatedChunks  []*schemas.BifrostResponse // Accumulated streaming chunks
 	mu                 sync.Mutex     // Mutex for thread-safe chunk accumulation
 }
plugins/otel/converter.go (1)

165-170: Potential overflow when converting uint64 to int64.

Lines 169-170 cast uint64 to int64 without checking for overflow. Values greater than math.MaxInt64 (~9.2 quintillion) will wrap to negative numbers.

While uncommon in practice, consider adding a bounds check for defensive coding.

🔎 Proposed fix
 	case uint64:
+		if v > math.MaxInt64 {
+			return kvInt(key, math.MaxInt64)
+		}
 		return kvInt(key, int64(v))
plugins/governance/main.go (2)

291-297: Fix error message accuracy.

Line 294: The error message says "failed to marshal" but the operation is sonic.Unmarshal (unmarshaling).

🔎 Proposed fix
 		err = sonic.Unmarshal(ctx.Request.Body(), &payload)
 		if err != nil {
-			p.logger.Error("failed to marshal request body to check for virtual key: %v", err)
+			p.logger.Error("failed to unmarshal request body: %v", err)
 			next(ctx)
 			return
 		}

233-261: Missing prefix validation for x-bf-vk header.

Line 237 returns the x-bf-vk header value without validating that it starts with VirtualKeyPrefix (sk-bf-), unlike the Authorization and x-api-key headers which do validate the prefix. This inconsistency could allow invalid virtual keys to pass through.

🔎 Proposed fix
 func parseVirtualKey(ctx *fasthttp.RequestCtx) *string {
 	var virtualKeyValue string
 	vkHeader := ctx.Request.Header.Peek("x-bf-vk")
 	if string(vkHeader) != "" {
-		return bifrost.Ptr(string(vkHeader))
+		vkValue := string(vkHeader)
+		if strings.HasPrefix(strings.ToLower(vkValue), VirtualKeyPrefix) {
+			return bifrost.Ptr(vkValue)
+		}
 	}
🧹 Nitpick comments (20)
.github/workflows/scripts/push-mintlify-changelog.sh (1)

244-244: Add error handling for git operations.

The git pull (and subsequent git push at line 255) lack error handling. If the pull fails due to merge conflicts, network issues, or the branch not existing remotely, the script continues execution, potentially leading to incomplete or failed deployments.

🔎 Proposed improvements to add error handling

Add set -e at the top of the script to exit on errors, or add explicit error checks:

 # Pulling again before committing
 CURRENT_BRANCH="$(git rev-parse --abbrev-ref HEAD)"
 if [ "$CURRENT_BRANCH" = "HEAD" ]; then
   # In detached HEAD state (common in CI), use GITHUB_REF_NAME or default to main
   CURRENT_BRANCH="${GITHUB_REF_NAME:-main}"
 fi
-git pull origin "$CURRENT_BRANCH"
+if ! git pull origin "$CURRENT_BRANCH"; then
+  echo "❌ Failed to pull from origin/$CURRENT_BRANCH"
+  exit 1
+fi

Similarly for git push:

-git push origin "$CURRENT_BRANCH"
+if ! git push origin "$CURRENT_BRANCH"; then
+  echo "❌ Failed to push to origin/$CURRENT_BRANCH"
+  exit 1
+fi

Alternatively, add set -e near the top of the script (after the shebang) to automatically exit on any command failure:

 #!/usr/bin/env bash
+set -e
 
 VERSION=$1
plugins/semanticcache/changelog.md (1)

1-18: Consider adding a version header for clarity.

The changelog documents the breaking changes well, but it lacks a version header (e.g., ## v1.x.x). Adding a version number would help users quickly identify which release introduced these changes, consistent with standard changelog conventions.

🔎 Suggested format
+## v1.x.x
+
 - chore: upgraded versions of core to 1.3.0 and framework to 1.2.0
 
 ### BREAKING CHANGES
plugins/logging/changelog.md (1)

1-19: LGTM! Changelog documents both feature and breaking changes clearly.

The changelog effectively communicates the new central accumulator feature and the breaking API change. The migration guidance is helpful for plugin consumers.

Consider adding a version header for consistency with standard changelog conventions, similar to the suggestion for semanticcache/changelog.md.

plugins/logging/utils.go (1)

13-14: Streaming accumulator → ProcessedStreamResponse mapping looks correct

The field mapping from StreamAccumulatorResult into AccumulatedData / ProcessedStreamResponse is consistent with the referenced types, including correct StreamType/StreamResponseType selection and tool call propagation. If you expect new RequestType values in the stack, you might consider logging when the default branch is hit instead of silently defaulting to StreamTypeChat, but the current behavior is safe.

Also applies to: 229-299

ui/lib/store/apis/devApi.ts (1)

1-71: Dev pprof API slice matches backend contracts

The TS interfaces line up with the Go devpprof structs (field names and types), and the /dev/pprof endpoint wiring via baseApi looks correct. The “polls every 10 seconds” note is purely documentary here; ensure the actual polling interval is configured where the hook is consumed (e.g., useGetDevPprofQuery options) if that behavior is required.

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

308-355: Tests correctly updated to use schemas.BifrostHTTPMiddleware

The middleware constructions using schemas.BifrostHTTPMiddleware(func(next fasthttp.RequestHandler) fasthttp.RequestHandler { ... }) keep the tests aligned with the new core middleware type and still validate chaining, ordering, context mutation, and short‑circuit behavior through lib.ChainMiddlewares.

If you want to reduce noise, you could rely on type inference, e.g.:

var middleware schemas.BifrostHTTPMiddleware = func(next fasthttp.RequestHandler) fasthttp.RequestHandler { ... }

but that’s purely stylistic.

Also applies to: 381-399, 409-427, 473-491

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

73-80: Config routes now use core middleware type and expose proxy/pricing endpoints

Updating RegisterRoutes to accept ...schemas.BifrostHTTPMiddleware and wiring /api/proxy-config and /api/pricing/force-sync through lib.ChainMiddlewares is consistent with the new middleware architecture and makes the existing handlers reachable. You might also update the comment above to reflect the full set of routes, but behavior-wise this looks good.

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

1198-1216: Consider safer type assertion for trace completer retrieval.

The type assertion on Line 1203 uses the comma-ok pattern but discards the ok value. While the code will safely skip calling traceCompleter if it's nil, a malformed value of the wrong type could cause a silent failure rather than being caught early.

🔎 Suggested improvement for safer type assertion
-	// Get the trace completer function for use in the streaming callback
-	traceCompleter, _ := ctx.UserValue(schemas.BifrostContextKeyTraceCompleter).(func())
+	// Get the trace completer function for use in the streaming callback
+	traceCompleter, ok := ctx.UserValue(schemas.BifrostContextKeyTraceCompleter).(func())
+	if !ok && ctx.UserValue(schemas.BifrostContextKeyTraceCompleter) != nil {
+		logger.Warn("BifrostContextKeyTraceCompleter has unexpected type")
+	}
transports/changelog.md (1)

15-22: Add language specifier to the code fence.

The fenced code block is missing a language identifier, which prevents proper syntax highlighting.

🔎 Suggested fix
   **Migration summary:**
-  ```
+  ```go
   // v1.3.x (removed)
   TransportInterceptor(ctx *BifrostContext, url string, headers map[string]string, body map[string]any) (map[string]string, map[string]any, error)

As per static analysis hints, this addresses the MD040 lint rule.

plugins/governance/main.go (1)

42-49: Interface still references deprecated TransportInterceptor.

The BaseGovernancePlugin interface still declares TransportInterceptor (line 44) while the actual implementation now uses HTTPTransportMiddleware. This creates an inconsistency between the interface contract and the implementation.

Consider updating this interface to reflect the new middleware pattern or deprecating it if no longer needed.

🔎 Proposed fix
 type BaseGovernancePlugin interface {
 	GetName() string
-	TransportInterceptor(ctx *schemas.BifrostContext, url string, headers map[string]string, body map[string]any) (map[string]string, map[string]any, error)
+	HTTPTransportMiddleware() schemas.BifrostHTTPMiddleware
 	PreHook(ctx *schemas.BifrostContext, req *schemas.BifrostRequest) (*schemas.BifrostRequest, *schemas.PluginShortCircuit, error)
 	PostHook(ctx *schemas.BifrostContext, result *schemas.BifrostResponse, err *schemas.BifrostError) (*schemas.BifrostResponse, *schemas.BifrostError, error)
 	Cleanup() error
 	GetGovernanceStore() GovernanceStore
 }
transports/bifrost-http/handlers/devpprof.go (1)

104-122: Global collector singleton may cause lifecycle issues.

The globalCollector with sync.Once creates a single shared instance. If Stop() is called (line 152-154), the collectorOnce is never reset, so subsequent calls to getOrCreateCollector() will return the stopped collector. This could cause issues if NewDevPprofHandler() is called after cleanup.

Consider adding a reset mechanism or documenting that the collector is intended for single-lifecycle use.

framework/tracing/helpers.go (1)

1-83: Helpers correctly encapsulate common TraceStore operations; consider nil-store guard

The helpers cleanly centralize trace ID lookup and span/attribute/event manipulation against TraceStore. If you expect these to be called from broader call sites over time, you might optionally early-return when store is nil in GetTrace, AddSpan, AddChildSpan, EndSpan, SetSpanAttribute, and AddSpanEvent to avoid panics from accidental nil stores; current call sites appear to always pass a real store.

core/schemas/tracer.go (1)

9-187: Tracer interface and NoOpTracer implementation are coherent with new tracing usage

The Tracer interface cleanly captures span lifecycle, LLM attribute helpers, deferred span/streaming accumulation, and shutdown, and NoOpTracer correctly implements a fully inert default that keeps existing behavior unchanged when tracing is disabled. Using SpanHandle as interface{} gives implementations flexibility, though if you ever want stronger type safety you could wrap it in a dedicated named type without changing semantics.

core/bifrost.go (3)

118-147: Tracer initialization and SetTracer/getTracer semantics are reasonable

Init now installs either a provided tracer or schemas.DefaultTracer() and stores it as a *tracerWrapper; SetTracer defensively replaces nil with DefaultTracer() before storing, and getTracer() simply loads the wrapper and returns its schemas.Tracer. Given Init always does an initial Store, the type assertion in getTracer() is safe under the current usage model.

Also applies to: 253-265


3045-3075: Key-selection span and streaming PostHook finalizer are well-integrated

Adding a SpanKindInternal “key.selection” span around selectKeyFromProviderForModel and attaching key ID/name attributes gives fine-grained visibility into key choice, and updating req.Context with the span context ensures subsequent operations nest correctly.

For streaming requests, capturing a postHookSpanFinalizer closure that calls pipeline.FinalizeStreamingPostHookSpans and only releasing the pipeline from that finalizer (or immediately on error) is a sensible way to avoid leaking pipelines while still aggregating per-plugin post-hook timings across chunks.

Also applies to: 3092-3099, 3102-3118


3451-3541: Streaming timing aggregation and FinalizeStreamingPostHookSpans look correct

The reset logic, timing accumulator, and FinalizeStreamingPostHookSpans flow are coherent:

  • resetPluginPipeline clears counts and timing structures for reuse.
  • accumulatePluginTiming lazily initializes per-plugin accumulators and preserves plugin execution order.
  • FinalizeStreamingPostHookSpans builds a nested span hierarchy in plugin order, attaches aggregated attributes (invocations, avg/total duration, error count), and unwinds them with appropriate statuses.

GetChunkCount simply exposes chunkCount, which will be useful for higher-level observability.

transports/bifrost-http/server/server.go (3)

204-207: LoadPlugin refactor (including governance) centralizes plugin initialization well

The generic LoadPlugin[T schemas.Plugin] helper:

  • Cleanly handles dynamic plugins when path != nil.
  • Initializes built-in plugins (telemetry, logging, governance, maxim, semanticcache, otel) with the right config/state (including the new GovernanceInMemoryStore using *lib.Config).
  • Returns strongly-typed instances, with clear type-mismatch errors per plugin.

This makes plugin loading from both Bootstrap and ReloadPlugin much more uniform.

Also applies to: 215-325


509-527: Governance plugin accessors now consistently work through FindPluginByName and BaseGovernancePlugin

Both getGovernancePlugin and GetGovernanceData now:

  • Look up the governance plugin by governance.PluginName under PluginsMutex.RLock().
  • Verify the plugin implements governance.BaseGovernancePlugin before use.

This keeps all governance-related APIs using a single, type-safe access path.

Also applies to: 658-671


1239-1272: Tracing bootstrap for inference routes is wired appropriately

In Bootstrap:

  • Inference middlewares are extended with handlers.TransportInterceptorMiddleware(s.Config) first.
  • Observability plugins are collected via ObservabilityPlugin interface checks, and logging-plugin presence is detected explicitly.
  • When either observability or logging is active, a TraceStore and Tracer are created, installed into the Bifrost client via SetTracer, and a TracingMiddleware is prepended to the inference middleware chain.

This ensures tracing (including central streaming accumulation) is only enabled when needed, and that the HTTP layer, tracer, and core Bifrost all share the same tracer instance.

framework/tracing/llmspan.go (1)

191-193: Namespace ExtraParams keys to prevent attribute collisions.

User-provided ExtraParams keys are written directly into the attributes map without any prefix or namespace. If a user sets a key matching a standard attribute name (e.g., "gen_ai.request.model", "gen_ai.request.temperature"), it will silently overwrite the standard attribute set earlier.

This pattern occurs throughout the file at 14 locations. Consider prefixing all ExtraParams keys with a namespace like "gen_ai.extra." or "user.extra." to prevent collisions.

🔎 Example fix for one location
 	// ExtraParams
 	for k, v := range req.Params.ExtraParams {
-		attrs[k] = fmt.Sprintf("%v", v)
+		attrs["gen_ai.extra."+k] = fmt.Sprintf("%v", v)
 	}

Apply this pattern consistently across all 14 occurrences.

Also applies to: 298-300, 365-367, 575-577, 708-710, 738-740, 753-755, 768-770, 783-785, 958-960, 982-984, 997-999, 1012-1014, 1027-1029

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 408a05b and 5456aad.

⛔ Files ignored due to path filters (3)
  • core/go.sum is excluded by !**/*.sum
  • examples/plugins/hello-world/go.sum is excluded by !**/*.sum
  • transports/go.sum is excluded by !**/*.sum
📒 Files selected for processing (92)
  • .github/workflows/release-pipeline.yml
  • .github/workflows/scripts/push-mintlify-changelog.sh
  • .github/workflows/scripts/release-bifrost-http.sh
  • .github/workflows/scripts/release-framework.sh
  • core/bifrost.go
  • core/bifrost_test.go
  • core/changelog.md
  • core/go.mod
  • core/mcp.go
  • core/providers/utils/utils.go
  • core/schemas/bifrost.go
  • core/schemas/context.go
  • core/schemas/plugin.go
  • core/schemas/trace.go
  • core/schemas/tracer.go
  • core/version
  • docs/docs.json
  • docs/plugins/getting-started.mdx
  • docs/plugins/migration-guide.mdx
  • docs/plugins/writing-plugin.mdx
  • examples/plugins/hello-world/go.mod
  • examples/plugins/hello-world/main.go
  • framework/changelog.md
  • framework/configstore/tables/mcp.go
  • framework/plugins/dynamicplugin.go
  • framework/plugins/dynamicplugin_test.go
  • framework/streaming/accumulator.go
  • framework/streaming/audio.go
  • framework/streaming/chat.go
  • framework/streaming/responses.go
  • framework/streaming/transcription.go
  • framework/streaming/types.go
  • framework/tracing/helpers.go
  • framework/tracing/llmspan.go
  • framework/tracing/propagation.go
  • framework/tracing/store.go
  • framework/tracing/tracer.go
  • framework/version
  • plugins/governance/changelog.md
  • plugins/governance/go.mod
  • plugins/governance/main.go
  • plugins/jsonparser/changelog.md
  • plugins/jsonparser/main.go
  • plugins/logging/changelog.md
  • plugins/logging/main.go
  • plugins/logging/utils.go
  • plugins/maxim/changelog.md
  • plugins/maxim/main.go
  • plugins/mocker/changelog.md
  • plugins/mocker/main.go
  • plugins/otel/changelog.md
  • plugins/otel/converter.go
  • plugins/otel/main.go
  • plugins/otel/ttlsyncmap.go
  • plugins/semanticcache/changelog.md
  • plugins/semanticcache/main.go
  • plugins/telemetry/changelog.md
  • plugins/telemetry/main.go
  • transports/bifrost-http/handlers/cache.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/devpprof.go
  • transports/bifrost-http/handlers/devpprof_unix.go
  • transports/bifrost-http/handlers/devpprof_windows.go
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/handlers/health.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/handlers/logging.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/handlers/mcpserver.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • transports/bifrost-http/handlers/plugins.go
  • transports/bifrost-http/handlers/providers.go
  • transports/bifrost-http/handlers/session.go
  • transports/bifrost-http/handlers/ui.go
  • transports/bifrost-http/handlers/websocket.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/lib/config_test.go
  • transports/bifrost-http/lib/middleware.go
  • transports/bifrost-http/server/server.go
  • transports/changelog.md
  • transports/go.mod
  • transports/version
  • ui/app/clientLayout.tsx
  • ui/app/workspace/logs/views/logDetailsSheet.tsx
  • ui/components/devProfiler.tsx
  • ui/components/sidebar.tsx
  • ui/components/ui/switch.tsx
  • ui/lib/store/apis/devApi.ts
  • ui/lib/store/apis/index.ts
💤 Files with no reviewable changes (3)
  • core/mcp.go
  • plugins/otel/ttlsyncmap.go
  • framework/configstore/tables/mcp.go
🚧 Files skipped from review as they are similar to previous changes (39)
  • framework/version
  • core/go.mod
  • core/bifrost_test.go
  • plugins/telemetry/main.go
  • core/schemas/context.go
  • framework/plugins/dynamicplugin_test.go
  • transports/bifrost-http/handlers/governance.go
  • framework/streaming/chat.go
  • transports/go.mod
  • transports/bifrost-http/handlers/devpprof_windows.go
  • .github/workflows/scripts/release-bifrost-http.sh
  • framework/streaming/types.go
  • plugins/maxim/changelog.md
  • transports/bifrost-http/handlers/cache.go
  • framework/streaming/transcription.go
  • transports/bifrost-http/handlers/plugins.go
  • plugins/mocker/main.go
  • transports/bifrost-http/handlers/session.go
  • core/schemas/plugin.go
  • transports/bifrost-http/handlers/devpprof_unix.go
  • docs/docs.json
  • plugins/governance/go.mod
  • core/version
  • plugins/semanticcache/main.go
  • transports/version
  • plugins/jsonparser/changelog.md
  • framework/changelog.md
  • .github/workflows/scripts/release-framework.sh
  • plugins/otel/changelog.md
  • ui/app/workspace/logs/views/logDetailsSheet.tsx
  • .github/workflows/release-pipeline.yml
  • transports/bifrost-http/lib/config_test.go
  • transports/bifrost-http/handlers/logging.go
  • plugins/mocker/changelog.md
  • transports/bifrost-http/handlers/ui.go
  • core/schemas/trace.go
  • transports/bifrost-http/handlers/mcpserver.go
  • framework/streaming/responses.go
  • examples/plugins/hello-world/main.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:

  • ui/components/sidebar.tsx
  • transports/bifrost-http/handlers/inference.go
  • ui/components/devProfiler.tsx
  • transports/bifrost-http/handlers/mcp.go
  • ui/components/ui/switch.tsx
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • core/changelog.md
  • core/providers/utils/utils.go
  • transports/changelog.md
  • framework/streaming/accumulator.go
  • framework/tracing/propagation.go
  • transports/bifrost-http/handlers/health.go
  • framework/plugins/dynamicplugin.go
  • ui/lib/store/apis/index.ts
  • plugins/logging/utils.go
  • transports/bifrost-http/handlers/config.go
  • plugins/governance/changelog.md
  • transports/bifrost-http/integrations/router.go
  • plugins/otel/main.go
  • transports/bifrost-http/handlers/devpprof.go
  • core/schemas/bifrost.go
  • framework/streaming/audio.go
  • framework/tracing/tracer.go
  • examples/plugins/hello-world/go.mod
  • transports/bifrost-http/handlers/providers.go
  • transports/bifrost-http/lib/middleware.go
  • framework/tracing/store.go
  • plugins/otel/converter.go
  • docs/plugins/getting-started.mdx
  • plugins/semanticcache/changelog.md
  • plugins/governance/main.go
  • framework/tracing/helpers.go
  • ui/app/clientLayout.tsx
  • docs/plugins/migration-guide.mdx
  • ui/lib/store/apis/devApi.ts
  • plugins/maxim/main.go
  • plugins/logging/main.go
  • core/bifrost.go
  • docs/plugins/writing-plugin.mdx
  • transports/bifrost-http/handlers/middlewares.go
  • plugins/telemetry/changelog.md
  • core/schemas/tracer.go
  • transports/bifrost-http/server/server.go
  • transports/bifrost-http/handlers/websocket.go
  • transports/bifrost-http/lib/config.go
  • plugins/logging/changelog.md
  • plugins/jsonparser/main.go
  • framework/tracing/llmspan.go
🧠 Learnings (6)
📚 Learning: 2025-12-09T17:07:42.007Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/schemas/account.go:9-18
Timestamp: 2025-12-09T17:07:42.007Z
Learning: In core/schemas/account.go, the HuggingFaceKeyConfig field within the Key struct is currently unused and reserved for future Hugging Face inference endpoint deployments. Do not flag this field as missing from OpenAPI documentation or require its presence in the API spec until the feature is actively implemented and used. When the feature is added, update the OpenAPI docs accordingly; otherwise, treat this field as non-breaking and not part of the current API surface.

Applied to files:

  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • core/providers/utils/utils.go
  • framework/streaming/accumulator.go
  • framework/tracing/propagation.go
  • transports/bifrost-http/handlers/health.go
  • framework/plugins/dynamicplugin.go
  • plugins/logging/utils.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/integrations/router.go
  • plugins/otel/main.go
  • transports/bifrost-http/handlers/devpprof.go
  • core/schemas/bifrost.go
  • framework/streaming/audio.go
  • framework/tracing/tracer.go
  • transports/bifrost-http/handlers/providers.go
  • transports/bifrost-http/lib/middleware.go
  • framework/tracing/store.go
  • plugins/otel/converter.go
  • plugins/governance/main.go
  • framework/tracing/helpers.go
  • plugins/maxim/main.go
  • plugins/logging/main.go
  • core/bifrost.go
  • transports/bifrost-http/handlers/middlewares.go
  • core/schemas/tracer.go
  • transports/bifrost-http/server/server.go
  • transports/bifrost-http/handlers/websocket.go
  • transports/bifrost-http/lib/config.go
  • plugins/jsonparser/main.go
  • framework/tracing/llmspan.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
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • transports/bifrost-http/handlers/health.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/handlers/devpprof.go
  • transports/bifrost-http/handlers/providers.go
  • transports/bifrost-http/lib/middleware.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/server/server.go
  • transports/bifrost-http/handlers/websocket.go
  • transports/bifrost-http/lib/config.go
📚 Learning: 2025-12-29T09:14:16.633Z
Learnt from: akshaydeo
Repo: maximhq/bifrost PR: 888
File: transports/bifrost-http/handlers/middlewares.go:246-256
Timestamp: 2025-12-29T09:14:16.633Z
Learning: In the bifrost HTTP transport, fasthttp.RequestCtx is the primary context carrier and should be passed directly to functions that expect a context.Context. Do not convert to context.Context unless explicitly required. Ensure tracer implementations and related components are designed to accept fasthttp.RequestCtx directly, and document this architectural decision for maintainers.

Applied to files:

  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • transports/bifrost-http/handlers/health.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/handlers/devpprof.go
  • transports/bifrost-http/handlers/providers.go
  • transports/bifrost-http/lib/middleware.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/server/server.go
  • transports/bifrost-http/handlers/websocket.go
  • transports/bifrost-http/lib/config.go
📚 Learning: 2025-12-19T09:26:54.961Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/providers/utils/utils.go:1050-1051
Timestamp: 2025-12-19T09:26:54.961Z
Learning: Update streaming end-marker handling so HuggingFace is treated as a non-[DONE] provider for backends that do not emit a DONE marker (e.g., meta llama on novita). In core/providers/utils/utils.go, adjust ProviderSendsDoneMarker() (or related logic) to detect providers that may not emit DONE and avoid relying on DONE as the sole end signal. Add tests to cover both DONE-emitting and non-DONE backends, with clear documentation in code comments explaining the rationale and any fallback behavior.

Applied to files:

  • core/providers/utils/utils.go
📚 Learning: 2025-12-24T10:55:31.424Z
Learnt from: akshaydeo
Repo: maximhq/bifrost PR: 888
File: framework/tracing/propagation.go:24-36
Timestamp: 2025-12-24T10:55:31.424Z
Learning: In framework/tracing/propagation.go, ensure ExtractParentID() returns ctx.TraceID (not ctx.ParentID) because the parent request's traceparent header carries the trace ID used for continuing the trace.

Applied to files:

  • framework/tracing/propagation.go
📚 Learning: 2025-12-22T10:50:40.990Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1154
File: plugins/governance/store.go:1165-1186
Timestamp: 2025-12-22T10:50:40.990Z
Learning: In the Bifrost governance plugin, budgets and rate limits have 1:1 relationships with their parent entities (virtual keys, teams, customers). Do not assume sharing; ensure cascade deletion logic only deletes budgets/rate limits when there are no shared references. Enforce invariants in code and add tests to verify no cross-entity sharing and that cascade deletes only remove the specific child of the parent. If a counterexample arises, adjust data model or add guards.

Applied to files:

  • plugins/governance/main.go
🧬 Code graph analysis (29)
transports/bifrost-http/handlers/inference.go (2)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
core/schemas/bifrost.go (2)
  • BifrostContextKeyDeferTraceCompletion (148-148)
  • BifrostContextKeyTraceCompleter (149-149)
ui/components/devProfiler.tsx (1)
ui/lib/utils/port.ts (1)
  • isDevelopmentMode (110-112)
transports/bifrost-http/handlers/mcp.go (2)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/lib/ctx.go (1)
  • ConvertToBifrostContext (80-409)
transports/bifrost-http/handlers/integrations.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/middlewares_test.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
framework/streaming/accumulator.go (1)
framework/streaming/types.go (3)
  • StreamAccumulator (103-115)
  • ChatStreamChunk (77-87)
  • ResponsesStreamChunk (90-100)
transports/bifrost-http/handlers/health.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
framework/plugins/dynamicplugin.go (2)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
examples/plugins/hello-world/main.go (2)
  • GetName (15-17)
  • HTTPTransportMiddleware (19-27)
plugins/logging/utils.go (4)
core/schemas/tracer.go (1)
  • StreamAccumulatorResult (15-33)
core/schemas/bifrost.go (6)
  • RequestType (89-89)
  • TextCompletionStreamRequest (94-94)
  • ChatCompletionStreamRequest (96-96)
  • ResponsesStreamRequest (98-98)
  • SpeechStreamRequest (101-101)
  • TranscriptionStreamRequest (103-103)
framework/streaming/types.go (11)
  • ProcessedStreamResponse (118-126)
  • StreamType (10-10)
  • StreamTypeText (13-13)
  • StreamTypeChat (14-14)
  • StreamTypeResponses (17-17)
  • StreamTypeAudio (15-15)
  • StreamTypeTranscription (16-16)
  • StreamResponseType (20-20)
  • StreamResponseTypeFinal (24-24)
  • StreamResponseTypeDelta (23-23)
  • AccumulatedData (28-48)
core/schemas/chatcompletions.go (1)
  • ChatAssistantMessage (659-666)
transports/bifrost-http/handlers/config.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/integrations/router.go (2)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
core/schemas/bifrost.go (2)
  • BifrostContextKeyDeferTraceCompletion (148-148)
  • BifrostContextKeyTraceCompleter (149-149)
plugins/otel/main.go (3)
core/schemas/plugin.go (2)
  • BifrostHTTPMiddleware (38-38)
  • ObservabilityPlugin (123-137)
core/schemas/context.go (1)
  • BifrostContext (32-42)
core/schemas/trace.go (1)
  • Trace (10-19)
transports/bifrost-http/handlers/devpprof.go (3)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/lib/middleware.go (1)
  • ChainMiddlewares (11-23)
transports/bifrost-http/handlers/utils.go (1)
  • SendJSON (16-22)
core/schemas/bifrost.go (2)
framework/tracing/tracer.go (1)
  • Tracer (17-20)
core/schemas/tracer.go (1)
  • Tracer (38-116)
framework/streaming/audio.go (1)
framework/streaming/types.go (1)
  • AccumulatedData (28-48)
framework/tracing/tracer.go (5)
framework/tracing/store.go (1)
  • TraceStore (25-37)
framework/streaming/accumulator.go (2)
  • Accumulator (14-30)
  • NewAccumulator (440-480)
core/schemas/tracer.go (3)
  • Tracer (38-116)
  • SpanHandle (11-11)
  • StreamAccumulatorResult (15-33)
framework/tracing/helpers.go (3)
  • GetTraceID (11-20)
  • EndSpan (50-56)
  • GetTrace (23-29)
framework/tracing/llmspan.go (1)
  • PopulateRequestAttributes (14-60)
transports/bifrost-http/handlers/providers.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/lib/middleware.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
framework/tracing/store.go (3)
framework/tracing/tracer.go (1)
  • Tracer (17-20)
core/schemas/trace.go (6)
  • Trace (10-19)
  • Span (52-65)
  • SpanEvent (107-111)
  • SpanKind (115-115)
  • SpanStatusUnset (147-147)
  • SpanStatus (143-143)
framework/tracing/helpers.go (3)
  • GetTrace (23-29)
  • AddSpan (32-38)
  • EndSpan (50-56)
plugins/otel/converter.go (2)
plugins/otel/main.go (1)
  • OtelPlugin (58-75)
core/schemas/trace.go (17)
  • Trace (10-19)
  • Span (52-65)
  • SpanKind (115-115)
  • SpanKindLLMCall (121-121)
  • SpanKindHTTPRequest (131-131)
  • SpanKindPlugin (123-123)
  • SpanKindInternal (139-139)
  • SpanKindRetry (127-127)
  • SpanKindFallback (129-129)
  • SpanKindMCPTool (125-125)
  • SpanKindEmbedding (133-133)
  • SpanKindSpeech (135-135)
  • SpanKindTranscription (137-137)
  • SpanStatus (143-143)
  • SpanStatusOk (149-149)
  • SpanStatusError (151-151)
  • SpanEvent (107-111)
plugins/governance/main.go (3)
core/utils.go (1)
  • Ptr (56-58)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-27)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
framework/tracing/helpers.go (3)
core/schemas/bifrost.go (1)
  • BifrostContextKeyTraceID (144-144)
framework/tracing/store.go (1)
  • TraceStore (25-37)
core/schemas/trace.go (5)
  • Trace (10-19)
  • SpanKind (115-115)
  • Span (52-65)
  • SpanStatus (143-143)
  • SpanEvent (107-111)
ui/app/clientLayout.tsx (1)
ui/components/devProfiler.tsx (1)
  • DevProfiler (55-422)
ui/lib/store/apis/devApi.ts (2)
transports/bifrost-http/handlers/devpprof.go (6)
  • MemoryStats (29-35)
  • CPUStats (38-42)
  • RuntimeStats (45-51)
  • AllocationInfo (54-60)
  • HistoryPoint (63-70)
  • PprofData (73-80)
ui/lib/store/apis/index.ts (1)
  • baseApi (2-2)
plugins/maxim/main.go (2)
core/schemas/plugin.go (2)
  • Plugin (71-97)
  • BifrostHTTPMiddleware (38-38)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-27)
core/bifrost.go (7)
framework/tracing/tracer.go (1)
  • Tracer (17-20)
core/schemas/tracer.go (3)
  • Tracer (38-116)
  • DefaultTracer (181-183)
  • SpanHandle (11-11)
core/schemas/bifrost.go (10)
  • ListModelsRequest (92-92)
  • BifrostRequest (175-196)
  • BifrostContextKeyTracer (147-147)
  • BifrostContextKeyStreamStartTime (146-146)
  • BifrostStream (455-462)
  • BifrostContextKeyTraceID (144-144)
  • BifrostResponse (348-369)
  • RequestType (89-89)
  • BifrostContextKeyPostHookSpanFinalizer (150-150)
  • BifrostError (490-499)
core/schemas/trace.go (10)
  • SpanKindFallback (129-129)
  • AttrProviderName (159-159)
  • AttrRequestModel (160-160)
  • SpanStatusError (151-151)
  • SpanStatusOk (149-149)
  • SpanKind (115-115)
  • SpanKindRetry (127-127)
  • SpanKindLLMCall (121-121)
  • SpanKindInternal (139-139)
  • SpanKindPlugin (123-123)
framework/tracing/helpers.go (1)
  • EndSpan (50-56)
core/utils.go (1)
  • IsStreamRequestType (197-199)
core/schemas/context.go (1)
  • NewBifrostContextWithTimeout (70-73)
transports/bifrost-http/handlers/middlewares.go (7)
core/schemas/plugin.go (2)
  • BifrostHTTPMiddleware (38-38)
  • Plugin (71-97)
transports/bifrost-http/lib/middleware.go (1)
  • ChainMiddlewares (11-23)
framework/configstore/store.go (1)
  • ConfigStore (18-162)
core/schemas/tracer.go (1)
  • Tracer (38-116)
framework/tracing/propagation.go (1)
  • ExtractParentID (74-84)
core/schemas/bifrost.go (4)
  • BifrostContextKeyTraceID (144-144)
  • BifrostContextKeyTraceCompleter (149-149)
  • BifrostContextKeySpanID (145-145)
  • BifrostContextKeyDeferTraceCompletion (148-148)
core/schemas/trace.go (3)
  • SpanKindHTTPRequest (131-131)
  • SpanStatusError (151-151)
  • SpanStatusOk (149-149)
core/schemas/tracer.go (8)
core/schemas/models.go (1)
  • Model (109-129)
core/schemas/bifrost.go (4)
  • ModelProvider (33-33)
  • BifrostError (490-499)
  • BifrostRequest (175-196)
  • BifrostResponse (348-369)
core/schemas/chatcompletions.go (1)
  • BifrostLLMUsage (855-862)
core/schemas/speech.go (1)
  • BifrostSpeechResponse (22-29)
core/schemas/transcriptions.go (1)
  • BifrostTranscriptionResponse (16-26)
core/schemas/trace.go (3)
  • Trace (10-19)
  • SpanKind (115-115)
  • SpanStatus (143-143)
core/schemas/context.go (1)
  • BifrostContext (32-42)
framework/tracing/tracer.go (1)
  • Tracer (17-20)
plugins/jsonparser/main.go (2)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-27)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
🪛 markdownlint-cli2 (0.18.1)
transports/changelog.md

15-15: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

Comment thread docs/plugins/migration-guide.mdx
Comment thread framework/tracing/tracer.go
@akshaydeo akshaydeo force-pushed the 11-19-plugins_v2_architecture branch from 5456aad to d3b125f Compare December 29, 2025 10:17
Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 29, 2025

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown
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

Caution

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

⚠️ Outside diff range comments (2)
plugins/governance/main.go (1)

42-49: BaseGovernancePlugin still exposes TransportInterceptor while implementation has moved to HTTPTransportMiddleware

BaseGovernancePlugin continues to require a TransportInterceptor method, but GovernancePlugin now implements HTTPTransportMiddleware instead and no longer provides a TransportInterceptor in this file. That mismatch can be confusing for callers and makes the “v2” plugin architecture look inconsistent.

Unless something else still relies on the old interface, consider updating BaseGovernancePlugin to:

  • drop TransportInterceptor from its contract, and
  • (optionally) add HTTPTransportMiddleware() schemas.BifrostHTTPMiddleware if you want this interface to stay aligned with schemas.Plugin.

If external code is still compiling against BaseGovernancePlugin, you can either add a small compatibility shim on GovernancePlugin or migrate those call sites to the new middleware pattern.

Also applies to: 263-315

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

204-213: Return a shallow copy of the Providers map instead of the live reference.

GovernanceInMemoryStore.GetConfiguredProviders() releases s.Config.Mu.RLock() before returning, but the caller then accesses the map reference without holding any lock. Meanwhile, Config.AddProvider(), Config.UpdateProviderConfig(), and Config.RemoveProvider() can mutate the map at runtime (they each acquire Mu.Lock() and modify c.Providers). This creates a data race: concurrent map mutations from one goroutine and map access from another will panic under Go's concurrency rules.

Iterate under the lock and return a shallow copy: this ensures callers have a stable snapshot that won't race with concurrent mutations.

♻️ Duplicate comments (15)
ui/components/ui/switch.tsx (1)

13-29: Verify this file belongs in this PR.

The changes to this Switch component (default size and border radius modifications) appear unrelated to the stated PR objectives, which focus on replacing TransportInterceptor with HTTPTransportMiddleware for plugin architecture. These UI changes:

  1. Change the default size from "default" to "md" (making switches smaller)
  2. Change border radius from rounded-full to rounded-sm (altering the visual appearance)

Both changes were flagged as breaking in previous review comments and affect all Switch instances across the codebase.

Given the PR's backend focus, please confirm whether:

  • This file was accidentally included from uncommitted work or a different branch
  • These UI changes are part of a parallel effort that should be in a separate PR
  • There's an intentional design system update not mentioned in the PR description

I can search the PR stack (#1170, #1169, #1121, #1120, #1119) to verify if there are related UI changes that explain this file's inclusion:

#!/bin/bash
# Description: Search for related Switch component or UI changes in the codebase and recent commits

echo "=== Searching for Switch component references ==="
rg -g '!node_modules' --type-add 'tsx:*.tsx' --type-add 'ts:*.ts' -t tsx -t ts -C2 'Switch.*size.*=|import.*Switch'

echo -e "\n=== Checking git history for UI-related changes ==="
git log --oneline --all --grep="UI\|design\|switch\|component" -20 2>/dev/null || echo "Git log not available"

echo -e "\n=== Looking for design system or UI documentation updates ==="
fd -e md -e txt -e mdx . | xargs rg -l "switch\|design.system\|component.library" 2>/dev/null | head -20

echo -e "\n=== Checking for other UI component changes in recent commits ==="
git diff --name-only HEAD~10 HEAD 2>/dev/null | grep -E 'ui/components|design' | head -20
ui/app/workspace/logs/views/logDetailsSheet.tsx (1)

184-186: Add aria-label for accessibility.

The icon-only button still lacks an aria-label to provide context for screen reader users. This was flagged in a previous review and remains unaddressed.

🔎 Suggested fix
 <DropdownMenuTrigger asChild>
-  <Button variant="ghost" size="icon">
+  <Button variant="ghost" size="icon" aria-label="Log actions">
     <MoreVertical className="h-3 w-3" />
   </Button>
 </DropdownMenuTrigger>
framework/version (1)

1-1: Past review concern remains unaddressed: Major version bump required.

The previous review flagged that this breaking API change (Plugin interface replacing TransportInterceptor() with HTTPTransportMiddleware()) requires a major version bump to 2.0.0, not the minor bump to 1.2.0 shown here. Breaking changes with incompatible signatures and semantics warrant a major version per semantic versioning.

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

880-931: Streaming span completion logic is solid, but context handling and chunk accumulation could be cleaned up

  • The changes to ProcessAndSendResponse / ProcessAndSendBifrostError correctly:
    • Accumulate streaming chunks under the active trace ID.
    • Run post‑hooks before sending on the stream channel.
    • Complete the deferred LLM span on the final chunk, including the SkipStream path, which fixes the “no span completion when a plugin short‑circuits” case.

Two follow‑ups worth addressing:

  1. Use post‑processed chunks for accumulation to match span data with what the client sees

    Right now you call:

    tracer.AddStreamingChunk(traceID, response)
    processedResponse, processedError := postHookRunner(&ctx, response, nil)
    ...
    accumulatedResp, ttftMs, chunkCount := tracer.GetAccumulatedChunks(traceID)
    tracer.PopulateLLMResponseAttributes(handle, accumulatedResp, err)

    So the accumulated response used for PopulateLLMResponseAttributes is built from the pre–post‑hook response, while completeDeferredSpan receives processedResponse/processedError. If you want the LLM span to reflect post‑hook transformations (what the client actually saw), consider accumulating processedResponse instead (or in addition), e.g. move AddStreamingChunk after postHookRunner and feed it the processed value.

  2. Pass context.Context by value in completeDeferredSpan (repeat from earlier review)

    completeDeferredSpan still uses ctx *context.Context and all call sites pass &ctx. Go conventionally passes context.Context by value; it’s already safe for concurrent use and designed to be immutable from the caller’s perspective. The pointer indirection complicates call sites and makes nil‑handling more fragile than needed.

    Refactoring to:

    func completeDeferredSpan(ctx context.Context, result *schemas.BifrostResponse, err *schemas.BifrostError) { ... }

    and updating call sites to pass ctx directly would simplify the API and align with Go idioms without changing behavior.

Also applies to: 938-981, 1127-1134, 1423-1513

framework/tracing/llmspan.go (1)

190-193: ExtraParams key collision issue remains unaddressed.

User-provided keys from ExtraParams are written directly to the attributes map without namespacing. This pattern repeats throughout the file (lines 298-300, 365-367, 575-577, 708-710, 738-740, 753-755, 768-770, 783-785, 958-960, 982-984, 997-999, 1012-1014, 1027-1029). If a user sets a key like "gen_ai.request.model" in ExtraParams, it would overwrite standard attributes.

transports/changelog.md (2)

15-15: Add language specifier to fenced code block.

The code block lacks a language identifier, preventing syntax highlighting and violating MD040.

🔎 Suggested fix
   **Migration summary:**
-  ```
+  ```go
   // v1.3.x (removed)
   TransportInterceptor(ctx *BifrostContext, url string, headers map[string]string, body map[string]any) (map[string]string, map[string]any, error)

15-22: Fix version inconsistency with core changelog.

The migration summary shows the new API as "v1.4.x+ (new)" but core/changelog.md states the new API is available in "core v1.3.0+". This inconsistency could confuse developers about which versions to use.

Update the version references to align with the core changelog, changing "v1.4.x+" to "v1.3.0+" to ensure consistent guidance.

🔎 Suggested fix
   **Migration summary:**

// v1.3.x (removed)
TransportInterceptor(ctx *BifrostContext, url string, headers map[string]string, body map[string]any) (map[string]string, map[string]any, error)

  • // v1.4.x+ (new)
  • // v1.3.0+ (new)
    HTTPTransportMiddleware() BifrostHTTPMiddleware
    // where BifrostHTTPMiddleware = func(next fasthttp.RequestHandler) fasthttp.RequestHandler
</details>

Based on learnings, ...

</blockquote></details>
<details>
<summary>docs/plugins/migration-guide.mdx (1)</summary><blockquote>

`247-250`: **v1.4.0 tag does not exist yet.**

This issue was flagged in a previous review. The `go get` command references `@v1.4.0` which hasn't been published. Consider adding a note that users should use the latest available version or wait for the v1.4.0 release.

</blockquote></details>
<details>
<summary>framework/plugins/dynamicplugin.go (1)</summary><blockquote>

`140-147`: **Fix error message to match the actual expected type.**

The error message on line 146 is inconsistent with the actual type being validated. The code casts to `func() schemas.BifrostHTTPMiddleware`, but the error message states `func() fasthttp.RequestHandler`.


<details>
<summary>🔎 Suggested fix</summary>

```diff
 	if dp.httpTransportMiddleware, ok = httpTransportMiddlewareSym.(func() schemas.BifrostHTTPMiddleware); !ok {
-		return nil, fmt.Errorf("failed to cast HTTPTransportMiddleware to func() fasthttp.RequestHandler")
+		return nil, fmt.Errorf("failed to cast HTTPTransportMiddleware to func() schemas.BifrostHTTPMiddleware")
 	}
plugins/governance/main.go (2)

233-261: Virtual key extraction from x-bf-vk is more permissive than other headers

parseVirtualKey:

  • returns x-bf-vk verbatim without checking VirtualKeyPrefix, while
  • Authorization, x-api-key, and x-goog-api-key values are all validated to start with VirtualKeyPrefix.

For consistency and to avoid accidentally treating unrelated header values as virtual keys, you may want to normalize and prefix‑check x-bf-vk the same way you do for the other headers (e.g., trim spaces, lower‑case for the prefix comparison, and only return when the prefix matches). If the intent is that x-bf-vk can carry non‑prefixed identifiers, it would be helpful to document that explicitly.


263-315: Minor fixes in governance HTTPTransportMiddleware: body check and log message wording

Two small issues in HTTPTransportMiddleware are worth addressing:

  1. Body presence check

    if ctx.Request.Body() == nil {
        next(ctx)
        return
    }

    fasthttp.Request.Body() typically returns a []byte, which may be non‑nil but zero‑length. Using len(ctx.Request.Body()) == 0 would avoid attempting to unmarshal an empty body and avoid noisy error logs.

  2. Unmarshal log message wording

    When sonic.Unmarshal fails, the log currently says:

    p.logger.Error("failed to marshal request body to check for virtual key: %v", err)

    This should say “unmarshal” to match the actual operation:

    p.logger.Error("failed to unmarshal request body to check for virtual key: %v", err)

Both changes are low‑risk quality-of-life improvements.

framework/tracing/store.go (1)

13-22: Remove unused Tracer and RequestID fields from DeferredSpanInfo.

As noted in a previous review, StoreDeferredSpan() at line 121 only initializes SpanID and StartTime. The Tracer and RequestID fields are never assigned or read anywhere in the codebase, adding unnecessary memory overhead.

🔎 Proposed fix
 // DeferredSpanInfo stores information about a deferred span for streaming requests
 type DeferredSpanInfo struct {
 	SpanID             string
 	StartTime          time.Time
-	Tracer             schemas.Tracer // Reference to tracer for completing the span
-	RequestID          string         // Request ID for accumulator lookup
 	FirstChunkTime     time.Time      // Timestamp of first chunk (for TTFT calculation)
 	AccumulatedChunks  []*schemas.BifrostResponse // Accumulated streaming chunks
 	mu                 sync.Mutex     // Mutex for thread-safe chunk accumulation
 }
plugins/otel/converter.go (1)

165-170: Potential data loss when casting large uint64 values to int64.

For uint64 values greater than math.MaxInt64 (9.2 quintillion), the cast to int64 will overflow and produce incorrect negative values. While uncommon in practice, this could affect certain metrics or identifiers.

🔎 Proposed fix with overflow handling
+import "math"
+
 case uint64:
+	if v > math.MaxInt64 {
+		// Overflow: cap at MaxInt64 to avoid negative values
+		return kvInt(key, math.MaxInt64)
+	}
 	return kvInt(key, int64(v))
core/schemas/trace.go (1)

9-19: Protect Trace.Reset, Span.End, and Span.Reset with the existing mutexes to avoid data races.

Trace and Span use mu to guard concurrent access in AddSpan, GetSpan, SetAttribute, and AddEvent, but:

  • Trace.Reset clears all fields and truncates Spans without taking t.mu.
  • Span.End mutates EndTime, Status, and StatusMsg without s.mu.
  • Span.Reset clears all fields and truncates Events without s.mu.

If traces/spans are reused from a pool or manipulated while other goroutines still hold references (e.g., via TraceStore), these unsynchronized writes can race with readers/writers that do hold the lock.

Align these methods with the other mutators by acquiring the mutex at the start and deferring unlock around all field resets/mutations.

Also applies to: 21-27, 28-38, 40-49, 51-65, 67-83, 84-90, 91-104

framework/tracing/tracer.go (1)

259-271: Sort map-derived indices in buildCompleteResponseFromChunks to make output deterministic.

buildCompleteResponseFromChunks builds:

  • choiceIndices from choiceMap keys (lines 395‑398), and
  • tcIndices from acc.toolCalls keys (lines 434‑437),

but never sorts these slices before iteration. Because Go map iteration order is randomized, the resulting Choices slice and each choice’s ToolCalls order will vary run‑to‑run. This can break tests and make logs/traces hard to diff.

You already have a comment about sorting choice indices; the missing piece is the actual sort.Ints(...) calls.

Consider:

+import "sort"
@@
 	// Sort choice indices for deterministic output
 	choiceIndices := make([]int, 0, len(choiceMap))
 	for idx := range choiceMap {
 		choiceIndices = append(choiceIndices, idx)
 	}
+	sort.Ints(choiceIndices)
@@
 			if len(accum.toolCalls) > 0 {
 				// Sort tool calls by index
 				tcIndices := make([]int, 0, len(accum.toolCalls))
 				for tcIdx := range accum.toolCalls {
 					tcIndices = append(tcIndices, tcIdx)
 				}
+				sort.Ints(tcIndices)

Also applies to: 273-291, 293-401, 433-443

🧹 Nitpick comments (12)
transports/bifrost-http/handlers/devpprof_unix.go (1)

11-26: Approve implementation with observability suggestion.

The CPU sampling implementation is correct:

  • Build tags properly exclude Windows
  • Time conversion from rusage.Utime/Stime.{Sec,Usec} to time.Duration is accurate
  • Error fallback matches Windows behavior (timestamp-only)

Consider logging when Getrusage fails to aid debugging in development environments.

🔎 Optional: Add error logging for failed CPU sampling
 func getCPUSample() cpuSample {
 	var rusage syscall.Rusage
 	if err := syscall.Getrusage(syscall.RUSAGE_SELF, &rusage); err != nil {
+		// Consider logging: log.Warn("failed to get CPU rusage", "error", err)
 		return cpuSample{timestamp: time.Now()}
 	}
core/utils.go (1)

400-403: Consider more comprehensive span name sanitization.

The current implementation only handles spaces and uppercase letters. Tracing systems typically have stricter requirements for span names, and inputs might contain other problematic characters such as tabs, newlines, control characters, or special symbols that could result in invalid span names.

🔎 Suggested enhancement for more robust sanitization
-// sanitizeSpanName sanitizes a span name to remove capital letters and spaces to make it a valid span name
+// sanitizeSpanName sanitizes a span name by normalizing whitespace, removing invalid characters,
+// and converting to lowercase to ensure compatibility with tracing systems
 func sanitizeSpanName(name string) string {
-	return strings.ToLower(strings.ReplaceAll(name, " ", "-"))
+	// Replace all whitespace (spaces, tabs, newlines) with hyphens
+	name = strings.Map(func(r rune) rune {
+		if unicode.IsSpace(r) {
+			return '-'
+		}
+		// Allow alphanumerics, hyphens, underscores, and dots
+		if unicode.IsLetter(r) || unicode.IsNumber(r) || r == '-' || r == '_' || r == '.' {
+			return r
+		}
+		// Replace other characters with hyphens
+		return '-'
+	}, name)
+	// Convert to lowercase and trim leading/trailing hyphens
+	name = strings.ToLower(name)
+	name = strings.Trim(name, "-")
+	// Collapse multiple consecutive hyphens
+	for strings.Contains(name, "--") {
+		name = strings.ReplaceAll(name, "--", "-")
+	}
+	return name
 }

Note: You'll need to add "unicode" to the imports if this approach is adopted.

.github/workflows/release-pipeline.yml (1)

9-11: Consider whether branch-specific concurrency groups are needed.

The static release-pipeline concurrency group means releases from main and v1.4.0 will queue behind each other. If these branches should release independently, consider using a branch-specific group:

 concurrency:
-  group: release-pipeline
+  group: release-pipeline-${{ github.ref_name }}
   cancel-in-progress: false

However, if the intent is to serialize all releases regardless of branch (to prevent conflicts in shared resources like Docker registries), the current configuration is correct.

ui/app/workspace/logs/views/logDetailsSheet.tsx (1)

50-164: Consider refactoring the long copyRequestBody function.

The copyRequestBody function is 114 lines long and handles multiple responsibilities. While the current implementation is correct, consider refactoring for improved maintainability:

  1. Extract helper functions (extractTextFromMessage, extractTextsFromMessage) to module level
  2. Create separate functions for each request type (chat, responses, speech, etc.)
  3. Extract request body building logic into smaller, composable functions

This would make the code easier to test and maintain.

ui/components/devProfiler.tsx (1)

139-144: Consider using a distinct icon for the minimize action.

The minimize button (line 144) uses ChevronDown, which is the same icon used for the expanded state toggle (line 134). This could cause confusion about which action each button performs.

🔎 Suggested icon alternatives

Consider using Minimize2 or ChevronsDown from lucide-react for the minimize action to differentiate it from expand/collapse:

 import { Activity, ChevronDown, ChevronUp, Cpu, HardDrive, X } from 'lucide-react'
+import { Minimize2 } from 'lucide-react'

Then update the minimize button:

           <button
             onClick={handleToggleVisible}
             className="rounded p-1 transition-colors hover:bg-zinc-700"
             title="Minimize"
           >
-            <ChevronDown className="h-4 w-4" />
+            <Minimize2 className="h-4 w-4" />
           </button>
framework/streaming/responses.go (1)

897-906: Missing TTFT for OpenAI-compatible provider path.

The AccumulatedData constructed for OpenAI-compatible providers (OpenAI, OpenRouter, Azure non-Anthropic) at line 897 does not include TimeToFirstToken. This creates an inconsistency where TTFT is available for non-OpenAI providers but not for OpenAI.

Consider adding TTFT calculation for consistency:

🔎 Suggested approach
 					data := &AccumulatedData{
 						RequestID:      requestID,
 						Status:         "success",
 						Stream:         true,
 						StartTimestamp: startTimestamp,
 						EndTimestamp:   endTimestamp,
 						Latency:        result.GetExtraFields().Latency,
+						TimeToFirstToken: accumulatedData.TimeToFirstToken,
 						ErrorDetails:   bifrostErr,
 						RawResponse:    accumulatedData.RawResponse,
 					}
transports/bifrost-http/lib/middleware.go (1)

11-23: Consider adding nil middleware handling.

The function iterates over middlewares without checking for nil values. If a plugin's HTTPTransportMiddleware() returns nil (as seen in plugins/jsonparser/main.go), line 20 will panic when attempting to call middlewares[i](chained).

🔎 Proposed fix: Skip nil middlewares
 func ChainMiddlewares(handler fasthttp.RequestHandler, middlewares ...schemas.BifrostHTTPMiddleware) fasthttp.RequestHandler {
 	// If no middlewares, return the original handler
 	if len(middlewares) == 0 {
 		return handler
 	}
 	// Build the chain from right to left (last middleware wraps the handler)
 	// This ensures execution order is left to right (first middleware executes first)
 	chained := handler
 	for i := len(middlewares) - 1; i >= 0; i-- {
+		if middlewares[i] == nil {
+			continue
+		}
 		chained = middlewares[i](chained)
 	}
 	return chained
 }
framework/plugins/dynamicplugin_test.go (1)

72-83: Consider verifying context value propagation in the middleware test.

The hello-world plugin's HTTPTransportMiddleware sets a user value on the context (ctx.SetUserValue(...)). Consider adding an assertion to verify this value is accessible after the middleware executes, which would validate the full data flow.

🔎 Optional enhancement
 		// Call the wrapped handler
 		wrappedHandler(ctx)
 
 		// Verify the next handler was called
 		assert.True(t, nextHandlerCalled, "Next handler should have been called")
+
+		// Verify context value was set by middleware
+		value := ctx.UserValue(schemas.BifrostContextKey("hello-world-plugin-transport-interceptor"))
+		assert.Equal(t, "transport-interceptor-value", value, "Middleware should set context value")
 	})
framework/streaming/accumulator.go (1)

104-115: TTFT timestamp handling looks correct; StartTimestamp fallback could be simplified

The combination of:

  • createStreamAccumulator setting Timestamp and StartTimestamp to time.Now(),
  • CreateStreamAccumulator explicitly overriding StartTimestamp under the mutex, and
  • FirstChunkTimestamp being set on the first chunk in each add*StreamChunk path

gives a coherent model for TTFT/latency calculations.

Given createStreamAccumulator now always initializes StartTimestamp, the if accumulator.StartTimestamp.IsZero() branches in the add*StreamChunk helpers are effectively a defensive fallback for non-standard creation paths. If there are no such paths, you could remove those checks to reduce mental overhead, but the current code is functionally sound.

Also applies to: 129-223, 384-392

framework/changelog.md (1)

1-25: Tighten wording and optionally add one sentence on tracing framework behavior

The headline

feat: adds new tracing framework for allowing plugins to enable e2e tracing

reads a bit awkwardly. Consider something like:

  • “feat: add tracing framework to enable end‑to‑end tracing from plugins”

Also, you might add a short follow‑up sentence (before the BREAKING CHANGES header) summarizing what the framework does (e.g., that plugins can emit traces/spans via the new observability interfaces), to match the level of detail given for the HTTPTransportMiddleware change.

plugins/logging/main.go (1)

436-438: Consider logging at warn level when tracer/traceID is unavailable for streaming.

The debug log silently handles missing tracer or traceID, but this condition during streaming could indicate a configuration issue. Consider using Warn level to surface potential misconfigurations earlier.

🔎 Proposed change
 			if streamResponse == nil {
-				p.logger.Debug("failed to process streaming response: tracer or traceID not available")
+				p.logger.Warn("failed to process streaming response: tracer or traceID not available")
 			} else if streamResponse.Type == streaming.StreamResponseTypeFinal {
core/bifrost.go (1)

69-75: Tracing integration through Bifrost and PluginPipeline looks solid; consider minor attribute/key naming alignment.

The new tracer plumbing (atomic tracerWrapper, Init/SetTracer, execution spans in executeRequestWithRetries, fallback spans, key‑selection spans, and PluginPipeline’s streaming post‑hook aggregation/finalizer) is consistent and concurrency‑safe for the intended Init‑based construction and per‑request pipeline usage. The shutdown path correctly calls tracer.Stop() via the wrapper, so TraceStore and the streaming accumulator get cleaned up.

One small nit: in the key‑selection span you’re emitting "key.id" / "key.name" instead of the existing AttrSelectedKeyID / AttrSelectedKeyName semantic keys from core/schemas/trace.go. For downstream consumers this inconsistency can be confusing; consider switching those attributes to the standardized names to keep trace data uniform.

Also applies to: 102-107, 118-147, 253-265, 2396-2425, 2495-2523, 2841-2950, 3045-3075, 3080-3099, 3538-3555, 3946-3950

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 76a4e9d and d3b125f.

⛔ Files ignored due to path filters (4)
  • core/go.sum is excluded by !**/*.sum
  • examples/plugins/hello-world/go.sum is excluded by !**/*.sum
  • plugins/governance/go.sum is excluded by !**/*.sum
  • transports/go.sum is excluded by !**/*.sum
📒 Files selected for processing (93)
  • .github/workflows/release-pipeline.yml
  • .github/workflows/scripts/push-mintlify-changelog.sh
  • .github/workflows/scripts/release-bifrost-http.sh
  • .github/workflows/scripts/release-framework.sh
  • core/bifrost.go
  • core/bifrost_test.go
  • core/changelog.md
  • core/go.mod
  • core/mcp.go
  • core/providers/utils/utils.go
  • core/schemas/bifrost.go
  • core/schemas/context.go
  • core/schemas/plugin.go
  • core/schemas/trace.go
  • core/schemas/tracer.go
  • core/utils.go
  • core/version
  • docs/docs.json
  • docs/plugins/getting-started.mdx
  • docs/plugins/migration-guide.mdx
  • docs/plugins/writing-plugin.mdx
  • examples/plugins/hello-world/go.mod
  • examples/plugins/hello-world/main.go
  • framework/changelog.md
  • framework/configstore/tables/mcp.go
  • framework/plugins/dynamicplugin.go
  • framework/plugins/dynamicplugin_test.go
  • framework/streaming/accumulator.go
  • framework/streaming/audio.go
  • framework/streaming/chat.go
  • framework/streaming/responses.go
  • framework/streaming/transcription.go
  • framework/streaming/types.go
  • framework/tracing/helpers.go
  • framework/tracing/llmspan.go
  • framework/tracing/propagation.go
  • framework/tracing/store.go
  • framework/tracing/tracer.go
  • framework/version
  • plugins/governance/changelog.md
  • plugins/governance/go.mod
  • plugins/governance/main.go
  • plugins/jsonparser/changelog.md
  • plugins/jsonparser/main.go
  • plugins/logging/changelog.md
  • plugins/logging/main.go
  • plugins/logging/utils.go
  • plugins/maxim/changelog.md
  • plugins/maxim/main.go
  • plugins/mocker/changelog.md
  • plugins/mocker/main.go
  • plugins/otel/changelog.md
  • plugins/otel/converter.go
  • plugins/otel/main.go
  • plugins/otel/ttlsyncmap.go
  • plugins/semanticcache/changelog.md
  • plugins/semanticcache/main.go
  • plugins/telemetry/changelog.md
  • plugins/telemetry/main.go
  • transports/bifrost-http/handlers/cache.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/devpprof.go
  • transports/bifrost-http/handlers/devpprof_unix.go
  • transports/bifrost-http/handlers/devpprof_windows.go
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/handlers/health.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/handlers/logging.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/handlers/mcpserver.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • transports/bifrost-http/handlers/plugins.go
  • transports/bifrost-http/handlers/providers.go
  • transports/bifrost-http/handlers/session.go
  • transports/bifrost-http/handlers/ui.go
  • transports/bifrost-http/handlers/websocket.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/lib/config_test.go
  • transports/bifrost-http/lib/middleware.go
  • transports/bifrost-http/server/server.go
  • transports/changelog.md
  • transports/go.mod
  • transports/version
  • ui/app/clientLayout.tsx
  • ui/app/workspace/logs/views/logDetailsSheet.tsx
  • ui/components/devProfiler.tsx
  • ui/components/sidebar.tsx
  • ui/components/ui/switch.tsx
  • ui/lib/store/apis/devApi.ts
  • ui/lib/store/apis/index.ts
💤 Files with no reviewable changes (3)
  • framework/configstore/tables/mcp.go
  • core/mcp.go
  • plugins/otel/ttlsyncmap.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:

  • framework/streaming/responses.go
  • core/version
  • framework/streaming/types.go
  • transports/bifrost-http/handlers/logging.go
  • plugins/jsonparser/changelog.md
  • ui/lib/store/apis/index.ts
  • transports/bifrost-http/handlers/session.go
  • transports/bifrost-http/handlers/cache.go
  • transports/bifrost-http/handlers/providers.go
  • plugins/maxim/changelog.md
  • framework/streaming/chat.go
  • plugins/logging/utils.go
  • transports/bifrost-http/handlers/devpprof_windows.go
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/handlers/config.go
  • framework/plugins/dynamicplugin_test.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • ui/app/workspace/logs/views/logDetailsSheet.tsx
  • plugins/semanticcache/changelog.md
  • plugins/logging/changelog.md
  • transports/go.mod
  • plugins/governance/changelog.md
  • transports/bifrost-http/handlers/governance.go
  • examples/plugins/hello-world/main.go
  • transports/bifrost-http/lib/middleware.go
  • framework/streaming/transcription.go
  • framework/streaming/audio.go
  • plugins/telemetry/changelog.md
  • plugins/semanticcache/main.go
  • ui/components/devProfiler.tsx
  • ui/app/clientLayout.tsx
  • transports/bifrost-http/handlers/plugins.go
  • plugins/otel/main.go
  • core/changelog.md
  • transports/bifrost-http/handlers/health.go
  • docs/docs.json
  • core/schemas/plugin.go
  • core/bifrost_test.go
  • plugins/mocker/changelog.md
  • transports/changelog.md
  • framework/plugins/dynamicplugin.go
  • framework/streaming/accumulator.go
  • core/go.mod
  • plugins/mocker/main.go
  • plugins/governance/main.go
  • framework/changelog.md
  • docs/plugins/writing-plugin.mdx
  • core/schemas/context.go
  • transports/bifrost-http/handlers/ui.go
  • transports/bifrost-http/handlers/middlewares.go
  • core/providers/utils/utils.go
  • plugins/maxim/main.go
  • transports/bifrost-http/handlers/devpprof_unix.go
  • transports/bifrost-http/handlers/websocket.go
  • plugins/otel/changelog.md
  • docs/plugins/migration-guide.mdx
  • examples/plugins/hello-world/go.mod
  • transports/version
  • ui/lib/store/apis/devApi.ts
  • transports/bifrost-http/integrations/router.go
  • docs/plugins/getting-started.mdx
  • core/utils.go
  • plugins/jsonparser/main.go
  • transports/bifrost-http/handlers/devpprof.go
  • framework/tracing/propagation.go
  • ui/components/sidebar.tsx
  • framework/version
  • transports/bifrost-http/handlers/mcpserver.go
  • transports/bifrost-http/lib/config_test.go
  • core/schemas/bifrost.go
  • ui/components/ui/switch.tsx
  • transports/bifrost-http/handlers/inference.go
  • plugins/logging/main.go
  • plugins/governance/go.mod
  • transports/bifrost-http/lib/config.go
  • framework/tracing/helpers.go
  • transports/bifrost-http/server/server.go
  • core/schemas/tracer.go
  • framework/tracing/store.go
  • core/schemas/trace.go
  • plugins/otel/converter.go
  • plugins/telemetry/main.go
  • framework/tracing/tracer.go
  • transports/bifrost-http/handlers/mcp.go
  • core/bifrost.go
  • framework/tracing/llmspan.go
🧠 Learnings (7)
📚 Learning: 2025-12-09T17:07:42.007Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/schemas/account.go:9-18
Timestamp: 2025-12-09T17:07:42.007Z
Learning: In core/schemas/account.go, the HuggingFaceKeyConfig field within the Key struct is currently unused and reserved for future Hugging Face inference endpoint deployments. Do not flag this field as missing from OpenAPI documentation or require its presence in the API spec until the feature is actively implemented and used. When the feature is added, update the OpenAPI docs accordingly; otherwise, treat this field as non-breaking and not part of the current API surface.

Applied to files:

  • framework/streaming/responses.go
  • framework/streaming/types.go
  • transports/bifrost-http/handlers/logging.go
  • transports/bifrost-http/handlers/session.go
  • transports/bifrost-http/handlers/cache.go
  • transports/bifrost-http/handlers/providers.go
  • framework/streaming/chat.go
  • plugins/logging/utils.go
  • transports/bifrost-http/handlers/devpprof_windows.go
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/handlers/config.go
  • framework/plugins/dynamicplugin_test.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • transports/bifrost-http/handlers/governance.go
  • examples/plugins/hello-world/main.go
  • transports/bifrost-http/lib/middleware.go
  • framework/streaming/transcription.go
  • framework/streaming/audio.go
  • plugins/semanticcache/main.go
  • transports/bifrost-http/handlers/plugins.go
  • plugins/otel/main.go
  • transports/bifrost-http/handlers/health.go
  • core/schemas/plugin.go
  • core/bifrost_test.go
  • framework/plugins/dynamicplugin.go
  • framework/streaming/accumulator.go
  • plugins/mocker/main.go
  • plugins/governance/main.go
  • core/schemas/context.go
  • transports/bifrost-http/handlers/ui.go
  • transports/bifrost-http/handlers/middlewares.go
  • core/providers/utils/utils.go
  • plugins/maxim/main.go
  • transports/bifrost-http/handlers/devpprof_unix.go
  • transports/bifrost-http/handlers/websocket.go
  • transports/bifrost-http/integrations/router.go
  • core/utils.go
  • plugins/jsonparser/main.go
  • transports/bifrost-http/handlers/devpprof.go
  • framework/tracing/propagation.go
  • transports/bifrost-http/handlers/mcpserver.go
  • transports/bifrost-http/lib/config_test.go
  • core/schemas/bifrost.go
  • transports/bifrost-http/handlers/inference.go
  • plugins/logging/main.go
  • transports/bifrost-http/lib/config.go
  • framework/tracing/helpers.go
  • transports/bifrost-http/server/server.go
  • core/schemas/tracer.go
  • framework/tracing/store.go
  • core/schemas/trace.go
  • plugins/otel/converter.go
  • plugins/telemetry/main.go
  • framework/tracing/tracer.go
  • transports/bifrost-http/handlers/mcp.go
  • core/bifrost.go
  • framework/tracing/llmspan.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/logging.go
  • transports/bifrost-http/handlers/session.go
  • transports/bifrost-http/handlers/cache.go
  • transports/bifrost-http/handlers/providers.go
  • transports/bifrost-http/handlers/devpprof_windows.go
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/lib/middleware.go
  • transports/bifrost-http/handlers/plugins.go
  • transports/bifrost-http/handlers/health.go
  • transports/bifrost-http/handlers/ui.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/handlers/devpprof_unix.go
  • transports/bifrost-http/handlers/websocket.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/handlers/devpprof.go
  • transports/bifrost-http/handlers/mcpserver.go
  • transports/bifrost-http/lib/config_test.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/server/server.go
  • transports/bifrost-http/handlers/mcp.go
📚 Learning: 2025-12-29T09:14:16.633Z
Learnt from: akshaydeo
Repo: maximhq/bifrost PR: 888
File: transports/bifrost-http/handlers/middlewares.go:246-256
Timestamp: 2025-12-29T09:14:16.633Z
Learning: In the bifrost HTTP transport, fasthttp.RequestCtx is the primary context carrier and should be passed directly to functions that expect a context.Context. Do not convert to context.Context unless explicitly required. Ensure tracer implementations and related components are designed to accept fasthttp.RequestCtx directly, and document this architectural decision for maintainers.

Applied to files:

  • transports/bifrost-http/handlers/logging.go
  • transports/bifrost-http/handlers/session.go
  • transports/bifrost-http/handlers/cache.go
  • transports/bifrost-http/handlers/providers.go
  • transports/bifrost-http/handlers/devpprof_windows.go
  • transports/bifrost-http/handlers/integrations.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/lib/middleware.go
  • transports/bifrost-http/handlers/plugins.go
  • transports/bifrost-http/handlers/health.go
  • transports/bifrost-http/handlers/ui.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/handlers/devpprof_unix.go
  • transports/bifrost-http/handlers/websocket.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/handlers/devpprof.go
  • transports/bifrost-http/handlers/mcpserver.go
  • transports/bifrost-http/lib/config_test.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/server/server.go
  • transports/bifrost-http/handlers/mcp.go
📚 Learning: 2025-12-24T04:36:57.698Z
Learnt from: danpiths
Repo: maximhq/bifrost PR: 1169
File: transports/bifrost-http/handlers/governance.go:1708-1878
Timestamp: 2025-12-24T04:36:57.698Z
Learning: In governance update handlers (e.g., updateModelConfig, updateProviderGovernance), design updates to support clearing individual fields by sending null/empty values (e.g., {"rate_limit": {"token_max_limit": null}} clears token_max_limit). Follow this pattern for partial updates so users can remove specific governance settings without deleting the whole entity. Ensure budget updates follow the same approach using direct field assignment. Review input validation, JSON decoding (e.g., pointers vs values in Go structs), and API documentation to reflect nullable fields and expected behavior.

Applied to files:

  • transports/bifrost-http/handlers/governance.go
📚 Learning: 2025-12-22T10:50:40.990Z
Learnt from: Pratham-Mishra04
Repo: maximhq/bifrost PR: 1154
File: plugins/governance/store.go:1165-1186
Timestamp: 2025-12-22T10:50:40.990Z
Learning: In the Bifrost governance plugin, budgets and rate limits have 1:1 relationships with their parent entities (virtual keys, teams, customers). Do not assume sharing; ensure cascade deletion logic only deletes budgets/rate limits when there are no shared references. Enforce invariants in code and add tests to verify no cross-entity sharing and that cascade deletes only remove the specific child of the parent. If a counterexample arises, adjust data model or add guards.

Applied to files:

  • plugins/governance/main.go
📚 Learning: 2025-12-19T09:26:54.961Z
Learnt from: qwerty-dvorak
Repo: maximhq/bifrost PR: 1006
File: core/providers/utils/utils.go:1050-1051
Timestamp: 2025-12-19T09:26:54.961Z
Learning: Update streaming end-marker handling so HuggingFace is treated as a non-[DONE] provider for backends that do not emit a DONE marker (e.g., meta llama on novita). In core/providers/utils/utils.go, adjust ProviderSendsDoneMarker() (or related logic) to detect providers that may not emit DONE and avoid relying on DONE as the sole end signal. Add tests to cover both DONE-emitting and non-DONE backends, with clear documentation in code comments explaining the rationale and any fallback behavior.

Applied to files:

  • core/providers/utils/utils.go
📚 Learning: 2025-12-24T10:55:31.424Z
Learnt from: akshaydeo
Repo: maximhq/bifrost PR: 888
File: framework/tracing/propagation.go:24-36
Timestamp: 2025-12-24T10:55:31.424Z
Learning: In framework/tracing/propagation.go, ensure ExtractParentID() returns ctx.TraceID (not ctx.ParentID) because the parent request's traceparent header carries the trace ID used for continuing the trace.

Applied to files:

  • framework/tracing/propagation.go
🧬 Code graph analysis (42)
transports/bifrost-http/handlers/logging.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/session.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/cache.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/providers.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
framework/streaming/chat.go (1)
framework/streaming/types.go (1)
  • AccumulatedData (28-48)
plugins/logging/utils.go (5)
core/schemas/tracer.go (1)
  • StreamAccumulatorResult (15-33)
core/schemas/bifrost.go (6)
  • RequestType (89-89)
  • TextCompletionStreamRequest (94-94)
  • ChatCompletionStreamRequest (96-96)
  • ResponsesStreamRequest (98-98)
  • SpeechStreamRequest (101-101)
  • TranscriptionStreamRequest (103-103)
framework/streaming/types.go (11)
  • ProcessedStreamResponse (118-126)
  • StreamType (10-10)
  • StreamTypeText (13-13)
  • StreamTypeChat (14-14)
  • StreamTypeResponses (17-17)
  • StreamTypeAudio (15-15)
  • StreamTypeTranscription (16-16)
  • StreamResponseType (20-20)
  • StreamResponseTypeFinal (24-24)
  • StreamResponseTypeDelta (23-23)
  • AccumulatedData (28-48)
core/schemas/models.go (1)
  • Model (109-129)
core/schemas/chatcompletions.go (1)
  • ChatAssistantMessage (659-666)
transports/bifrost-http/handlers/integrations.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/config.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
framework/plugins/dynamicplugin_test.go (1)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-27)
transports/bifrost-http/handlers/middlewares_test.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/governance.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
examples/plugins/hello-world/main.go (2)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
core/schemas/bifrost.go (1)
  • BifrostContextKey (119-119)
transports/bifrost-http/lib/middleware.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
framework/streaming/transcription.go (1)
framework/streaming/types.go (1)
  • AccumulatedData (28-48)
framework/streaming/audio.go (1)
framework/streaming/types.go (1)
  • AccumulatedData (28-48)
ui/components/devProfiler.tsx (3)
npx/bin.js (3)
  • k (178-178)
  • sizes (179-179)
  • data (172-172)
ui/lib/utils/port.ts (1)
  • isDevelopmentMode (110-112)
ui/components/ui/tooltip.tsx (1)
  • Tooltip (43-43)
ui/app/clientLayout.tsx (1)
ui/components/devProfiler.tsx (1)
  • DevProfiler (55-422)
transports/bifrost-http/handlers/plugins.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
plugins/otel/main.go (3)
core/schemas/plugin.go (2)
  • BifrostHTTPMiddleware (38-38)
  • ObservabilityPlugin (123-137)
core/schemas/context.go (1)
  • BifrostContext (32-42)
core/schemas/trace.go (1)
  • Trace (10-19)
transports/bifrost-http/handlers/health.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
core/schemas/plugin.go (2)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-27)
core/schemas/trace.go (1)
  • Trace (10-19)
core/bifrost_test.go (1)
core/schemas/tracer.go (1)
  • DefaultTracer (181-183)
framework/streaming/accumulator.go (1)
framework/streaming/types.go (3)
  • StreamAccumulator (103-115)
  • ChatStreamChunk (77-87)
  • ResponsesStreamChunk (90-100)
plugins/mocker/main.go (2)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-27)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
plugins/governance/main.go (3)
core/utils.go (1)
  • Ptr (56-58)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-27)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
core/schemas/context.go (1)
core/schemas/bifrost.go (1)
  • BifrostContextKeyDeferTraceCompletion (148-148)
transports/bifrost-http/handlers/ui.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
core/providers/utils/utils.go (4)
core/schemas/bifrost.go (5)
  • BifrostResponse (348-369)
  • BifrostContextKeyTraceID (144-144)
  • BifrostError (490-499)
  • BifrostContextKeyPostHookSpanFinalizer (150-150)
  • BifrostContextKeySpanID (145-145)
core/schemas/tracer.go (1)
  • Tracer (38-116)
core/schemas/trace.go (2)
  • AttrTimeToFirstToken (198-198)
  • AttrTotalChunks (199-199)
framework/tracing/helpers.go (1)
  • EndSpan (50-56)
plugins/maxim/main.go (1)
core/schemas/plugin.go (2)
  • Plugin (71-97)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/handlers/websocket.go (1)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
ui/lib/store/apis/devApi.ts (2)
transports/bifrost-http/handlers/devpprof.go (6)
  • MemoryStats (29-35)
  • CPUStats (38-42)
  • RuntimeStats (45-51)
  • AllocationInfo (54-60)
  • HistoryPoint (63-70)
  • PprofData (73-80)
ui/lib/store/apis/index.ts (1)
  • baseApi (2-2)
transports/bifrost-http/integrations/router.go (2)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
core/schemas/bifrost.go (2)
  • BifrostContextKeyDeferTraceCompletion (148-148)
  • BifrostContextKeyTraceCompleter (149-149)
plugins/jsonparser/main.go (2)
examples/plugins/hello-world/main.go (1)
  • HTTPTransportMiddleware (19-27)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
transports/bifrost-http/lib/config_test.go (1)
transports/bifrost-http/lib/config.go (1)
  • LoadConfig (278-358)
core/schemas/bifrost.go (2)
framework/tracing/tracer.go (1)
  • Tracer (17-20)
core/schemas/tracer.go (1)
  • Tracer (38-116)
transports/bifrost-http/handlers/inference.go (2)
core/schemas/plugin.go (1)
  • BifrostHTTPMiddleware (38-38)
core/schemas/bifrost.go (2)
  • BifrostContextKeyDeferTraceCompletion (148-148)
  • BifrostContextKeyTraceCompleter (149-149)
plugins/logging/main.go (6)
core/utils.go (1)
  • IsStreamRequestType (197-199)
core/schemas/bifrost.go (3)
  • RequestType (89-89)
  • BifrostContextKeyTracer (147-147)
  • BifrostContextKeyTraceID (144-144)
framework/tracing/tracer.go (1)
  • Tracer (17-20)
core/schemas/tracer.go (1)
  • Tracer (38-116)
framework/streaming/types.go (1)
  • ProcessedStreamResponse (118-126)
core/providers/gemini/types.go (1)
  • Type (783-783)
core/schemas/tracer.go (5)
core/schemas/bifrost.go (4)
  • ModelProvider (33-33)
  • BifrostError (490-499)
  • BifrostRequest (175-196)
  • BifrostResponse (348-369)
core/schemas/chatcompletions.go (1)
  • BifrostLLMUsage (855-862)
core/schemas/speech.go (1)
  • BifrostSpeechResponse (22-29)
core/schemas/transcriptions.go (1)
  • BifrostTranscriptionResponse (16-26)
core/schemas/trace.go (3)
  • Trace (10-19)
  • SpanKind (115-115)
  • SpanStatus (143-143)
framework/tracing/store.go (4)
core/schemas/tracer.go (1)
  • Tracer (38-116)
core/schemas/bifrost.go (1)
  • BifrostResponse (348-369)
core/schemas/trace.go (5)
  • Trace (10-19)
  • Span (52-65)
  • SpanEvent (107-111)
  • SpanKind (115-115)
  • SpanStatus (143-143)
framework/tracing/helpers.go (3)
  • GetTrace (23-29)
  • AddSpan (32-38)
  • EndSpan (50-56)
core/schemas/trace.go (2)
framework/tracing/helpers.go (1)
  • AddSpan (32-38)
ui/lib/constants/logs.ts (1)
  • Status (171-171)
plugins/otel/converter.go (2)
core/schemas/trace.go (13)
  • Trace (10-19)
  • Span (52-65)
  • SpanKind (115-115)
  • SpanKindLLMCall (121-121)
  • SpanKindHTTPRequest (131-131)
  • SpanKindRetry (127-127)
  • SpanKindFallback (129-129)
  • SpanKindMCPTool (125-125)
  • SpanKindEmbedding (133-133)
  • SpanKindSpeech (135-135)
  • SpanKindTranscription (137-137)
  • SpanStatus (143-143)
  • SpanEvent (107-111)
transports/bifrost-http/main.go (1)
  • Version (72-72)
framework/tracing/tracer.go (6)
framework/tracing/store.go (1)
  • TraceStore (25-37)
framework/streaming/accumulator.go (2)
  • Accumulator (14-30)
  • NewAccumulator (440-480)
core/schemas/tracer.go (2)
  • Tracer (38-116)
  • SpanHandle (11-11)
framework/tracing/helpers.go (3)
  • GetTraceID (11-20)
  • EndSpan (50-56)
  • GetTrace (23-29)
core/schemas/bifrost.go (2)
  • BifrostContextKeyRequestID (125-125)
  • BifrostContextKeyStreamEndIndicator (132-132)
framework/streaming/types.go (1)
  • StreamResponseTypeFinal (24-24)
🪛 markdownlint-cli2 (0.18.1)
transports/changelog.md

15-15: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

Comment thread core/version
Comment thread transports/bifrost-http/handlers/inference.go
Comment thread transports/bifrost-http/integrations/router.go
Comment thread transports/version
@akshaydeo akshaydeo merged commit ed0154c into v1.4.0 Dec 29, 2025
4 checks passed
@akshaydeo akshaydeo deleted the 11-19-plugins_v2_architecture branch December 29, 2025 10:50
@coderabbitai coderabbitai Bot mentioned this pull request Jan 7, 2026
18 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant