Skip to content

feat: starts forwarding x-bf-eh to OTEL - #3730

Merged
akshaydeo merged 2 commits into
devfrom
05-22-feat_starts_forwarding_x-bf-eh_to_otel
May 25, 2026
Merged

akshaydeo merged 2 commits into
devfrom
05-22-feat_starts_forwarding_x-bf-eh_to_otel

Conversation

@roroghost17

@roroghost17 roroghost17 commented May 25, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Extra headers forwarded to upstream providers (via x-bf-eh-* passthrough or direct allowlist forwarding) were not visible in trace spans, making it difficult to correlate what Bifrost actually sent to a provider with what observability backends recorded. This PR surfaces those headers as span attributes so the two views are consistent.

Changes

  • In executeRequestWithRetries, after setting the retry count attribute, the extra headers stored in the request context under BifrostContextKeyExtraHeaders are now iterated and each header is recorded as a span attribute using the gen_ai.request.extra_header prefix. Single-value headers are stored as a plain string; multi-value headers are stored as a slice.
  • Added AttrExtraHeaderPrefix = "gen_ai.request.extra_header" to the trace schema constants to provide a stable, namespaced key for these attributes.

Type of change

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

Affected areas

  • Core (Go)
  • Transports (HTTP)
  • Providers/Integrations
  • Plugins
  • UI (React)
  • Docs

How to test

Send a request to Bifrost that includes extra headers (either via x-bf-eh-* or an allowlisted header). Inspect the resulting trace span and confirm attributes of the form gen_ai.request.extra_header.<header-name> are present with the correct values.

go test ./...

Breaking changes

  • Yes
  • No

Related issues

Security considerations

Header values forwarded to upstream providers are recorded in trace spans. Ensure that any sensitive headers (e.g., authorization tokens, API keys) are not included in the extra headers allowlist or x-bf-eh-* passthrough, as they will become visible in observability backends.

Checklist

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

@CLAassistant

CLAassistant commented May 25, 2026 •

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@coderabbitai

coderabbitai Bot commented May 25, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • Chores
    • Enhanced request tracing to capture caller-supplied extra headers as span attributes on each retry/LLM call, including support for multiple header values.
    • Automatically redact sensitive headers in traces, recording only their presence to reduce exposure of credentials in observability data.

Walkthrough

Extra headers supplied by callers are surfaced as span attributes during LLM request retries. A new span attribute prefix constant and redaction helpers are added; retry logic records each header value (or a redacted marker for sensitive names) onto the active tracing span.

Changes

Extra Headers Span Attribute Tracing

Layer / File(s) Summary
Schema: AttrExtraHeader and token-detail constants
core/schemas/trace.go
Adds AttrExtraHeaderPrefix = "gen_ai.request.extra_header."; reformats GenAI token-detail constants and adjusts imports/struct alignment.
Redaction helpers (exported)
core/schemas/trace.go
Adds RedactedAttrValue = "REDACTED" and IsSensitiveHeader(name string) bool to classify credential-like headers for redaction.
Extra headers span recording implementation
core/bifrost.go
executeRequestWithRetries reads BifrostContextKeyExtraHeaders from context and sets span attributes per header using AttrExtraHeaderPrefix; uses IsSensitiveHeader to redact sensitive values and emits either a single value or the full []string when multiple values exist.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A header hops onto the tracing thread,
Extras whispered, softly said,
Sensitive secrets masked in light,
Each retry notes what callers write,
Spans now hold the story of the request.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: starts forwarding x-bf-eh to OTEL' directly summarizes the main change: surfacing extra headers as OpenTelemetry span attributes.
Description check ✅ Passed The description is comprehensive, covering all required sections including summary, changes, type, affected areas, testing, security, and checklist items. Minor: some checklist items remain unchecked despite being addressed (tests/documentation).
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 05-22-feat_starts_forwarding_x-bf-eh_to_otel

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


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

roroghost17 commented May 25, 2026 •

Copy link
Copy Markdown
Contributor Author

@greptile-apps

greptile-apps Bot commented May 25, 2026 •

Copy link
Copy Markdown
Contributor

Confidence Score: 5/5

The change is additive and bounded — it only appends span attributes after existing logic and adds no new code paths that could alter request execution.

Both previously identified blocking issues (missing trailing dot in the constant, no sensitive-header filtering) are addressed. The remaining observations are purely cosmetic: a redundant switch case and non-lowercased header keys in attribute names, neither of which affects correctness or security.

No files require special attention; both changed files are self-contained and well-commented.

Important Files Changed

Filename Overview
core/bifrost.go Adds extra-header span attribute recording in executeRequestWithRetries, with sensitive-header redaction via IsSensitiveHeader; minor: header name is not lowercased before use as an attribute key
core/schemas/trace.go Adds AttrExtraHeaderPrefix constant (with correct trailing dot), RedactedAttrValue, and IsSensitiveHeader function; minor: switch cases for "authorization"/"proxy-authorization" are redundant with the Contains check below

Reviews (3): Last reviewed commit: "feat: starts forwarding x-bf-eh to OTEL" | Re-trigger Greptile

Comment thread core/schemas/trace.go Outdated
Comment thread core/bifrost.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@core/bifrost.go`:
- Around line 5389-5403: The code currently records caller-supplied extra
headers from ctx.Value(schemas.BifrostContextKeyExtraHeaders) into OTEL via
tracer.SetAttribute (using schemas.AttrExtraHeaderPrefix+name), which may leak
secrets; change the logic in the block that iterates extraHeaders to filter
and/or redact sensitive header names (e.g. authorization, cookie, x-api-key,
set-cookie and any header matching /token|auth|secret/i) before calling
tracer.SetAttribute — for sensitive headers either omit the value and set a
presence flag attribute (e.g. schemas.AttrExtraHeaderPrefix+name+":present" =
true) or replace the value with a constant like "<redacted>" (or a minimal
masked form), while non-sensitive headers continue to be exported as before.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e78780be-4b75-4bb2-bc77-791326ae6375

📥 Commits

Reviewing files that changed from the base of the PR and between aacd593 and b0ec1df.

📒 Files selected for processing (2)
  • core/bifrost.go
  • core/schemas/trace.go

@roroghost17
roroghost17 force-pushed the 05-22-fix_search_content_and_video_getting_dropped_from_bedrock_requests branch from aacd593 to 25fbd51 Compare May 25, 2026 13:44
@roroghost17
roroghost17 force-pushed the 05-22-feat_starts_forwarding_x-bf-eh_to_otel branch from b0ec1df to 0c85f0b Compare May 25, 2026 13:45

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@core/schemas/trace.go`:
- Around line 427-433: The IsSensitiveHeader function currently only matches a
short whitelist of exact header names; broaden it to catch credential-bearing
headers by adding case-insensitive pattern/substring checks (or a compiled
regex) for common tokens like "token", "auth", "apikey"/"api-key", "secret",
"credential", "password", and header suffixes like "-api-key" or provider
prefixes (e.g., "x-amz-"); update the function IsSensitiveHeader to normalize
the name (lowercase/trim), then return true if it equals known exact names OR
contains any of those substrings or matches the suffix/patterns so headers such
as "x-auth-token", "x-amz-security-token", and "my-service-api-key" are also
treated as sensitive.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 74b9d5ad-cca7-4384-acd2-32ff97f9ef65

📥 Commits

Reviewing files that changed from the base of the PR and between b0ec1df and 0c85f0b.

📒 Files selected for processing (2)
  • core/bifrost.go
  • core/schemas/trace.go

Comment thread core/schemas/trace.go
@roroghost17
roroghost17 force-pushed the 05-22-fix_search_content_and_video_getting_dropped_from_bedrock_requests branch from 25fbd51 to 61f7e27 Compare May 25, 2026 14:35
@roroghost17
roroghost17 force-pushed the 05-22-feat_starts_forwarding_x-bf-eh_to_otel branch from 0c85f0b to bff5db0 Compare May 25, 2026 14:36

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@core/bifrost.go`:
- Around line 5389-5411: The code is recording BifrostContextKeyExtraHeaders
(the pre-filter context map) into spans, which can include headers that never
made it to the upstream request; instead emit attributes from the finalized
header set after filterHeaders(...)/skipHeaders have been applied. Modify the
code path that builds/sends the upstream request (the function that calls
filterHeaders and applies skipHeaders) to either: 1) move the
tracer.SetAttribute logic there and iterate the post-filter map, using
schemas.IsSensitiveHeader and schemas.AttrExtraHeaderPrefix as before, or 2)
return/pass the finalized map[string][]string up to the tracing caller (e.g.,
via the request object or context key) and change the current loop to read that
finalized map; ensure single-valued vs multi-valued handling and redaction
behavior (schemas.RedactedAttrValue) remain identical.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: da201eb1-d855-4263-a6d8-40df5c661f4c

📥 Commits

Reviewing files that changed from the base of the PR and between 0c85f0b and bff5db0.

📒 Files selected for processing (2)
  • core/bifrost.go
  • core/schemas/trace.go

Comment thread core/bifrost.go
coderabbitai[bot]
coderabbitai Bot previously approved these changes May 25, 2026

akshaydeo commented May 25, 2026 •

Copy link
Copy Markdown
Contributor

Merge activity

  • May 25, 3:35 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • May 25, 3:36 PM UTC: @akshaydeo merged this pull request with Graphite.

@akshaydeo
akshaydeo changed the base branch from 05-22-fix_search_content_and_video_getting_dropped_from_bedrock_requests to graphite-base/3730 May 25, 2026 15:35
@akshaydeo
akshaydeo changed the base branch from graphite-base/3730 to dev May 25, 2026 15:35
@akshaydeo
akshaydeo dismissed coderabbitai[bot]’s stale review May 25, 2026 15:35

The base branch was changed.

@akshaydeo
akshaydeo merged commit 6fbad5a into dev May 25, 2026
9 of 10 checks passed
@akshaydeo
akshaydeo deleted the 05-22-feat_starts_forwarding_x-bf-eh_to_otel branch May 25, 2026 15:36
akshaydeo pushed a commit that referenced this pull request May 26, 2026
## Summary

Extra headers forwarded to upstream providers (via `x-bf-eh-*` passthrough or direct allowlist forwarding) were not visible in trace spans, making it difficult to correlate what Bifrost actually sent to a provider with what observability backends recorded. This PR surfaces those headers as span attributes so the two views are consistent.

## Changes

- In `executeRequestWithRetries`, after setting the retry count attribute, the extra headers stored in the request context under `BifrostContextKeyExtraHeaders` are now iterated and each header is recorded as a span attribute using the `gen_ai.request.extra_header` prefix. Single-value headers are stored as a plain string; multi-value headers are stored as a slice.
- Added `AttrExtraHeaderPrefix = "gen_ai.request.extra_header"` to the trace schema constants to provide a stable, namespaced key for these attributes.

## Type of change

- [ ] Bug fix
- [x] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [x] Core (Go)
- [ ] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

Send a request to Bifrost that includes extra headers (either via `x-bf-eh-*` or an allowlisted header). Inspect the resulting trace span and confirm attributes of the form `gen_ai.request.extra_header.<header-name>` are present with the correct values.

```sh
go test ./...
```

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

## Security considerations

Header values forwarded to upstream providers are recorded in trace spans. Ensure that any sensitive headers (e.g., authorization tokens, API keys) are not included in the extra headers allowlist or `x-bf-eh-*` passthrough, as they will become visible in observability backends.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [ ] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [ ] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
@akshaydeo akshaydeo mentioned this pull request May 26, 2026
akshaydeo added a commit that referenced this pull request May 26, 2026
## ✨ Features

- **Azure v1 API Migration** — Migrated Azure provider to the v1 API:
removed the `api-version` query parameter and the
`/openai/deployments/{model}/...` URL pattern in favor of
`/openai/v1/{operation}`; the `api_version` field has been dropped from
`AzureKeyConfig` (#3661, #3756)
- **EnvVar Support for OTEL & Prometheus Configs** — `CollectorURL`,
`MetricsEndpoint`, headers, push gateway URL, and basic auth credentials
can now be sourced from environment variables (e.g.,
`env.OTEL_COLLECTOR_URL`); added a new `ConfigMarshallerPlugin`
interface that lets plugins control storage/redaction round-trips
(#3651)
- **OTel Extra Header Forwarding** — `x-bf-eh-*` extra headers forwarded
to upstream providers are now also emitted on the request span under
`gen_ai.request.extra_header.*` for end-to-end tracing (#3730)
- **OTel Semantic Conventions** — Aligned OTel attribute keys with the
OpenTelemetry GenAI spec (canonical `gen_ai.*` and new `bifrost.*`
attributes); legacy attributes are retained in parallel to avoid
breaking existing dashboards (#3732)
- **VK Quota with Provider Configs** — `GetVirtualKeyQuotaByValue` and
the `getVirtualKeyQuota` HTTP response now include `provider_configs`
with their budgets and rate limits (#3721)
- **MCP Temp Token Non-Auth Toggle** — Added
`mcp_enable_temp_token_auth` client config flag to gate short-lived MCP
token minting for non-authenticated users (#3720)
- **Responses Stream in JSON Parser** — `jsonparser` plugin now handles
OpenAI Responses API streaming (`ResponsesStreamRequest`) in addition to
chat completions (#3749)
- **Session API Rework** — Logout now calls both the password-based
session logout and OAuth logout endpoints and resets all RTK Query cache
state (#3698)

## 🐞 Fixed

- **Streaming Latency for Observability** — Deferred root span
termination to the trace completer callback for streaming requests so
request latency is no longer inflated by header-flush time (#3762)
- **Stream Cancellation Race** — Set `BifrostContextKeyConnectionClosed`
before closing the stream and short-circuit `idleTimeoutReader.Read`
when the connection is already closed to avoid panics and hangs on
cancellation (#3733)
- **Bedrock Cache Points** — Strip cache points from Bedrock requests
for models that do not support prompt caching (e.g., GLM, Llama) to
avoid Converse API errors (#3754)
- **Bedrock Empty Text Blocks** — Skip empty/nil text blocks during
Bedrock response conversion to avoid invalid messages (#3747)
- **Bedrock Reasoning + Tools** — Preserve reasoning content blocks on
assistant turns that also contain tool calls in the Bedrock chat
converter (#3690)
- **Bedrock Search Content & Video** — Restored search content and video
parts that were being dropped from Bedrock-native passthrough requests
(#3729)
- **Structured Output Stop Reason** — Fixed an incorrect `tool_calls`
finish reason when structured output is combined with extended-thinking
tools (#3685)
- **Gemini Tool Schema Passthrough** — Forward full tool parameter
schemas via `parametersJsonSchema` instead of the lossy `parameters`
form; corrected tool response role to `user`; resolved structured output
+ tools conflict (#3761)
- **Anthropic Stop Reason & Tool Versions** — Normalized stop reason
mapping (`end_turn` to `stop`, `tool_use` to `tool_calls`, `max_tokens`
to `length`) and upgraded `text_editor_20250124`/`str_replace_editor` to
`text_editor_20250728` for computer-use tools (#3761)
- **Azure Endpoint Redaction** — Fixed a panic when
`AzureKeyConfig.Endpoint` is a literal value rather than an env
reference (#3761)
- **Auth Middleware Path Match** — Match temp-token auth middleware
whitelist against the request path only, not the full URI with query
parameters (#3737)
- **Governance Blocked Models UI** — Restored the missing Blocked Models
create/edit UI in the VK provider config sheet (#3750)
- **Logging Plugin Cleanup Drain** — Fixed a shutdown race where
`batchWriter` could drop in-flight log entries; `Cleanup` now drains
both the recovered batch and remaining queue within a 30-second budget
(#3717)
- **Model Rankings Empty Entries** — Excluded entries with empty `model`
values from model rankings matview queries so blank rows no longer
surface in the UI (#3758)
- **User Filter Duplicates** — Recreated `mv_filter_users` matview to
require non-empty `user_name`, eliminating duplicate filter dropdown
entries (#3764)
- **User Filter Display Name** — Use `user_name` instead of `user_id` as
the display label for users in logging filters (#3691)
- **Large Numeric ID Precision** — Preserve large numeric IDs in URL
search params by skipping JSON parsing for plain strings (#3692)

## 🔧 Refactors & Chores

- **Error Propagation for GetAvailable\* APIs** — `GetAvailable*`
methods on `LoggerPlugin`/`LogManager` now return wrapped errors instead
of silently logging and returning empty slices (#3759)
- **Governance Blocklist Matching** — Use `slices.Contains` for VK
blocked-model matching for clearer code with identical semantics (#3727)
- **Exported `ResolvePeriod`** — Renamed `resolvePeriod` to
`ResolvePeriod` so external packages can reuse the period parsing
(#3763)

## 📚 Docs

- **OTEL Env Var Documentation** — Documented `env.VAR_NAME` support for
`collector_url`, `metrics_endpoint`, and headers in OTEL/Prometheus
plugin docs
- **OTEL OSS Features & Examples** — Added OTEL documentation to the OSS
features list with usage examples (#3731)
- **Anthropic Auth Recommendation** — Recommend `ANTHROPIC_AUTH_TOKEN`
over `ANTHROPIC_CUSTOM_HEADERS` for Claude Code authentication (#3686)
@akshaydeo akshaydeo mentioned this pull request May 27, 2026
18 tasks
akshaydeo added a commit that referenced this pull request May 27, 2026
## Summary

This PR releases Bifrost OSS `v1.5.5` and Enterprise `v1.4.4`, bumping all module pins from `v1.5.12`/`v1.3.12` to `v1.5.13`/`v1.3.13` across core, framework, and all plugins. It also hardens the Docker manifest shell scripts, expands CI egress allowlists, and updates documentation to reflect the new SCIM-based user provisioning feature.

## Changes

- **Module version bumps**: All `go.mod`/`go.sum` files updated from `core v1.5.12` → `v1.5.13`, `framework v1.3.12` → `v1.3.13`, and all plugin versions incremented accordingly (`compat`, `governance`, `jsonparser`, `logging`, `maxim`, `mocker`, `otel`, `prompts`, `semanticcache`, `telemetry`).
- **Docker manifest scripts**: Added `#!/usr/bin/env bash` shebang and `set -euo pipefail` to `create-docker-manifest.sh` and `create-docker-manifest-ubi9.sh`; quoted all variable expansions and switched `jq -r` to `jq -er` to fail on null digests.
- **CI egress allowlist**: Added `production.cloudfront.docker.com:443` to Docker-related job allowlists, and added `_https._tcp.dl.google.com:443` and `motd.ubuntu.com:443` to the Ubuntu package job allowlist.
- **Changelog files**: Cleared per-module `changelog.md` files (content moved into the new versioned docs). Added `docs/changelogs/v1.5.5.mdx` and `docs/changelogs/ent-v1.4.4.mdx` with full release notes, and registered both in `docs/docs.json`.
- **Documentation**: Replaced the SSO Integration link with a User Provisioning (SCIM) link in both `README.md` and `transports/README.md`.
- **Enterprise v1.4.4 highlights** (documented): Kafka and Google Cloud Pub/Sub observability sinks, chunked streaming with a 100 MB inter-node message ceiling, BigQuery custom labels via env vars using the new `ConfigMarshallerPlugin` interface, temporary access token expiry extensions, and a multi-node cluster integration harness.
- **OSS v1.5.5 highlights** (documented): Azure v1 API migration, env-var support for OTel/Prometheus configs, OTel extra-header forwarding and semantic-convention alignment, virtual key quota including provider configs, Responses API streaming in `jsonparser`, and a batch of Bedrock, Gemini, Anthropic, Azure, and logging plugin fixes.

## Type of change

- [ ] Bug fix
- [x] Feature
- [ ] Refactor
- [x] Documentation
- [x] Chore/CI

## Affected areas

- [x] Core (Go)
- [x] Transports (HTTP)
- [x] Providers/Integrations
- [x] Plugins
- [ ] UI (React)
- [x] Docs

## How to test

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

# Verify Docker manifest scripts exit on error
bash -n .github/workflows/scripts/create-docker-manifest.sh
bash -n .github/workflows/scripts/create-docker-manifest-ubi9.sh
```

Validate that the new changelog pages (`changelogs/v1.5.5` and `changelogs/ent-v1.4.4`) render correctly in the docs site.

## Screenshots/Recordings

N/A

## Breaking changes

- [x] Yes
- [ ] No

The Azure provider no longer accepts `api_version` in `AzureKeyConfig` and has migrated to the `/openai/v1/{operation}` URL pattern. See the [v1.4.0 Migration Guide](https://docs.getbifrost.ai/enterprise/migration-guides/v1.4.0) for full details.

## Related issues

#3661, #3756, #3651, #3730, #3732, #3754, #3747, #3690, #3729, #3685, #3733, #3761, #3735, #3721, #3720, #3749, #3698, #3762, #3750, #3727, #3717, #3759, #3758, #3764, #3691, #3692, #3737, #3763

## Security considerations

- The `ConfigMarshallerPlugin` interface redacts secrets (OTel collector URLs, Prometheus push gateway credentials, BigQuery labels) at config storage time and rehydrates them at load time, preventing plaintext secret persistence.
- Docker manifest scripts now use `set -euo pipefail`, preventing silent failures that could result in malformed or missing image manifests being pushed.

## Checklist

- [x] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [x] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [x] I verified the CI pipeline passes locally if applicable
akhsaul pushed a commit to akhsaul/bifrost that referenced this pull request Aug 27, 2026
## Summary

Extra headers forwarded to upstream providers (via `x-bf-eh-*` passthrough or direct allowlist forwarding) were not visible in trace spans, making it difficult to correlate what Bifrost actually sent to a provider with what observability backends recorded. This PR surfaces those headers as span attributes so the two views are consistent.

## Changes

- In `executeRequestWithRetries`, after setting the retry count attribute, the extra headers stored in the request context under `BifrostContextKeyExtraHeaders` are now iterated and each header is recorded as a span attribute using the `gen_ai.request.extra_header` prefix. Single-value headers are stored as a plain string; multi-value headers are stored as a slice.
- Added `AttrExtraHeaderPrefix = "gen_ai.request.extra_header"` to the trace schema constants to provide a stable, namespaced key for these attributes.

## Type of change

- [ ] Bug fix
- [x] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [x] Core (Go)
- [ ] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

Send a request to Bifrost that includes extra headers (either via `x-bf-eh-*` or an allowlisted header). Inspect the resulting trace span and confirm attributes of the form `gen_ai.request.extra_header.<header-name>` are present with the correct values.

```sh
go test ./...
```

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

## Security considerations

Header values forwarded to upstream providers are recorded in trace spans. Ensure that any sensitive headers (e.g., authorization tokens, API keys) are not included in the extra headers allowlist or `x-bf-eh-*` passthrough, as they will become visible in observability backends.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [ ] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [ ] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
akhsaul pushed a commit to akhsaul/bifrost that referenced this pull request Aug 27, 2026
## ✨ Features

- **Azure v1 API Migration** — Migrated Azure provider to the v1 API:
removed the `api-version` query parameter and the
`/openai/deployments/{model}/...` URL pattern in favor of
`/openai/v1/{operation}`; the `api_version` field has been dropped from
`AzureKeyConfig` (maximhq#3661, maximhq#3756)
- **EnvVar Support for OTEL & Prometheus Configs** — `CollectorURL`,
`MetricsEndpoint`, headers, push gateway URL, and basic auth credentials
can now be sourced from environment variables (e.g.,
`env.OTEL_COLLECTOR_URL`); added a new `ConfigMarshallerPlugin`
interface that lets plugins control storage/redaction round-trips
(maximhq#3651)
- **OTel Extra Header Forwarding** — `x-bf-eh-*` extra headers forwarded
to upstream providers are now also emitted on the request span under
`gen_ai.request.extra_header.*` for end-to-end tracing (maximhq#3730)
- **OTel Semantic Conventions** — Aligned OTel attribute keys with the
OpenTelemetry GenAI spec (canonical `gen_ai.*` and new `bifrost.*`
attributes); legacy attributes are retained in parallel to avoid
breaking existing dashboards (maximhq#3732)
- **VK Quota with Provider Configs** — `GetVirtualKeyQuotaByValue` and
the `getVirtualKeyQuota` HTTP response now include `provider_configs`
with their budgets and rate limits (maximhq#3721)
- **MCP Temp Token Non-Auth Toggle** — Added
`mcp_enable_temp_token_auth` client config flag to gate short-lived MCP
token minting for non-authenticated users (maximhq#3720)
- **Responses Stream in JSON Parser** — `jsonparser` plugin now handles
OpenAI Responses API streaming (`ResponsesStreamRequest`) in addition to
chat completions (maximhq#3749)
- **Session API Rework** — Logout now calls both the password-based
session logout and OAuth logout endpoints and resets all RTK Query cache
state (maximhq#3698)

## 🐞 Fixed

- **Streaming Latency for Observability** — Deferred root span
termination to the trace completer callback for streaming requests so
request latency is no longer inflated by header-flush time (maximhq#3762)
- **Stream Cancellation Race** — Set `BifrostContextKeyConnectionClosed`
before closing the stream and short-circuit `idleTimeoutReader.Read`
when the connection is already closed to avoid panics and hangs on
cancellation (maximhq#3733)
- **Bedrock Cache Points** — Strip cache points from Bedrock requests
for models that do not support prompt caching (e.g., GLM, Llama) to
avoid Converse API errors (maximhq#3754)
- **Bedrock Empty Text Blocks** — Skip empty/nil text blocks during
Bedrock response conversion to avoid invalid messages (maximhq#3747)
- **Bedrock Reasoning + Tools** — Preserve reasoning content blocks on
assistant turns that also contain tool calls in the Bedrock chat
converter (maximhq#3690)
- **Bedrock Search Content & Video** — Restored search content and video
parts that were being dropped from Bedrock-native passthrough requests
(maximhq#3729)
- **Structured Output Stop Reason** — Fixed an incorrect `tool_calls`
finish reason when structured output is combined with extended-thinking
tools (maximhq#3685)
- **Gemini Tool Schema Passthrough** — Forward full tool parameter
schemas via `parametersJsonSchema` instead of the lossy `parameters`
form; corrected tool response role to `user`; resolved structured output
+ tools conflict (maximhq#3761)
- **Anthropic Stop Reason & Tool Versions** — Normalized stop reason
mapping (`end_turn` to `stop`, `tool_use` to `tool_calls`, `max_tokens`
to `length`) and upgraded `text_editor_20250124`/`str_replace_editor` to
`text_editor_20250728` for computer-use tools (maximhq#3761)
- **Azure Endpoint Redaction** — Fixed a panic when
`AzureKeyConfig.Endpoint` is a literal value rather than an env
reference (maximhq#3761)
- **Auth Middleware Path Match** — Match temp-token auth middleware
whitelist against the request path only, not the full URI with query
parameters (maximhq#3737)
- **Governance Blocked Models UI** — Restored the missing Blocked Models
create/edit UI in the VK provider config sheet (maximhq#3750)
- **Logging Plugin Cleanup Drain** — Fixed a shutdown race where
`batchWriter` could drop in-flight log entries; `Cleanup` now drains
both the recovered batch and remaining queue within a 30-second budget
(maximhq#3717)
- **Model Rankings Empty Entries** — Excluded entries with empty `model`
values from model rankings matview queries so blank rows no longer
surface in the UI (maximhq#3758)
- **User Filter Duplicates** — Recreated `mv_filter_users` matview to
require non-empty `user_name`, eliminating duplicate filter dropdown
entries (maximhq#3764)
- **User Filter Display Name** — Use `user_name` instead of `user_id` as
the display label for users in logging filters (maximhq#3691)
- **Large Numeric ID Precision** — Preserve large numeric IDs in URL
search params by skipping JSON parsing for plain strings (maximhq#3692)

## 🔧 Refactors & Chores

- **Error Propagation for GetAvailable\* APIs** — `GetAvailable*`
methods on `LoggerPlugin`/`LogManager` now return wrapped errors instead
of silently logging and returning empty slices (maximhq#3759)
- **Governance Blocklist Matching** — Use `slices.Contains` for VK
blocked-model matching for clearer code with identical semantics (maximhq#3727)
- **Exported `ResolvePeriod`** — Renamed `resolvePeriod` to
`ResolvePeriod` so external packages can reuse the period parsing
(maximhq#3763)

## 📚 Docs

- **OTEL Env Var Documentation** — Documented `env.VAR_NAME` support for
`collector_url`, `metrics_endpoint`, and headers in OTEL/Prometheus
plugin docs
- **OTEL OSS Features & Examples** — Added OTEL documentation to the OSS
features list with usage examples (maximhq#3731)
- **Anthropic Auth Recommendation** — Recommend `ANTHROPIC_AUTH_TOKEN`
over `ANTHROPIC_CUSTOM_HEADERS` for Claude Code authentication (maximhq#3686)
occcat pushed a commit to occcat/bifrost that referenced this pull request Sep 2, 2026
## Summary

Extra headers forwarded to upstream providers (via `x-bf-eh-*` passthrough or direct allowlist forwarding) were not visible in trace spans, making it difficult to correlate what Bifrost actually sent to a provider with what observability backends recorded. This PR surfaces those headers as span attributes so the two views are consistent.

## Changes

- In `executeRequestWithRetries`, after setting the retry count attribute, the extra headers stored in the request context under `BifrostContextKeyExtraHeaders` are now iterated and each header is recorded as a span attribute using the `gen_ai.request.extra_header` prefix. Single-value headers are stored as a plain string; multi-value headers are stored as a slice.
- Added `AttrExtraHeaderPrefix = "gen_ai.request.extra_header"` to the trace schema constants to provide a stable, namespaced key for these attributes.

## Type of change

- [ ] Bug fix
- [x] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [x] Core (Go)
- [ ] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

Send a request to Bifrost that includes extra headers (either via `x-bf-eh-*` or an allowlisted header). Inspect the resulting trace span and confirm attributes of the form `gen_ai.request.extra_header.<header-name>` are present with the correct values.

```sh
go test ./...
```

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

## Security considerations

Header values forwarded to upstream providers are recorded in trace spans. Ensure that any sensitive headers (e.g., authorization tokens, API keys) are not included in the extra headers allowlist or `x-bf-eh-*` passthrough, as they will become visible in observability backends.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [ ] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [ ] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
occcat pushed a commit to occcat/bifrost that referenced this pull request Sep 2, 2026
## ✨ Features

- **Azure v1 API Migration** — Migrated Azure provider to the v1 API:
removed the `api-version` query parameter and the
`/openai/deployments/{model}/...` URL pattern in favor of
`/openai/v1/{operation}`; the `api_version` field has been dropped from
`AzureKeyConfig` (maximhq#3661, maximhq#3756)
- **EnvVar Support for OTEL & Prometheus Configs** — `CollectorURL`,
`MetricsEndpoint`, headers, push gateway URL, and basic auth credentials
can now be sourced from environment variables (e.g.,
`env.OTEL_COLLECTOR_URL`); added a new `ConfigMarshallerPlugin`
interface that lets plugins control storage/redaction round-trips
(maximhq#3651)
- **OTel Extra Header Forwarding** — `x-bf-eh-*` extra headers forwarded
to upstream providers are now also emitted on the request span under
`gen_ai.request.extra_header.*` for end-to-end tracing (maximhq#3730)
- **OTel Semantic Conventions** — Aligned OTel attribute keys with the
OpenTelemetry GenAI spec (canonical `gen_ai.*` and new `bifrost.*`
attributes); legacy attributes are retained in parallel to avoid
breaking existing dashboards (maximhq#3732)
- **VK Quota with Provider Configs** — `GetVirtualKeyQuotaByValue` and
the `getVirtualKeyQuota` HTTP response now include `provider_configs`
with their budgets and rate limits (maximhq#3721)
- **MCP Temp Token Non-Auth Toggle** — Added
`mcp_enable_temp_token_auth` client config flag to gate short-lived MCP
token minting for non-authenticated users (maximhq#3720)
- **Responses Stream in JSON Parser** — `jsonparser` plugin now handles
OpenAI Responses API streaming (`ResponsesStreamRequest`) in addition to
chat completions (maximhq#3749)
- **Session API Rework** — Logout now calls both the password-based
session logout and OAuth logout endpoints and resets all RTK Query cache
state (maximhq#3698)

## 🐞 Fixed

- **Streaming Latency for Observability** — Deferred root span
termination to the trace completer callback for streaming requests so
request latency is no longer inflated by header-flush time (maximhq#3762)
- **Stream Cancellation Race** — Set `BifrostContextKeyConnectionClosed`
before closing the stream and short-circuit `idleTimeoutReader.Read`
when the connection is already closed to avoid panics and hangs on
cancellation (maximhq#3733)
- **Bedrock Cache Points** — Strip cache points from Bedrock requests
for models that do not support prompt caching (e.g., GLM, Llama) to
avoid Converse API errors (maximhq#3754)
- **Bedrock Empty Text Blocks** — Skip empty/nil text blocks during
Bedrock response conversion to avoid invalid messages (maximhq#3747)
- **Bedrock Reasoning + Tools** — Preserve reasoning content blocks on
assistant turns that also contain tool calls in the Bedrock chat
converter (maximhq#3690)
- **Bedrock Search Content & Video** — Restored search content and video
parts that were being dropped from Bedrock-native passthrough requests
(maximhq#3729)
- **Structured Output Stop Reason** — Fixed an incorrect `tool_calls`
finish reason when structured output is combined with extended-thinking
tools (maximhq#3685)
- **Gemini Tool Schema Passthrough** — Forward full tool parameter
schemas via `parametersJsonSchema` instead of the lossy `parameters`
form; corrected tool response role to `user`; resolved structured output
+ tools conflict (maximhq#3761)
- **Anthropic Stop Reason & Tool Versions** — Normalized stop reason
mapping (`end_turn` to `stop`, `tool_use` to `tool_calls`, `max_tokens`
to `length`) and upgraded `text_editor_20250124`/`str_replace_editor` to
`text_editor_20250728` for computer-use tools (maximhq#3761)
- **Azure Endpoint Redaction** — Fixed a panic when
`AzureKeyConfig.Endpoint` is a literal value rather than an env
reference (maximhq#3761)
- **Auth Middleware Path Match** — Match temp-token auth middleware
whitelist against the request path only, not the full URI with query
parameters (maximhq#3737)
- **Governance Blocked Models UI** — Restored the missing Blocked Models
create/edit UI in the VK provider config sheet (maximhq#3750)
- **Logging Plugin Cleanup Drain** — Fixed a shutdown race where
`batchWriter` could drop in-flight log entries; `Cleanup` now drains
both the recovered batch and remaining queue within a 30-second budget
(maximhq#3717)
- **Model Rankings Empty Entries** — Excluded entries with empty `model`
values from model rankings matview queries so blank rows no longer
surface in the UI (maximhq#3758)
- **User Filter Duplicates** — Recreated `mv_filter_users` matview to
require non-empty `user_name`, eliminating duplicate filter dropdown
entries (maximhq#3764)
- **User Filter Display Name** — Use `user_name` instead of `user_id` as
the display label for users in logging filters (maximhq#3691)
- **Large Numeric ID Precision** — Preserve large numeric IDs in URL
search params by skipping JSON parsing for plain strings (maximhq#3692)

## 🔧 Refactors & Chores

- **Error Propagation for GetAvailable\* APIs** — `GetAvailable*`
methods on `LoggerPlugin`/`LogManager` now return wrapped errors instead
of silently logging and returning empty slices (maximhq#3759)
- **Governance Blocklist Matching** — Use `slices.Contains` for VK
blocked-model matching for clearer code with identical semantics (maximhq#3727)
- **Exported `ResolvePeriod`** — Renamed `resolvePeriod` to
`ResolvePeriod` so external packages can reuse the period parsing
(maximhq#3763)

## 📚 Docs

- **OTEL Env Var Documentation** — Documented `env.VAR_NAME` support for
`collector_url`, `metrics_endpoint`, and headers in OTEL/Prometheus
plugin docs
- **OTEL OSS Features & Examples** — Added OTEL documentation to the OSS
features list with usage examples (maximhq#3731)
- **Anthropic Auth Recommendation** — Recommend `ANTHROPIC_AUTH_TOKEN`
over `ANTHROPIC_CUSTOM_HEADERS` for Claude Code authentication (maximhq#3686)
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.

3 participants