github maximhq/bifrost transports/v2.2.1
Bifrost HTTP v2.2.1

latest releases: framework/v1.7.2, ent-v2.2.1-base
3 hours ago

Bifrost HTTP Transport Release v2.2.1

✨ Features

  • Virtual Key Assignees and Expanded Search - The virtual key list resolves assigned_user for every row via a single batch lookup, and VK search matches team, customer and user names in addition to key fields, so keys can be found by who owns them (#7225, #7226)
  • User Attribution in Prometheus Metrics - The telemetry plugin's metrics export includes user id and user name labels, so per-user usage and error rates can be graphed and alerted on directly (#7267)
  • Prompt Cache Breakpoints Capability - A SupportsPromptCacheBreakpoints model cap with a name-based fallback for OpenRouter gates prompt_cache_breakpoint forwarding, so OpenRouter's non-Claude models stop rejecting requests that carry it (#7261, #7262)
  • MCP Observed Latency - Native (observed) MCP tool-call latency from observed_latency_ms is surfaced in the logs duration column and detail sheet, kept clearly distinct from true execution time and never fabricating a synthetic start timestamp on the timeline (#7271)

🐞 Fixed

  • Resource ID Path Injection - Caller-supplied resource IDs (batch, file, video, container, response IDs, cached content names) were interpolated into outbound URL paths unvalidated; a crafted ID with ../, ?, #, percent-encoded bytes or control characters could redirect the request to an unintended upstream endpoint. A central EscapeResourceID helper validates and escapes every such ID (#7307, #7310)
  • Bedrock S3 SSRF - A caller-supplied s3:// file ID or s3_bucket param could control the upstream host, since S3 virtual-hosted URLs are built as https://{bucket}.{s3host}/{key}; a bucket like 127.0.0.1:PORT# opened a SigV4-signed TLS connection to a caller-chosen host. Bucket names are now validated with a DNS-compatible regex (#7315)
  • Caller-Forged Billing Idempotency - The billing-idempotency key was (RequestID, AttemptNumber), and RequestID may come from the caller's x-request-id header, so two unrelated requests sharing a deliberately chosen ID collided and the second was silently never charged. An internally minted BillingNonce is mixed into the key, making it unforgeable (#7303)
  • Abandoned Non-Streaming Request Hung Forever - The worker kept a ctx.Done() arm on an already-claimed delivery send, a regression from the #6972 delivery fix, so a non-streaming caller could hang indefinitely (#7313)
  • Repeated Empty Thinking Blocks - Streaming chat chunks emitted empty reasoning/message fields on every content delta, which clients rendered as repeated empty thinking blocks (#7318)
  • Claude Code Thread Continuations Failed Behind Key Rotation - Claude Code's server-side conversation threads are bound to the upstream account that creates them, and Bifrost's per-request key selection, retries and fallbacks cannot keep a continuation on that account, so thread: {"type": "continue"} requests failed with thread_not_found (about half the time on a two-key config). The Anthropic integration now declares itself stateless: continuations are refused before the provider call with a 400 whose details.error_code is thread_unsupported_request, which makes the client resend the turn in full and stop sending the thread field for the rest of the session, and the provider raw-body path strips thread from create requests so no orphaned thread state accumulates upstream. Token counting is never refused (#7274)
  • Gemini Flash-Lite Minimal Thinking Promoted to Low - Normalized gemini-3.1-flash-lite requests silently promoted minimal thinking to low on both Gemini and Vertex; the text model's four supported levels are now registered, preserving the image variant's separate restrictions (#7288) (thanks @Javtor!)
  • Config.json Virtual Key Limits Broke Under UI Edits - VK rate limits and budgets created via the config.json standalone-limits flow and then edited through the UI produced duplicate, conflicting ownership records (standalone budgets owned directly by the VK, plus orphaned UUID rate-limit rows created by the UI). Migration migrate_vk_standalone_limits_to_model_configs consolidates ownership into VK-scoped model configs while preserving usage counters, and the write paths stop creating the divergent rows (#7291)
  • Bedrock Service Tier Rejections - Service tier forwarding for Bedrock (Converse and Mantle paths) sent whatever tier was requested; it is now gated on explicit model capability metadata, failing closed when none exists, so models that do not support the requested tier stop rejecting the request (#7266)
  • Anthropic Root-Level Tool Schema Compositions - Anthropic rejects oneOf/anyOf/allOf at the root of a tool's input_schema but accepts them inside properties; root-level compositions are now rewritten into a flat object schema before dispatch, unblocking tools like Codex's automation_update (#7265)
  • GenAI Streaming TTS - Speech stream chunks routed through the /genai integration had no converter registered; ToGeminiSpeechStreamResponse now serves streaming TTS, and the streaming router returns a clean error instead of panicking when any stream converter is missing (#7248)
  • xAI Usage and Cost - xAI reports visible and reasoning completion tokens separately; completion_tokens now folds reasoning in so prompt_tokens + completion_tokens = total_tokens holds, and streaming cost normalization preserves xAI's authoritative cost_in_usd_ticks to the terminal usage chunk instead of falling back to catalog pricing (#7245, #7250)
  • OpenAI-Only search_content_types Forwarded Everywhere - search_content_types on web_search tools is an OpenAI-specific extension; it is now gated behind a per-provider capability check so Bedrock and other OpenAI-compatible backends receive a clean web_search tool without the field (#7244)
  • Claude Code diagnostics Field Rejected by Non-Native Providers - Claude Code sends diagnostics.previous_message_id on every request; on the typed-sanitizer path Bedrock, Vertex and Azure returned 400 diagnostics: Extra inputs are not permitted. The field is stripped for providers that do not support it (#7243)
  • Unreadable Bedrock Error Logs - AWS returns errors in a flat {"message": ..., "__type": ...} shape the shared Anthropic/OpenAI parsers never looked at, so Bedrock errors were logged with no human-readable reason; the root-level message now seeds BifrostError, and the logs UI falls back to showing the raw provider error body when no message could be extracted (#7221, #7222)
  • Anthropic container Param Dropped - The string-form container param on /anthropic/v1/messages was silently dropped, so container reuse provisioned a fresh container every time; it is now carried through the round trip (#5829) (thanks @AdityaPainuli!)
  • Responses API Finish Reason Missing From OTEL Spans - gen_ai.response.finish_reason / finish_reasons are now emitted on the llm.call span for /v1/responses requests, so refusals are visible to OTEL consumers, matching the chat path (#7205) (thanks @bkfl-notai!)
  • Cohere Fallback Response Shape - The Cohere-compatible route returned the raw Bifrost normalized response when a fallback served the request from a non-Cohere provider, which the Cohere SDK failed to parse; responses are now converted to the Cohere v2 shape (message, finish_reason) (#7198)
  • Skill Serving Race - Fixed a race condition in the skill serving handler
  • Log and Dashboard Label Truncation - Long model and provider labels in the logs table and dashboard charts truncate from the start so the distinctive suffix stays visible, the logs model column is wider, the search field border and icon spacing are cleaned up, and key picker options are keyed by id so duplicate labels stop highlighting together (#7215, #7216, #7217, #7269, #7297)

🗄️ Database Migrations

  • migrate_vk_standalone_limits_to_model_configs - Consolidates virtual key standalone budgets and rate limits into VK-scoped model configs: standalone budgets are re-pointed to the VK's top-level model config (created if missing, usage preserved), orphaned duplicate UUID rate-limit rows created by the UI are deleted with the model config re-pointed to the canonical config.json row, and vk.rate_limit_id is cleared.

🐙 Closed GitHub Issues

  • #123 - Files API Support
  • #5707 - string-form container param on /anthropic/v1/messages is silently dropped - container reuse provisions a fresh container
  • #7204 - Responses API path never sets gen_ai.response.finish_reason in OTEL traces (refusals invisible to observability)
  • #7287 - Gemini 3.1 Flash-Lite minimal thinking is silently promoted to low
  • #7294 - Streaming chat chunks emit empty reasoning/message fields on every content delta, causing repeated thinking blocks
  • #7308 - non-streaming caller hangs forever - worker keeps a ctx.Done() arm on a claimed delivery send (regression from #6972)

Installation

Docker

docker run -p 8080:8080 maximhq/bifrost:v2.2.1

Binary Download

npx @maximhq/bifrost --transport-version v2.2.1

Docker Images

  • maximhq/bifrost:v2.2.1 - This specific version
  • maximhq/bifrost:latest - Latest version (updated with this release)

This release was automatically created with dependencies: core v1.9.1, framework v1.7.1. All plugins have been validated and updated.

Don't miss a new bifrost release

NewReleases is sending notifications on new releases.