Skip to content

v1.5.5 - #3771

Merged
akshaydeo merged 46 commits into
mainfrom
dev
May 26, 2026
Merged

v1.5.5#3771
akshaydeo merged 46 commits into
mainfrom
dev

Conversation

@akshaydeo

Copy link
Copy Markdown
Contributor

✨ Features

🐞 Fixed

🔧 Refactors & Chores

📚 Docs

Vaibhav701161 and others added 30 commits May 27, 2026 00:27
Adds blocked model support for virtual key provider configurations.

Provider keys already supported both allowed and blocked models, but virtual key provider configs only supported allowed models. This PR adds the missing blocked model flow for virtual keys, so specific models can be denied at the VK provider-config level.

* Added `blacklisted_models` to virtual key provider configs.
* Added a configstore migration for the new `blacklisted_models` column.
* Updated virtual key create/update handlers to validate, persist, and return blocked models.
* Added governance checks to reject virtual key requests when the requested model is blocked.
* Made blocked models take priority over allowed models.
* Updated virtual key model filtering to respect blocked models.
* Added `Blocked Models` UI under provider configurations in the virtual key create/edit sheet.
* Added blocked model display in the virtual key details sheet.
* Updated frontend governance types for virtual key provider configs.

This follows the existing provider-key blocked model behavior instead of introducing a separate flow.

Behavior:

* Empty `blacklisted_models` means no models are blocked.
* `["*"]` blocks all models for that VK provider config.
* If the same model exists in both allowed and blocked models, the blocked list wins.

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

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

Local UI flow:

Start Bifrost locally with embedded UI on port `9090`, then open:

`http://localhost:9090/workspace/governance/virtual-keys`

Steps tested:

1. Open Virtual Keys.
2. Create or edit a virtual key.
3. Expand a provider config.
4. Confirm `Blocked Models` appears below `Allowed Models`.
5. Select a blocked model and save.
6. Reopen the virtual key and confirm the blocked model is still shown.
7. Refresh the page and confirm the value persists.

Runtime validation:

1. Configure a VK provider config with `allowed_models: ["*"]` and `blacklisted_models: ["<model-to-block>"]`.
2. Send a request through that virtual key using the blocked model.

Expected: request is rejected.

3. Send a request through the same virtual key using a model not present in `blacklisted_models`.

Expected: request succeeds.

4. Configure the same model in both `allowed_models` and `blacklisted_models`.

Expected: request is rejected because blocked models take priority.

5. Configure `blacklisted_models: []`.

Expected: existing virtual key behavior remains unchanged.

Sanity checks:

`go test ./framework/configstore/... ./plugins/governance/... ./transports/bifrost-http/handlers/...`

UI check:

`cd ui && pnpm build`

Added a recording showing the new `Blocked Models` field in the virtual key provider configuration flow.

https://github.com/user-attachments/assets/64efca01-0366-491c-b9e7-95dfa08eb0bc

* [ ] Yes
* [x] No

BF-896

This change improves virtual-key governance by allowing specific models to be denied for a VK provider config.

No provider secrets, customer keys, auth tokens, or PII are exposed or stored by this change. Existing provider-key behavior is unchanged.

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

Improves E2E test stability for virtual key editing by handling a budget reset dialog that can appear after saving, and marks a known flaky bulk-rotate test as `fixme` until the underlying UI bug is resolved.

## Changes

- Added `preserveBudgetUsageIfPrompted()` helper that detects the `vk-budget-reset-dialog` and clicks the preserve button if it appears after saving a virtual key. This prevents test failures caused by an unexpected dialog interrupting the save flow.
- Marked `should bulk rotate selected virtual keys only` as `test.fixme` due to a UI bug where the checkbox selection state resets when the search input filters out a previously selected row. When `bulkRotateVirtualKeys` searches by name to select each key, the first key becomes deselected as the search narrows to the second, resulting in only the last key being rotated.

## Type of change

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

## Affected areas

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

## How to test

Run the virtual keys E2E suite and confirm no failures occur on the save flow due to the budget reset dialog:

```sh
cd tests/e2e
npx playwright test features/virtual-keys/virtual-keys.spec.ts
```

The bulk rotate test will be skipped (`fixme`) and should not cause CI failures.

## Screenshots/Recordings

N/A

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

N/A

## Security considerations

None.

## 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
## Summary

Consolidates the Per User OAuth Postman coverage probes to align with the updated API surface, replacing the old register/authorize/token/consent/upstream endpoints with the new flow-based endpoints (`/api/oauth/per-user/flows/:flowId` and `/api/oauth/per-user/flows/:flowId/start`).

## Changes

- Removed coverage probe requests for `Per User OAuth Register`, `Per User OAuth Authorize`, `Per User OAuth Token`, `Per User OAuth Upstream Authorize`, `Per User Consent VK`, `Per User Consent User ID`, `Per User Consent Skip`, and `Per User Consent Submit`
- Added coverage probe requests for `Per User OAuth Flow Detail` (`GET /api/oauth/per-user/flows/coverage-probe-flow`) and `Per User OAuth Flow Start` (`GET /api/oauth/per-user/flows/coverage-probe-flow/start`)
- Added `OAuth Callback (Coverage Probe)` to the raw text shapes validation map, replacing the three previously tracked OAuth probe entries

## Type of change

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

## Affected areas

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

## How to test

Run the updated Postman collection against a running Bifrost instance and verify that the new flow-based coverage probe requests return expected responses and that the response structure validation passes.

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

## Security considerations

No security implications. These are test coverage probes only.

## 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
…`values.yaml` (#3612)

## Summary

Adds inline documentation comments to the Bifrost Helm chart values for timeout configuration fields, clarifying the units and defaults for rule and provider execution timeouts.

## Changes

- Updated the `timeout` comment for guardrail rules to specify seconds as the unit and note the default value of 60 seconds (previously showed `1000`, which was ambiguous in terms of units)
- Added a `timeout` comment for guardrail providers to document the default value of 30 seconds

## Type of change

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

## Affected areas

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

## How to test

Deploy or lint the Helm chart to confirm the values file renders correctly.

```sh
helm lint helm-charts/bifrost
```

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

## Security considerations

None.

## Checklist

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

## Summary

Adds optional gRPC port support to the Bifrost Helm chart, allowing the cluster to expose a gRPC endpoint when configured.

## Changes

- Added a conditional gRPC container port (`TCP`) to the deployment template, rendered only when `bifrost.cluster.grpc` is defined in values
- Added a corresponding conditional gRPC service port to the service template, targeting the named `grpc` port

## Type of change

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

## Affected areas

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

## How to test

Set the following in your Helm values and confirm the deployment and service include the gRPC port:

```yaml
bifrost:
  cluster:
    grpc:
      port: 50051
```

```sh
helm template ./helm-charts/bifrost | grep -A 4 grpc
```

Expected output should include the `grpc` port entry in both the deployment container ports and the service spec.

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

## Security considerations

The gRPC port is only exposed when explicitly configured. No authentication or TLS is configured at the Helm chart level; ensure appropriate network policies or ingress controls are in place when enabling this port.

## 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
…lag in Redis health checks (#3618)

## Summary

Simplifies Redis health check commands by leveraging the `REDISCLI_AUTH` environment variable for automatic authentication, removing the need for conditional `-a "$REDIS_PASSWORD"` flags in liveness and readiness probes.

## Changes

- Added `REDISCLI_AUTH` environment variable to the Redis container, sourced from the same secret as `REDIS_PASSWORD`. When set, `redis-cli` automatically uses this variable for authentication without requiring the `-a` flag.
- Replaced the conditional `redis-cli` probe commands (which branched on whether auth was enabled) with a single unconditional `redis-cli ping`. When auth is enabled, `REDISCLI_AUTH` handles it transparently; when auth is disabled, the variable is simply not set.

## Type of change

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

## Affected areas

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

## How to test

Deploy the Bifrost Helm chart with Redis auth enabled and verify that the liveness and readiness probes succeed without errors:

```sh
helm upgrade --install bifrost ./helm-charts/bifrost \
  --set vectorStore.redis.auth.enabled=true \
  --set vectorStore.redis.auth.password=<your-password>

kubectl describe pod <redis-pod> | grep -A5 "Liveness\|Readiness"
```

Expected: probes report successful `ping` responses and the pod reaches `Running` state.

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

## Security considerations

`REDISCLI_AUTH` is sourced from the same Kubernetes secret as `REDIS_PASSWORD`, so there is no change in how the password is stored or exposed. This approach avoids passing the password as a visible command-line argument, which is a minor security improvement.

## 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
## Summary

The Weaviate PersistentVolumeClaim (PVC) in the Bifrost Helm chart was being created unconditionally whenever Weaviate was enabled and not using an external instance. This change gates PVC creation behind the `persistence.enabled` flag, preventing unnecessary PVC creation when persistence is not desired.

## Changes

- Added `.Values.vectorStore.weaviate.persistence.enabled` as an additional condition to the PVC template, so the PVC is only created when persistence is explicitly enabled

## Type of change

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

## Affected areas

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

## How to test

Deploy the Bifrost Helm chart with Weaviate enabled and `vectorStore.weaviate.persistence.enabled` set to `false`. Verify that no PVC is created. Then set it to `true` and confirm the PVC is created as expected.

```sh
helm template bifrost ./helm-charts/bifrost \
  --set vectorStore.enabled=true \
  --set vectorStore.type=weaviate \
  --set vectorStore.weaviate.enabled=true \
  --set vectorStore.weaviate.external.enabled=false \
  --set vectorStore.weaviate.persistence.enabled=false \
  | grep -i PersistentVolumeClaim
```

Expected: no PVC output when `persistence.enabled=false`.

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

## Security considerations

None.

## 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
…chart NOTES.txt (#3620)

## Summary

Adds runtime warnings to the Bifrost Helm chart's `NOTES.txt` when default passwords are detected for PostgreSQL or Redis, alerting operators before they deploy to production with insecure credentials.

## Changes

- Added a warning message displayed post-install if PostgreSQL is still using the default password `"bifrost_password"`, prompting users to set `postgresql.auth.password` to a strong value.
- Added a warning message displayed post-install if Redis is still using the default password `"redis_password"`, prompting users to set `vectorStore.redis.auth.password` to a strong value.

## Type of change

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

## Affected areas

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

## How to test

Deploy the Helm chart with default values and verify the warnings appear in the install notes:

```sh
helm install bifrost ./helm-charts/bifrost
# Expected: warnings about default PostgreSQL and Redis passwords appear in NOTES output

helm install bifrost ./helm-charts/bifrost \
  --set postgresql.auth.password=myStrongPass \
  --set vectorStore.redis.auth.password=myStrongRedisPass
# Expected: no warnings appear in NOTES output
```

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

## Security considerations

This change surfaces a security risk to operators who leave default credentials in place when deploying to production. It does not change any authentication logic but ensures users are explicitly warned about insecure default passwords for PostgreSQL and Redis.

## 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
…eterministic Helm rendering (#3621)

## Summary

Helm chart templates that iterate over maps (provider secrets and Weaviate env vars) were producing non-deterministic ordering, which causes unnecessary diff noise and potential reconciliation churn in GitOps workflows. This change enforces alphabetical ordering when ranging over these maps.

## Changes

- Provider secrets in `deployment.yaml` and `stateful.yaml` now iterate over `providerSecrets` keys sorted alphabetically before looking up each value
- Weaviate environment variables in `weaviate-deployment.yaml` now iterate over `env` keys sorted alphabetically before looking up each value

## Type of change

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

## Affected areas

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

## How to test

Render the Helm templates and verify that environment variables appear in a consistent, alphabetically sorted order across multiple renders:

```sh
helm template bifrost ./helm-charts/bifrost --values your-values.yaml
```

Run the render multiple times and confirm the output is identical each time, with env vars appearing in alphabetical order.

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

## Security considerations

No security implications. This change only affects the ordering of environment variable declarations in rendered Kubernetes manifests.

## 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
…t Helm chart (#3689)

## Summary

Adds support for using a pre-existing Kubernetes Secret to supply the PostgreSQL password when using the bundled (non-external) PostgreSQL deployment. Previously, only the `postgresql.auth.password` plaintext value or an external secret was supported. This allows users to manage the PostgreSQL credential through their own secret management tooling without exposing the password in `values.yaml`.

## Changes

- Added `postgresql.auth.existingSecret` and `postgresql.auth.passwordKey` fields to `values.yaml`, allowing users to reference a pre-existing Kubernetes Secret for the bundled PostgreSQL password.
- The bundled PostgreSQL pod (`postgresql-deployment.yaml`) now reads `POSTGRES_PASSWORD` from the specified existing secret and key, falling back to the auto-generated secret and `password` key when not set.
- The `deployment.yaml` and `stateful.yaml` templates now inject `BIFROST_POSTGRES_PASSWORD` from the existing secret when `postgresql.auth.existingSecret` is set and external PostgreSQL is not enabled.
- The `secrets.yaml` template skips creating the auto-generated PostgreSQL secret when `postgresql.auth.existingSecret` is provided, preventing conflicts.
- The `_helpers.tpl` password helper returns the `env.BIFROST_POSTGRES_PASSWORD` sentinel (indicating the value should be read from the environment) when an existing secret is configured, rather than attempting to inline the password.

## Type of change

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

## Affected areas

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

## How to test

Set the following in your `values.yaml` or via `--set` flags and confirm the deployment reads the password from the referenced secret rather than generating one:

```sh
# Create a secret manually
kubectl create secret generic my-pg-secret --from-literal=password=mysecretpassword

# Install/upgrade the chart referencing the existing secret
helm upgrade --install bifrost ./helm-charts/bifrost \
  --set postgresql.auth.existingSecret=my-pg-secret \
  --set postgresql.auth.passwordKey=password

# Verify the auto-generated secret is NOT created
kubectl get secret bifrost-postgresql  # should return NotFound

# Verify the PostgreSQL pod and bifrost deployment reference my-pg-secret
kubectl get deployment -o yaml | grep -A5 "POSTGRES_PASSWORD"
kubectl get deployment -o yaml | grep -A5 "BIFROST_POSTGRES_PASSWORD"
```

New configuration values:

| Key | Default | Description |
|-----|---------|-------------|
| `postgresql.auth.existingSecret` | `""` | Name of an existing Kubernetes Secret containing the PostgreSQL password. Takes precedence over `postgresql.auth.password`. |
| `postgresql.auth.passwordKey` | `"password"` | Key within the existing secret that holds the password value. |

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

## Security considerations

This change improves the security posture by allowing operators to avoid storing the PostgreSQL password as plaintext in `values.yaml`. When `existingSecret` is set, no Kubernetes Secret is generated by the chart, and both the PostgreSQL pod and the Bifrost application read the credential directly from the referenced secret at runtime.

## 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
…sql deployment (#3694)

## Summary

Moves the hardcoded `fsGroup: 999` pod security context for the PostgreSQL deployment into a configurable `values.yaml` field, and adds support for a container-level security context on the PostgreSQL primary container.

## Changes

- Replaced the hardcoded `fsGroup: 999` in the PostgreSQL deployment pod spec with a templated reference to `.Values.postgresql.primary.podSecurityContext`, defaulting to `fsGroup: 999` in `values.yaml`
- Added an optional `containerSecurityContext` field under `postgresql.primary` (defaults to `{}`) that, when set, is applied to the PostgreSQL container's `securityContext`

## Type of change

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

## Affected areas

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

## How to test

Deploy the Bifrost Helm chart and verify the PostgreSQL pod launches with the expected security context. Override `postgresql.primary.podSecurityContext` and `postgresql.primary.containerSecurityContext` in a custom `values.yaml` to confirm they are applied correctly.

```sh
helm template bifrost ./helm-charts/bifrost -f custom-values.yaml | grep -A 10 securityContext
```

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

## Security considerations

This change allows operators to customize the security context for the PostgreSQL pod and container, enabling stricter security policies (e.g., running as a non-root user, dropping capabilities) without requiring chart modifications.

## 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
…talog URL, and bug fixes (#3623)

Bumps the Bifrost Helm chart to version `2.1.18`, introducing new configuration options, fixing several rendering and runtime bugs, and updating the Helm index accordingly.

- Added `bifrost.featureFlags` map to `values.yaml` and `_helpers.tpl`, rendering into `feature_flags.flags` in the generated config JSON. Each entry accepts a literal boolean or `"env.NAME"` string.
- Added `bifrost.modelCatalog.modelParametersUrl` to allow operators to override the URL Bifrost uses to fetch model parameter definitions.
- Fixed the Deployment not exposing the cluster gRPC container port and `service.yaml` missing the gRPC service port, aligning both with StatefulSet/headless service behaviour.
- Fixed Weaviate PVC rendering when `vectorStore.weaviate.persistence.enabled=false`; PVC is now gated on persistence being enabled.
- Fixed Redis liveness/readiness probes passing the password via `-a` flag in process args; switched to the `REDISCLI_AUTH` environment variable to avoid credential exposure.
- Fixed nondeterministic env var ordering for `providerSecrets` and `weaviate.env` map iterations by sorting keys with `sortAlpha`.
- Corrected guardrail `timeout` example values in `values.yaml` (provider default `30s`, rule default `60s`).

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

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

```sh
helm lint helm-charts/bifrost

helm template bifrost helm-charts/bifrost \
  --set bifrost.featureFlags.someFlag=true \
  --set bifrost.modelCatalog.modelParametersUrl="https://example.com/params.json"

helm template bifrost helm-charts/bifrost | grep -A5 "grpc"

helm template bifrost helm-charts/bifrost \
  --set vectorStore.weaviate.persistence.enabled=false | grep -c "PersistentVolumeClaim"

helm template bifrost helm-charts/bifrost | grep "REDISCLI_AUTH"
```

- [ ] Yes
- [x] No

N/A

The Redis probe fix removes the password from process arguments, preventing credential leakage via `/proc` or process listings. No other security-sensitive changes.

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

When constructing Bedrock messages for assistant turns that include reasoning details alongside tool calls, the reasoning content blocks were being appended after text and tool-use blocks. Bedrock requires reasoning blocks to appear first in the content array, so this ordering caused malformed requests during multi-turn conversations involving extended thinking and tool use.

## Changes

- Reordered content block assembly in `convertMessage` so that reasoning blocks are always prepended before text/image content and tool-use blocks.
- Added a test case `AssistantMessage_WithReasoningAndToolCalls_ReasoningComesFirst` that verifies the first content block is a reasoning block and that tool-use blocks follow it.

## Type of change

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

## Affected areas

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

## How to test

```sh
go test ./core/providers/bedrock/... -run TestMultiTurnReasoningContentPassthrough
```

The new subtest `AssistantMessage_WithReasoningAndToolCalls_ReasoningComesFirst` should pass, confirming that the reasoning block is the first element in the assembled content array and that a tool-use block appears after it.

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

## Security considerations

No security implications.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
### TL;DR

Fix incorrect `tool_calls` finish reason being returned when structured output (response format) is used with extended thinking enabled on Anthropic and Bedrock providers.

### What changed?

- When extended thinking (reasoning) is active on Anthropic, forcing `tool_choice` to the structured output tool is now skipped, since Anthropic rejects that combination. The tool is still appended and the model may call it voluntarily.
- The finish reason override from `tool_calls` → `stop` for structured output is now gated on whether the structured output tool block was **actually consumed into text content**, rather than just whether a structured output tool name was configured. This prevents incorrectly overriding the finish reason in cases where the tool was never invoked.
- This fix is applied consistently across the chat completion (non-streaming and streaming), responses (non-streaming and streaming), and Bedrock chat completion paths.
- A `UsedStructuredOutputTool` / `consumedStructuredOutput` flag is tracked per-response and per-stream to record when the SO tool block is folded back into text content, and the finish reason override only fires when that flag is set.
- For the non-streaming responses path, the override logic inspects the response content blocks directly to confirm no real (non-SO) tool calls are present before remapping the stop reason.

### How to test?

1. Send a chat completion or responses request with a `response_format` (structured output) and extended thinking/reasoning enabled against an Anthropic model. Verify the finish reason is `stop` and no API rejection occurs due to conflicting `tool_choice`.
2. Send a request with `response_format` but **without** extended thinking. Verify the finish reason is still `stop` and the structured output JSON is returned as content.
3. Send a request that uses both real tool calls and structured output simultaneously. Verify the finish reason correctly reflects `tool_calls` for the real tools.
4. Repeat the above for streaming and non-streaming variants, and for the Bedrock provider.

### Why make this change?

Anthropic rejects requests that combine extended thinking with a forced `tool_choice`, causing failures when structured output was requested alongside reasoning. Additionally, the previous finish reason override was too broad — it fired whenever a structured output tool name was present in context, even if the tool was never actually used, which could mask legitimate `tool_calls` finish reasons in mixed-tool scenarios.
…3729)

## Summary

Fixes a regression ([#3537](#3537)) where Bedrock-native passthrough requests containing `toolResult.content[].searchResult` or `toolResult.content[].video` blocks were silently dropped during the `ToBifrostResponsesRequest` → `ToBedrockResponsesRequest` round-trip. The outbound request would carry an empty `{"text": ""}` block instead of the original content.

## Changes

- Added `BedrockSearchResultBlock`, `BedrockSearchResultContent`, `BedrockCitationsConfig`, `BedrockVideoBlock`, `BedrockVideoSource`, and `BedrockS3Location` types to `types.go` so these AWS Converse API structures are recognized during JSON unmarshal.
- Added `SearchResult` and `Video` fields to `BedrockContentBlock` so the types are reachable from content blocks.
- Introduced a sentinel-envelope mechanism (`encodeBedrockToolResultEnvelope` / `decodeBedrockToolResultEnvelope`) in `utils.go`. When a `toolResult.content` array contains blocks that Bifrost's intermediate format cannot model natively (e.g. `searchResult`, `video`), the full content array is serialized into a keyed JSON envelope and stored as the tool output string. On the return leg, the envelope is detected and decoded back into the original `BedrockContentBlock` slice before being forwarded to Bedrock.
- Updated `ConvertBifrostMessagesToBedrockMessages` in `responses.go` to check for and decode the sentinel envelope before falling through to `tryParseJSONIntoContentBlock`.
- Updated `convertSingleBedrockMessageToBifrostMessages` in `responses.go` to detect unrepresentable blocks and encode them into the sentinel envelope instead of attempting lossy text extraction.
- Added regression tests `TestBedrockSearchResultToolResultRoundTrip` and `TestBedrockVideoToolResultRoundTrip` that exercise the full `ToBifrostResponsesRequest` → `ToBedrockResponsesRequest` round-trip and assert all fields survive intact.

## Type of change

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

## Affected areas

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

## How to test

```sh
go test ./core/providers/bedrock/... -run "TestBedrockSearchResultToolResultRoundTrip|TestBedrockVideoToolResultRoundTrip" -v
```

Both tests should pass. Prior to this fix, both would fail with `expected toolResult.content[].searchResult to round-trip; got nil` and `expected toolResult.content[].video to round-trip; got nil` respectively.

To run the full Bedrock provider test suite:

```sh
go test ./core/providers/bedrock/...
```

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

Closes #3537

## Security considerations

The sentinel envelope is a self-contained JSON object keyed by a fixed internal string. It is only decoded when the key is present and the envelope contains exactly one key, preventing accidental misinterpretation of user-supplied JSON payloads. No secrets or PII are introduced by this mechanism.

## Checklist

- [x] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [x] I verified the CI pipeline passes locally if applicable
## 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
## Summary

Adds a new top-level OpenTelemetry feature page and documents that caller-supplied `x-bf-eh-*` headers (and direct-allowlist headers) are surfaced as `gen_ai.request.extra_header.<name>` span attributes on the `llm.call` span, enabling trace filtering and correlation by session, tenant, or correlation ID without additional instrumentation.

## Changes

- Added `docs/features/otel.mdx` — a new top-level OTel overview page covering captured attributes, dynamic attribute injection via `x-bf-eh-*` headers, and links to the full integration guide and related references.
- Registered `features/otel` in `docs/docs.json` so the new page appears in the navigation.
- Updated `docs/features/observability/otel.mdx` to document the "Caller-Supplied Headers" behaviour under the captured data section, with an example showing `x-bf-eh-session-id` producing `gen_ai.request.extra_header.session-id` on the span.
- Updated `docs/providers/request-options.mdx` to note that forwarded extra headers are also attached to the OTel span, and added a concrete `curl` example showing the dual effect on both the provider request and the `llm.call` span.

## Type of change

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

## Affected areas

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

## How to test

Navigate to the docs site and verify:

1. The new **OpenTelemetry** page appears in the sidebar under Features.
2. The `features/otel` page renders correctly with the attribute table, dynamic injection section, and the `curl` example.
3. The `features/observability/otel` page shows the new **Caller-Supplied Headers** subsection under the captured data section.
4. The `providers/request-options` page shows the updated extra-headers description and the session-ID example with the expected OTel outcome.

## Breaking changes

- [ ] Yes
- [x] No

## Security considerations

The documentation explicitly notes that the same security denylist and header filter configuration that gates provider forwarding also gates which headers appear as span attributes — no additional headers are exposed beyond what is already forwarded to the upstream provider.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [ ] I added/updated tests where appropriate
- [x] I updated documentation where needed
- [ ] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
…nventions (#3732)

## Summary

Aligns Bifrost's tracing attribute emissions with the OpenTelemetry GenAI semantic conventions spec. Bifrost-internal concepts (routing, governance, retry counters, etc.) were previously emitted under the `gen_ai.*` namespace, which pollutes the OTel spec namespace. This PR introduces a parallel `bifrost.*` namespace as the canonical home for those attributes and adds spec-compliant mappings for provider names, operation names, token usage keys, tool execution attributes, and more.

Legacy `gen_ai.*` emissions are retained in parallel (tagged `// legacy:`) to avoid breaking existing dashboards, with a clear migration path to drop them once consumers have moved over.

## Changes

- Added `core/schemas/otelconv.go` with `OTelOperationName` and `OTelProviderName` helpers that map Bifrost-internal types to OTel GenAI spec values (e.g. `Bedrock` → `aws.bedrock`, `ChatCompletionRequest` → `chat`).
- Introduced a full set of `bifrost.*` attribute constants (`AttrBifrostProviderName`, `AttrBifrostVirtualKeyID`, `AttrBifrostRetries`, etc.) as the canonical namespace for Bifrost-internal span attributes.
- Added new OTel spec-aligned attribute constants: `AttrOperationName`, `AttrChoiceCount`, `AttrEmbeddingsDimensionCount`, `AttrEncodingFormats`, `AttrUsageCacheReadInputTokens`, `AttrUsageCacheCreationInputTokens`, `AttrErrorTypeSpec`, and tool execution attributes (`AttrToolName`, `AttrToolCallID`, `AttrToolCallArguments`, `AttrToolCallResult`, `AttrToolType`).
- Updated `executeRequestWithRetries` in `bifrost.go` to emit both legacy and canonical attributes, use `OTelProviderName`/`OTelOperationName`, and format the root span name as `"{operation} {model}"` per the GenAI semconv.
- Updated `PopulateRequestAttributes`, `PopulateErrorAttributes`, `PopulateContextAttributes`, and all response/request attribute helpers in `llmspan.go` to emit both legacy and spec-aligned keys in parallel.
- Fixed `AttrStopSequences` to emit a proper `[]string` slice instead of a comma-joined string; the joined form is preserved under `AttrBifrostStopSequencesJoined` for back-compat.
- Added `gen_ai.usage.input_tokens` / `gen_ai.usage.output_tokens` alongside the deprecated `prompt_tokens` / `completion_tokens` keys across chat, text completion, and embedding response helpers.
- Added OTel GenAI tool execution attributes to MCP execute-tool spans in `pluginpipeline.go`, including tool name, call ID, arguments, and result (captured via named returns).
- Updated the OTel plugin converter to emit `bifrost.request.id` alongside the legacy `gen_ai.request_id`.

## Type of change

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

## Affected areas

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

## How to test

```sh
go test ./core/... ./framework/tracing/... ./plugins/otel/...
```

Validate that spans emitted by `executeRequestWithRetries` contain both the legacy `gen_ai.*` attributes and the new `bifrost.*` / spec-aligned counterparts. Confirm MCP execute-tool spans include `gen_ai.tool.name`, `gen_ai.tool.call.id`, `gen_ai.tool.call.arguments`, and `gen_ai.tool.call.result`. Verify `gen_ai.provider.name` now emits the spec-canonical form (e.g. `aws.bedrock`) while `bifrost.provider.name` retains the short Bifrost name.

## Breaking changes

- [ ] Yes
- [x] No

All previously emitted attributes are still present. New attributes are additive. The only behavioral difference is that `gen_ai.provider.name` now emits the OTel canonical form rather than the Bifrost short name; the short name is preserved under `bifrost.provider.name`.

## Security considerations

No auth, secrets, PII, or sandboxing changes. Span attributes may now include tool call arguments and results on MCP tool spans — operators should ensure their observability backend's data retention policies are appropriate for that data.

## 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
…3733)

## Summary

Fixes a race condition in `SetupStreamCancellation` where `BifrostContextKeyConnectionClosed` was set *after* calling `Close()` on the body stream. Because `Close()` immediately unblocks any in-progress `Read` (which may panic due to a force-closed connection), the recover block in `idleTimeoutReader.Read` could run before the flag was set — causing it to re-panic instead of returning `ErrStreamClosed`.

Additionally, `idleTimeoutReader.Read` was returning `(0, nil)` when the connection was already marked closed, which is incorrect. It now returns the appropriate closed-stream error via `closedReadError()`.

## Changes

- `SetupStreamCancellation` now sets `BifrostContextKeyConnectionClosed` **before** calling `Close()` or `CloseWithError()` in all four call sites, eliminating the race window where a panicking `Read` could recover before the flag was visible.
- `idleTimeoutReader.Read` now returns `r.closedReadError()` instead of `(0, nil)` when the connection is already closed, ensuring callers receive a meaningful error rather than a silent empty read.
- Added `syncedPanicBody`, a deterministic test helper that reproduces the exact race: `Close()` triggers a panic in `Read` and then blocks, holding `SetupStreamCancellation` inside `Close()` so the flag is guaranteed to be unset when the recover block runs under the unfixed code.
- Added `TestSetupStreamCancellation_NoPanicOnCancelledContext` which fails against the unfixed code and passes after the fix.

## Type of change

- [x] Bug fix

## Affected areas

- [x] Core (Go)
- [x] Transports (HTTP)

## How to test

```sh
go test ./core/providers/utils/... -race -count=1 -run TestSetupStreamCancellation_NoPanicOnCancelledContext
go test ./core/providers/utils/... -race -count=1
```

The new test should pass without any detected data races or re-panics.

## Breaking changes

- [x] No

## Security considerations

None. This is a stability fix for stream teardown on context cancellation.

## Checklist

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

Replaces `nil` logger arguments with a `noopLogger{}` instance in Bedrock transport tests to ensure tests use a valid logger implementation rather than a nil value.

## Changes

- Replaced `nil` with `noopLogger{}` when calling `NewBedrockProvider` across all Bedrock transport tests to provide a proper no-op logger, avoiding potential nil pointer dereferences during test execution.

## Type of change

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

## Affected areas

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

## How to test

```sh
go test ./core/providers/bedrock/...
```

All Bedrock transport tests should pass without nil pointer panics or logger-related errors.

## Screenshots/Recordings

N/A

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

N/A

## Security considerations

No security implications. This is a test-only change that swaps a nil logger for a no-op implementation.

## Checklist

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

Refactors VK blocked-model matching to use `slices.Contains`, as requested in review.

This keeps the existing behavior unchanged while making the matching logic cleaner. Bare and provider-prefixed model names are still treated as equivalent, so entries like `mistral:latest` and `ollama/mistral:latest` continue to match correctly. Wildcard blocklists still block all models.

## Changes

* Added `blockedModelCandidates()` to build normalized match candidates for a model string.

  * Includes the lowercased raw model name.
  * Includes the lowercased bare model name after provider-prefix parsing.
* Updated `isModelBlockedByList()` to use `slices.Contains` for comparing normalized model forms.
* Preserved existing blocklist behavior for:

  * bare model vs bare request
  * prefixed blocklist entry vs bare request
  * bare blocklist entry vs prefixed request
  * prefixed model vs prefixed request
  * wildcard `["*"]`

Design decision:

* This is only a small internal refactor of the VK blocklist helper.
* No runtime behavior is intentionally changed.
* Provider-key behavior is unchanged.

## Type of change

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

## Affected areas

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

## How to test

Sanity checks:

```sh
go test ./plugins/governance/...
go build -o ./tmp/bifrost-http ./transports/bifrost-http
```

Verified local Ollama E2E behavior:

* `["mistral:latest"]` + `mistral:latest` → `403 model_blocked`
* `["ollama/mistral:latest"]` + `mistral:latest` → `403 model_blocked`
* `["mistral:latest"]` + `ollama/mistral:latest` → `403 model_blocked`
* `["ollama/mistral:latest"]` + `ollama/mistral:latest` → `403 model_blocked`
* Different allowed model → `200 OK`
* Empty blocklist → `200 OK`
* Wildcard blocklist `["*"]` → all tested models blocked
* Same model in allowlist and blocklist → `403 model_blocked`

## Screenshots/Recordings

Not applicable. This PR only refactors backend governance matching logic.

## Breaking changes

* [ ] Yes
* [x] No

## Related issues

Follow-up to #3718

## Security considerations

This keeps VK blocked-model enforcement intact for both bare and provider-prefixed model strings.

No secrets, auth tokens, provider keys, or PII are exposed or stored by this change. Provider-key behavior is unchanged.

## Checklist

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

Adds a new `mcp_enable_temp_token_auth` client config flag that gates whether Bifrost mints and accepts scoped short-lived temp tokens for MCP per-user OAuth authorization pages. Previously, temp-token auth was implicitly enabled whenever the `tempTokens` service was wired up. This change makes it an explicit opt-in, defaulting to `false`, so deployments must consciously enable it.

## Changes

- Added `MCPEnableTempTokenAuth` bool field to `ClientConfig`, `TableClientConfig`, and all relevant migration/serialization paths, with a database migration to add the column.
- `OAuth2Provider.InitiateUserOAuthFlow` now checks both that the `tempTokens` service is non-nil **and** that `MCPEnableTempTokenAuth` is `true` in client config before minting a temp token into the auth-page URL fragment.
- `AuthMiddleware` gains a `tempTokensEnabled` atomic bool, initialized and updated via `ReloadClientConfigFromConfigStore`, so the `X-Bifrost-Temp-Token` fallback path in `tryTempTokenOrUnauthorized` is also gated by the same flag at runtime.
- Added `UpdateTempTokenAuthEnabled` method on `AuthMiddleware` and wired it into `ReloadClientConfigFromConfigStore` so config changes take effect without a restart.
- UI MCP settings view exposes a new "Allow Temp Token Auth Links" toggle, visible only when SSO/SCIM is enabled (`IS_ENTERPRISE && authType === "sso"`), with a descriptive label explaining the security trade-off.
- Helm chart `_helpers.tpl`, `values.yaml`, and `values.schema.json` updated to expose `mcpEnableTempTokenAuth`.
- `transports/config.schema.json` and the schema test updated to include `mcp_enable_temp_token_auth`.
- Added `TestMCPTempTokenAuthEnabled` unit test covering the three states: no config, config with flag false, config with flag true.

## Type of change

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

## Affected areas

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

## How to test

```sh
# Core/Transports
go test ./framework/configstore/... ./framework/oauth2/... ./transports/bifrost-http/...

# UI
cd ui
pnpm i
pnpm build
```

**Manual validation:**

1. Start Bifrost with `mcp_enable_temp_token_auth: false` (default). Initiate a per-user MCP OAuth flow and confirm the returned auth-page URL contains **no** `#` fragment.
2. Set `mcp_enable_temp_token_auth: true`. Repeat the flow and confirm the URL contains a `#mcp_auth=<token>` fragment.
3. Confirm that after the flow completes or expires, the temp token is cleaned up and the fragment link no longer works.
4. In the UI (enterprise SSO deployment), navigate to MCP settings and verify the "Allow Temp Token Auth Links" toggle appears and persists correctly.

**New config field:**

| Field | Type | Default | Description |
|---|---|---|---|
| `mcp_enable_temp_token_auth` | `boolean` | `false` | When true, Bifrost mints and accepts scoped temp tokens for MCP per-user OAuth auth pages |

## Breaking changes

- [x] No

The flag defaults to `false`, preserving existing behavior. Deployments that previously relied on temp-token auth being implicitly active (when the service was wired) must explicitly set `mcp_enable_temp_token_auth: true`.

## Security considerations

Temp tokens embedded in URL fragments grant unauthenticated access to a specific MCP OAuth flow page for the lifetime of the flow. Keeping this opt-in (`false` by default) ensures operators consciously accept this trade-off. The tokens are scoped to a single `session_id` resource and are deleted on flow completion or expiry. The feature is surfaced in the UI only for SSO-enabled enterprise deployments where per-user OAuth flows are most relevant.

## Checklist

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

Extends the `/api/is-auth-enabled` endpoint contract to expose `has_valid_token` and `auth_type` fields alongside the existing `is_auth_enabled` field, and adds E2E test coverage for the new `mcp_enable_temp_token_auth` config flag.

## Changes

- Updated the `Check If Auth Enabled` response field assertion to include `has_valid_token` and `auth_type` in addition to `is_auth_enabled`.
- Added a test script to the `is-auth-enabled` request that validates:
  - `has_valid_token` is a boolean
  - `auth_type` is one of `'none'`, `'password'`, or `'sso'`
  - When auth is disabled, `auth_type` is `'none'` and `has_valid_token` is `false`
  - When auth is enabled, `auth_type` is not `'none'`
- Added two new Postman requests under the config section:
  - **Update Config (Enable Temp Token Auth)** — PUTs `mcp_enable_temp_token_auth: true` to `/api/config`
  - **Get Config (Verify Temp Token Auth)** — GETs `/api/config` and asserts `mcp_enable_temp_token_auth` round-trips as `true`

## Type of change

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

## Affected areas

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

## How to test

Run the updated Postman collection against a running Bifrost instance:

```sh
newman run tests/e2e/api/collections/bifrost-api-management.postman_collection.json \
  --env-var base_url=http://localhost:<port>
```

Expected outcomes:
- `Check If Auth Enabled` response includes `is_auth_enabled`, `has_valid_token`, and `auth_type`
- `auth_type` is one of `none`, `password`, or `sso`
- `mcp_enable_temp_token_auth` can be set to `true` via PUT and is returned as `true` on subsequent GET

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

## Security considerations

The `has_valid_token` and `auth_type` fields are exposed on the `is-auth-enabled` endpoint. This endpoint is intended to be publicly accessible for client bootstrapping. Care should be taken to ensure no sensitive session or token details are leaked beyond the boolean and enum values validated here.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [x] 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
…al key quota query and response (#3721)

## Summary

Provider-level budget and rate limit configurations were not being included when fetching virtual key quota data, meaning the `getVirtualKeyQuota` endpoint returned incomplete governance information. This PR extends the lean quota query to also load `ProviderConfigs` along with their associated `Budgets` and `RateLimit`, and exposes them in the API response.

## Changes

- `GetVirtualKeyQuotaByValue` now preloads `ProviderConfigs`, `ProviderConfigs.Budgets`, and `ProviderConfigs.RateLimit` in addition to the existing top-level `Budgets` and `RateLimit` preloads.
- The `getVirtualKeyQuota` HTTP handler now includes `provider_configs` in the JSON response payload.
- Updated the comment on `GetVirtualKeyQuotaByValue` to accurately reflect what is and isn't loaded (provider `Keys` and `MCPConfigs` are still excluded; `ProviderConfigs` themselves are now included).

## Type of change

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

## Affected areas

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

## How to test

Call the virtual key quota endpoint with a virtual key that has provider configs attached and verify the response includes `provider_configs` with their respective `budgets` and `rate_limit` fields populated.

```sh
go test ./framework/configstore/...
go test ./transports/bifrost-http/...
```

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

## Security considerations

No new secrets or PII are introduced. `ProviderConfigs` data returned is scoped to the authenticated virtual key, consistent with existing quota data access controls.

## 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
## Summary

The auth middleware whitelist was being matched against the full request URI (including query parameters), which could cause whitelist entries to fail to match when query strings were present. This fix ensures the whitelist is matched against the path only.

## Changes

- Replaced `ctx.Request.URI().RequestURI()` with `ctx.Path()` in the auth middleware so that URL whitelist checks are evaluated against the path component only, excluding any query parameters.

## Type of change

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

## Affected areas

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

## How to test

Add a whitelisted route to the auth config and make a request to that route with query parameters appended (e.g. `/login?redirect=/dashboard`). Verify that the request is correctly skipped for authorization rather than being blocked.

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

## Screenshots/Recordings

N/A

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

N/A

## Security considerations

This change narrows the input used for whitelist matching to the path only. Ensure that no existing whitelist entries rely on query parameter values for access control decisions, as those will no longer be considered during the skip check.

## 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
## Summary

The `Cleanup()` path in the logging plugin had a race condition where the `batchWriter` goroutine could still be mid-flush when the write queue was closed, causing in-memory log entries to be silently dropped on shutdown. This PR fixes the shutdown sequence so that `batchWriter` hands its in-progress batch back to `Cleanup`, which then drains both the recovered batch and any remaining channel-buffered entries under a bounded 30-second deadline before closing the queue.

## Changes

- **Shutdown sequencing fix**: `Cleanup` now cancels `batchCtx` to stop `batchWriter` before any further DB writes, waits on `batchWriterDone` for the ownership handoff, then calls `drainPending` to flush the recovered batch and remaining queue entries in `maxBatchSize` chunks.
- **`batchWriter` exit path**: Added a `batchCtx.Done()` case that parks the current in-memory batch into `p.recoveredBatch`, closes `batchWriterDone`, and returns without touching the store — leaving all drain responsibility to `Cleanup`.
- **`drainPending`**: New method that non-blockingly empties the write queue into a combined slice with the recovered batch, then processes it in chunks while checking a wall-clock deadline between chunks. Entries remaining past the deadline are counted as dropped and logged.
- **`cleanupDrainTimeout` constant**: Set to 30 seconds to match the outer server shutdown budget, ensuring the logging plugin can fully drain in the worst case without wedging the process.
- **`closed` flag ordering**: The closed flag is now set before cancelling `batchWriter` so no new producers can grow the queue while `drainPending` is running.
- **Doc comments**: Added Go doc comments to all exported and unexported methods in `hybrid.go` that were previously undocumented, covering hydration semantics, object-store error handling, and delegation behavior.
- **`interface{}` → `any`**: Replaced `interface{}` with the `any` alias in several map type literals and function signatures across `asyncjob.go`, `hybrid.go`, and `main.go`.

## Type of change

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

## Affected areas

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

## How to test

Four new tests in `plugins/logging/cleanup_test.go` cover the fixed shutdown paths:

```sh
go test ./plugins/logging/... -run TestCleanup -v
```

- `TestCleanupDrainsRecoveredBatchNoDrops` — verifies that entries sitting in `batchWriter`'s in-memory batch (below `maxBatchSize`, no auto-flush) are fully recovered and persisted by `drainPending`.
- `TestCleanupDrainsCombinedQueueAndBatchNoDrops` — uses a slow store and a burst larger than `maxBatchSize` to force entries into both the channel buffer and the in-memory batch simultaneously; asserts zero drops.
- `TestCleanupRejectsNewSendsAfterClosed` — confirms that `enqueueLogEntry` calls after `Cleanup` are silent no-ops and do not reach the store.
- `TestCleanupIsIdempotent` — confirms that calling `Cleanup` twice does not panic or re-close channels.

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

## Security considerations

None. Changes are limited to internal shutdown sequencing and logging infrastructure; no auth, secrets, or PII handling is affected.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
… OTEL and Prometheus plugin configs (#3651)

## Summary

This PR introduces `EnvVar`-typed fields for sensitive and configurable URL/credential values in the OpenTelemetry and Prometheus (telemetry) plugins, replacing raw `string` fields. This allows users to reference environment variables (e.g., `env.OTEL_COLLECTOR_URL`) instead of embedding literal values in stored configuration, improving secret management and deployment flexibility.

## Changes

- **`plugins/otel`**: `CollectorURL`, `MetricsEndpoint`, and `Headers` values in `Config` are now `*schemas.EnvVar` instead of `string`/`map[string]string`. Added `MarshalForStorage()` to serialize back to plain strings for DB persistence, `Redacted()` for safe API responses, and `resolveHeaders()` to convert `EnvVar` header maps to plain strings at runtime. Removed the inline `env.` prefix resolution loop from `Init` in favor of `EnvVar.GetValue()`.
- **`plugins/telemetry`**: `PushGatewayURL`, `BasicAuth.Username`, and `BasicAuth.Password` in `PushGatewayConfig`/`BasicAuthConfig` are now `*schemas.EnvVar`. Added `MarshalForStorage()` and `Redacted()` to `Config` with the same storage/API separation pattern.
- **`core/schemas`**: Introduced the `ConfigMarshallerPlugin` interface, optionally implemented by plugins that need custom config serialization. The server calls `MarshalConfigForStorage` before writing config to the DB and `RedactConfig` when building API responses. Both the OTEL and telemetry plugins implement this interface.
- **`transports/bifrost-http/handlers/plugins.go`**: Added `normalizePluginConfig()` to round-trip plugin configs through their typed structs before DB writes (ensuring `EnvVar` → plain string serialization), and `expandPluginConfigForAPI()` to expand stored plain strings back into full `EnvVar` objects with redaction for API responses. Refactored `getPlugins` to use `buildPluginResponseWithStatuses` to avoid redundant status fetches per plugin.
- **`transports/bifrost-http/lib/config.go`**: Added a `ConfigMarshallers` atomic cache derived from `BasePlugins`, rebuilt alongside the other interface caches on any plugin change.
- **`transports/bifrost-http/server/server.go`**: Implemented `NormalizePluginConfig` and `ExpandPluginConfigForAPI` on `BifrostHTTPServer`, backed by the `ConfigMarshallers` cache.
- **UI schemas (`ui/lib/types/schemas.ts`)**: Updated `otelConfigSchema` and `prometheusConfigSchema` to use `envVarSchema` for URL and credential fields. Validation logic now skips format checks for env var references (`from_env: true`) and checks `value` or `from_env` presence instead of raw string truthiness.
- **UI forms**: Replaced plain `<Input>` components with `<EnvVarInput>` for collector URL, metrics endpoint, push gateway URL, and basic auth fields in the OTEL and Prometheus form fragments. The password field now uses `EnvVarInput` with `hideValueWhenEnv` and `redactNonEnvValue` props, removing the manual show/hide toggle. `HeadersTable` now uses `useEnvVarInput` mode.
- **`ui/lib/utils/envVarForm.ts`**: Fixed `toEnvVarFormValue` to clear `value` when the input is an env reference string. Added `toEnvVarMapFormValue` to convert header maps of mixed `string | EnvVar` values into typed `EnvVar` form values.

## Type of change

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

## Affected areas

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

## How to test

```sh
# Core/Transports
go test ./plugins/otel/... ./plugins/telemetry/... ./transports/bifrost-http/...

# UI
cd ui
pnpm i
pnpm build
pnpm test
```

**Manual validation:**
1. Configure the OTEL plugin with `collector_url` set to `env.OTEL_COLLECTOR_URL` and verify the environment variable is resolved at runtime.
2. Set a literal URL and confirm it is stored and returned correctly.
3. Configure Prometheus push gateway with `env.PUSHGATEWAY_URL` and basic auth credentials via env vars; verify metrics are pushed correctly.
4. Confirm API responses show full `EnvVar` objects with sensitive values redacted.
5. Confirm DB-stored configs contain plain strings (`env.FOO` or literal values), not JSON objects.

## Breaking changes

- [x] Yes
- [ ] No

The `Config` structs for the OTEL and telemetry plugins have changed field types from `string` to `*schemas.EnvVar`. Any code directly constructing these structs (e.g., in tests or custom integrations) must be updated to wrap values using `schemas.NewEnvVar(...)` or equivalent. Configs already stored in the database as plain strings will be transparently upgraded on read via `EnvVar.UnmarshalJSON`.

## Security considerations

Sensitive fields (collector URLs, push gateway URLs, basic auth credentials, and OTEL headers) are now redacted in API responses via the `Redacted()` methods. Credentials are never stored as resolved values — only as `env.VAR_NAME` references or literal strings as provided by the user. The `FullyRedacted()` method is applied to passwords specifically.

## 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
## Summary

Adds Kafka as an enterprise observability plugin, enabling users to stream completed request traces as JSON to a Kafka topic for real-time analytics, alerting, and downstream processing.

## Changes

- Registered `kafka` as an enterprise plugin in the bifrost-http server alongside `datadog`
- Added a `KafkaConnectorView` fallback component that renders an enterprise upsell view for non-enterprise users, including the Kafka SVG icon and a link to the Kafka connector docs
- Added `KafkaView` as the plugin view wrapper used in the observability workspace
- Added Kafka to the supported platforms list in `observabilityView.tsx` with its icon and wired up the `KafkaView` component when the `kafka` plugin is selected
- Added a theme-aware Kafka SVG logo that renders dark or light depending on the user's color scheme preference

## Type of change

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

## Affected areas

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

## How to test

1. Navigate to the Observability section in the workspace UI.
2. Confirm that Kafka appears in the list of supported platforms with the correct icon.
3. Select Kafka — on a non-enterprise license, the enterprise upsell/contact view should be displayed.
4. On an enterprise license, the full Kafka connector configuration view should be rendered.

```sh
# UI
cd ui
pnpm i || npm i
pnpm build || npm run build
```

## Screenshots/Recordings

_Add before/after screenshots of the Kafka entry in the observability platform list and the enterprise upsell view._

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

_Link related issues here._

## Security considerations

Kafka connection credentials (brokers, auth tokens) should be treated as secrets and not exposed in logs or client-side state.

## 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
#3714)

## Summary

Adds Google Cloud Pub/Sub as a new observability connector option in the UI, gated behind the Bifrost enterprise license.

## Changes

- Added a `PubSubConnectorView` fallback component that renders a "Contact Us" / enterprise upsell view when the enterprise license is not present, using the `Rss` icon and linking to the Pub/Sub connector docs.
- Added `PubSubView` as a plugin view that wraps `PubSubConnectorView`.
- Registered `pubsub` as a supported platform in the observability view list, including a custom inline SVG icon styled in Google blue (`#4285F4`), and wired up the `PubSubView` to render when the `pubsub` plugin is selected.

## Type of change

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

## Affected areas

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

## How to test

```sh
cd ui
pnpm i || npm i
pnpm build || npm run build
```

1. Navigate to the Observability settings page in the UI.
2. Confirm that "Pub/Sub" appears in the list of supported platforms with the correct icon.
3. Select "Pub/Sub" and verify the enterprise upsell view is rendered, showing the title "Unlock Google Cloud Pub/Sub trace streaming" and a link to `https://docs.getbifrost.ai/enterprise/pubsub-connector`.

## Screenshots/Recordings

If UI changes, add before/after screenshots or short clips.

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

Link related issues and discussions. Example: Closes #123

## Security considerations

No security implications. This is a UI-only change that renders a static upsell view for non-enterprise users.

## 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
BearTS and others added 15 commits May 27, 2026 00:28
…ndpoint`, and Prometheus Push Gateway fields (#3753)

## Summary

Documents environment variable substitution support for `collector_url`, `metrics_endpoint`, and Prometheus Push Gateway fields (`push_gateway_url`, `username`, `password`). Previously, only header values were documented as supporting `env.VAR_NAME` — this update clarifies that endpoint URLs and credentials can also reference environment variables, keeping sensitive values out of stored configuration.

## Changes

- Updated `collector_url` and `metrics_endpoint` field types to `string | EnvVar` and noted `env.VAR_NAME` support in their descriptions
- Updated `push_gateway_url`, `username`, and `password` field types to `string | EnvVar` with the same notation
- Expanded the "Environment Variable Substitution" section in the OTel docs to include `collector_url` and `metrics_endpoint` in the example and explanation
- Clarified that stored configuration retains the `env.VAR_NAME` string and that resolved values are never persisted or returned in API responses
- Added a new "With Environment Variables" example block to the Prometheus docs showing `push_gateway_url`, `username`, and `password` using `env.VAR_NAME`

## Type of change

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

## Affected areas

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

## How to test

Review the updated docs pages for the OTel and Prometheus plugins and verify:
- Field type columns reflect `string | EnvVar` where applicable
- Example JSON snippets include `env.VAR_NAME` usage for endpoint URLs and credentials
- The security behavior description (no persistence of resolved values, redaction in API responses) is accurate against the implementation

## Breaking changes

- [ ] Yes
- [x] No

## Security considerations

The documentation now explicitly states that resolved environment variable values are never persisted to the database or config file, and that API responses return `EnvVar` objects with sensitive resolved values redacted. This is a documentation clarification of existing security behavior.

## Checklist

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

Removes the Azure-specific `api-version` query parameter and deployment-based URL patterns (`/openai/deployments/{model}/...`) from all Azure provider endpoints, replacing them with OpenAI-compatible `/openai/v1/...` paths. This aligns the Azure provider with a unified API routing approach where the endpoint itself is expected to handle versioning.

## Changes

- Replaced all `/openai/deployments/{model}/{operation}?api-version={version}` URL patterns with `/openai/v1/{operation}` across every operation: chat completions, text completions, embeddings, speech, transcription, image generation, image edits, files, batches, responses, and list models.
- Removed all `apiVersion` resolution logic (including fallback to `AzureAPIVersionDefault` and `AzureAPIVersionImageEditDefault`) throughout the provider since the version is no longer appended to URLs.
- Removed the hardcoded `?api-version=preview` suffix from the responses endpoint.
- Updated `buildContainerURL` to drop the `?api-version=` suffix, keeping the `/openai/v1{path}` format.
- The `BatchCancel` URL was also corrected to use the consistent `/openai/v1/batches/{id}/cancel` path.

## Type of change

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

## Affected areas

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

## How to test

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

Validate that requests to each Azure operation (chat, completions, embeddings, speech, transcription, image generation, image edits, files, batches) are routed to the correct `/openai/v1/...` path without any `api-version` query parameter appended.

## Breaking changes

- [x] Yes
- [ ] No

Any Azure deployments relying on the `api-version` query parameter being automatically appended by Bifrost will no longer receive it. The configured Azure endpoint must now handle versioning independently (e.g., via an API gateway or proxy that injects the required `api-version`). The `AzureKeyConfig.APIVersion` field is no longer used by the provider.

## Related issues

## Security considerations

No auth or secrets handling changes. The removal of `api-version` from URLs has no direct security implications, but operators should ensure their Azure endpoint or gateway enforces the correct API version to avoid unintended access to preview or deprecated API surfaces.

## 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
## Summary

Removes the `api_version` field from `AzureKeyConfig` across the codebase. Bifrost now uses the Azure OpenAI v1 API, which does not require an `api-version` query parameter, making this field obsolete.

## Changes

- Removed `api_version` from `AzureKeyConfig` in UI types, schemas, and form validation
- Removed `azure_api_version` from the OpenAPI spec and governance schema
- Removed `api_version` from all example and test config files
- Added a deprecated notice for `api_version` in `config.schema.json` to maintain backward compatibility for existing configs that may still include the field

## Type of change

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

## Affected areas

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

## How to test

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

# UI
cd ui
pnpm i || npm i
pnpm test || npm test
pnpm build || npm run build
```

Verify that Azure provider keys configured without `api_version` continue to route requests correctly to Azure OpenAI endpoints.

## Breaking changes

- [x] Yes
- [ ] No

The `api_version` field is no longer accepted in `AzureKeyConfig`. Existing configurations that include `api_version` should remove it. The field has been marked as deprecated in the JSON schema to avoid hard failures, but it will have no effect if present.

## Related issues

## Security considerations

No security implications. This change removes an unused configuration field and does not affect authentication or secret handling.

## 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
## Summary

Restores the missing `Blocked Models` create/edit UI in the VK provider config sheet.

The backend enforcement (`isModelBlockedByList`, `blockedModelCandidates`, `blocklist_test.go`) was already present on `dev`. The only missing piece was the frontend editor, which became unreachable after dev was rebased/force-pushed following the original merge of #3718.

Changes in this PR:
- Added `blacklisted_models` to the zod provider config schema
- Initialized edit mode with `config.blacklisted_models || []`
- Added `blacklisted_models: []` default for new provider configs
- Added `Blocked Models` `ModelMultiselect` block, placed between `Allowed Models` and `Allowed Keys`
- Wildcard (`*`) toggle behavior consistent with allowed models

## How to test

Build:

```sh
go test ./plugins/governance/...
go build -o ./tmp/bifrost-http ./transports/bifrost-http
cd ui && npm run build
```

Manual UI check:

`http://localhost:9090/workspace/governance/virtual-keys`

Expected create/edit flow:

`Allowed Models → Blocked Models → Allowed Keys`

Runtime enforcement (prefix-aware, already on dev):
- blacklist `["ollama/mistral:latest"]`, request `"mistral:latest"` → 403
- blacklist `["mistral:latest"]`, request `"ollama/mistral:latest"` → 403
- wildcard `["*"]` → 403 for any model
- empty blacklist → passes through

## Related

Restores the UI lost from #3718. Backend enforcement already present on `dev`.
## Summary

Fixes a bug where empty or nil text content blocks were being passed through to Bedrock during response conversion, which could cause unexpected behavior or errors when Bedrock receives blank text blocks.

## Changes

- Added a nil and empty string check for `block.Text` in `convertBifrostResponsesMessageContentBlocksToBedrockContentBlocks`. When a text block is nil or empty, it is now skipped via `continue` rather than being forwarded to Bedrock.

## Type of change

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

## Affected areas

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

## How to test

Send a request to the Bedrock provider that includes a message with an empty or nil text content block and verify it is handled gracefully without errors.

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

## Breaking changes

- [x] No

## Security considerations

None.

## 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
… reorganization (#3755)

Releases Bifrost Helm chart version 2.1.19, incorporating several bug fixes and new configuration options introduced since 2.1.18.

- Bumped chart version from `2.1.18` to `2.1.19`
- Added `existingSecret` support for hosted PostgreSQL, allowing `postgresql.auth.existingSecret` and `postgresql.auth.passwordKey` to reference a Kubernetes secret instead of a plaintext password
- Added `postgresql.primary.podSecurityContext` and `postgresql.primary.containerSecurityContext` to support clusters enforcing strict Kyverno/OPA security policies
- Added `bifrost.featureFlags` map to `values.yaml` and `_helpers.tpl`, rendering into `feature_flags.flags` in the generated config JSON
- Fixed Deployment not exposing the cluster gRPC container port and `service.yaml` missing the gRPC service port
- Fixed Weaviate PVC rendering when `vectorStore.weaviate.persistence.enabled=false`
- Fixed Redis probes passing password via `-a` flag; switched to `REDISCLI_AUTH` env var
- Fixed nondeterministic env var ordering for `providerSecrets` and `weaviate.env` map iterations by sorting keys with `sortAlpha`
- Corrected guardrail `timeout` example values in `values.yaml`
- Moved `bifrost.framework.pricing.modelParametersUrl` (previously `bifrost.modelCatalog.modelParametersUrl`) into the 2.1.18 changelog entry where it was originally introduced

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

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

```sh
helm repo update
helm install bifrost bifrost/bifrost --version 2.1.19 --dry-run
```

To validate `existingSecret` support:

```sh
helm install bifrost bifrost/bifrost \
  --set postgresql.enabled=true \
  --set postgresql.auth.existingSecret=my-pg-secret \
  --set postgresql.auth.passwordKey=password \
  --dry-run
```

- [ ] Yes
- [x] No

The `existingSecret` support for PostgreSQL allows operators to avoid storing plaintext passwords in Helm values, enabling integration with secret management tools such as the Vault Secrets Operator. The chart-managed secret is not created when `existingSecret` is set.

- [ ] 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 applicablecs
…support it (#3754)

## Summary

Some Bedrock models (e.g. GLM, Llama) do not support prompt-caching cache points in the Converse API and will return a 400 error if cache point blocks are present in the request. This PR adds a guard that strips cache points from Bedrock requests before they are sent, for any model that does not support them.

## Changes

- Added `BedrockModelSupportsCachePoints` in `core/schemas/utils.go` that returns `true` only for Anthropic and Nova models, which are the models known to support explicit cache points in the Converse API.
- Added `stripCachePointsFromBedrockRequest` in `core/providers/bedrock/utils.go` that removes cache point blocks from message content, nested tool result content, system messages, and tool config entries.
- Called `stripCachePointsFromBedrockRequest` in both `ToBedrockChatCompletionRequest` and `ToBedrockResponsesRequest` when the target model does not support cache points, preventing 400 errors from unsupported models receiving cache point blocks.

## Type of change

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

## Affected areas

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

## How to test

Send a Bedrock Converse request to a non-Anthropic, non-Nova model (e.g. a GLM or Llama model) with cache point blocks included in the request body. Verify the request succeeds without a 400 error and that cache point blocks are absent from the forwarded request.

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

## Breaking changes

- [ ] Yes
- [x] No

## Security considerations

No security implications. Cache point blocks are stripped only from the outbound request payload for unsupported models and do not affect authentication, secrets, or PII handling.

## 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
## Summary

Extends the `jsonparser` plugin to handle OpenAI Responses API streaming (`ResponsesStreamRequest`) in addition to the existing chat completion streaming support. Previously, the plugin only accumulated and repaired partial JSON for `ChatCompletionStreamRequest`; `output_text.delta` events from the Responses stream were silently passed through unprocessed.

## Changes

- `shouldRun` now permits both `ChatCompletionStreamRequest` and `ResponsesStreamRequest` instead of only chat completion streams.
- The early-return nil check in `PostLLMHook` now accepts responses with either `ChatResponse` or `ResponsesStreamResponse` set.
- `PostLLMHook` branches on `extraFields.RequestType` to apply the existing accumulate-and-repair logic to `ResponsesStreamResponse.Delta` for `output_text.delta` events, while leaving all other event types (e.g. `response.created`, `response.completed`) untouched.
- `getRequestID` now falls back to `ResponsesStreamResponse.Response.ID` when a chat response ID is unavailable.
- `deepCopyBifrostResponse` performs a proper deep copy of `ResponsesStreamResponse` (instead of a shallow reference copy) so that rewriting `Delta` does not mutate the original pointer.
- A new `deepCopyResponsesStreamResponse` helper is introduced to support the above.
- Tests cover: delta accumulation and repair across multiple chunks, passthrough of non-delta event types, immutability of the original pointer, `PerRequest` usage gating, and an optional end-to-end integration test against the OpenAI Responses stream API (skipped when `OPENAI_API_KEY` is unset).

## Type of change

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

## Affected areas

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

## How to test

```sh
# Run all plugin tests
go test ./plugins/jsonparser/...

# Run the new responses-stream-specific tests
go test ./plugins/jsonparser/... -run TestPostLLMHookResponsesStream

# Run the end-to-end integration test (requires a valid OpenAI key)
OPENAI_API_KEY=<your-key> go test ./plugins/jsonparser/... -run TestJsonParserPluginResponsesStreamEndToEnd -v
```

Expected outcomes:
- `TestPostLLMHookResponsesStreamDelta`: each accumulated delta chunk is valid JSON.
- `TestPostLLMHookResponsesStreamNonDeltaPassthrough`: non-delta events pass through with a nil `Delta`.
- `TestPostLLMHookResponsesStreamDoesNotMutateOriginal`: the original response pointer is not modified.
- `TestPostLLMHookResponsesStreamPerRequest`: plugin is a no-op without `EnableStreamingJSONParser` in context, and active with it.

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

## Security considerations

No new auth, secrets, or PII handling introduced. The plugin continues to operate only on in-flight response content already present in memory.

## Checklist

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

## Summary

For streaming (deferred) requests, the tracing middleware was ending the root HTTP span inside its `defer` block at handler return — before the deferred `llm.call` child span had finished. This caused the child span to appear longer than its parent in trace viewers, which is an invalid trace hierarchy.

The fix moves root span termination for deferred requests into the trace completer callback, which fires only after the stream has fully drained. This ensures the root span's latency covers the entire streamed response and that the `llm.call` child span always ends before its parent.

## Changes

- The middleware `defer` block now checks `BifrostContextKeyDeferTraceCompletion` before ending the root span — if the request is deferred, the root span is left open.
- The trace completer (registered via `OnStreamComplete`) now explicitly ends the root span via `GetSpanHandleByID` before calling `CompleteAndFlushTrace`, so the root span closes after the final SSE chunk is written.
- A new test `TestTracingMiddleware_StreamingRootSpanEndsAfterLLMSpan` verifies that the root span's `EndTime` is always greater than or equal to the `llm.call` span's `EndTime` for deferred requests, and that the root span has a positive duration.

## Type of change

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

## Affected areas

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

## How to test

```sh
go test ./transports/bifrost-http/handlers/... -run TestTracingMiddleware_StreamingRootSpanEndsAfterLLMSpan -v
```

Expected: the test passes, confirming the root span ends after the `llm.call` span for streaming requests.

To validate no regressions in non-streaming tracing:

```sh
go test ./transports/bifrost-http/handlers/...
```

## Screenshots/Recordings

N/A

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

N/A

## Security considerations

No security implications. This change only affects span lifecycle management within the tracing middleware.

## Checklist

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

This PR delivers a collection of fixes and improvements across the Gemini provider, Anthropic provider, Bedrock Responses API, Azure config redaction, and migration test infrastructure for v1.5.4.

## Changes

- **Gemini – tool schema passthrough via `ParametersJSONSchema`**: Tool parameter schemas are now forwarded to Gemini using the `parametersJsonSchema` wire key instead of the structured `parameters` field. This removes the previous schema-transformation layer (union-type expansion, `anyOf` rewriting, sibling-field stripping, `nullable` injection) and passes the raw JSON Schema through unchanged. As a result, array-typed unions, sibling fields alongside `anyOf`, and `description` fields are all preserved. The `propertyOrdering` field is no longer emitted separately; property order is maintained by the underlying `OrderedMap` serialization.
- **Gemini – tool response role corrected to `"user"`**: Function/tool response content blocks were being emitted with role `"model"`; they are now correctly emitted with role `"user"` across both the Chat and Responses API paths.
- **Gemini – structured output + tools conflict**: When both tools and a JSON response format are present for Gemini 2.5, `responseJsonSchema` is now also dropped (previously only `responseMimeType` was dropped).
- **Anthropic – stop reason normalization**: `end_turn` → `stop`, `tool_use` → `tool_calls`, `max_tokens` → `length` to align with the normalized Bifrost stop-reason vocabulary. Tests updated accordingly.
- **Anthropic – computer-use tool version mapping**: `text_editor_20250124`/`str_replace_editor` is now upgraded to `text_editor_20250728`/`str_replace_based_edit_tool` for `claude-sonnet-4-5` models. Test names and expectations updated to reflect the corrected behavior.
- **Bedrock – Responses API `hasToolUse` detection**: Replaced the content-block scan (which checked for unmatched `toolUse` blocks) with a direct check on `bifrostResp.Output` for `ResponsesMessageTypeFunctionCall`, making the detection more reliable and consistent with the Responses API data model.
- **Azure config redaction**: Fixed a panic/incorrect redaction when `AzureKeyConfig.Endpoint` is not sourced from an environment variable. The endpoint is now only redacted when `IsFromEnv()` is true; otherwise the original value is preserved as-is.
- **JSON parser plugin test**: Added missing `Params` with `json_object` format to the Responses stream end-to-end test to properly exercise the parser plugin.
- **Migration tests – v1.5.4 columns**: Added dynamic column update blocks for three new v1.5.4 migrations — `governance_virtual_key_provider_configs.blacklisted_models`, `governance_virtual_keys.created_by_user_id`, and `logs.inc_number` — for both PostgreSQL and SQLite paths. Also added `azure_api_version` to the list of dropped columns on `config_keys` for snapshot comparison.

## Type of change

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

## Affected areas

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

## How to test

```sh
go test ./core/providers/gemini/...
go test ./core/providers/anthropic/...
go test ./core/providers/bedrock/...
go test ./framework/configstore/...
go test ./plugins/jsonparser/...
```

For migration tests, run the migration test workflow against a PostgreSQL and SQLite target and verify that v1.5.4 column additions and the `azure_api_version` column drop are handled without snapshot mismatches.

## Breaking changes

- [x] Yes
- [ ] No

The Gemini tool schema wire format changes from `parameters` to `parametersJsonSchema`. Clients or tests that assert on the exact wire key or rely on the previous union-type/`anyOf` rewriting behavior will need to be updated. The Anthropic stop reason values (`end_turn`, `tool_use`, `max_tokens`) are replaced with normalized values (`stop`, `tool_calls`, `length`); any downstream code matching on the raw Anthropic strings will need to be updated.

## Security considerations

The Azure endpoint redaction fix ensures that plain (non-env-var) endpoint values are not incorrectly processed through the `Redacted()` path, preventing potential nil-pointer panics and ensuring the correct value is returned in config responses.

## Checklist

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

## Summary

Filters out entries with empty `model` values from model rankings queries to prevent blank/unknown models from appearing in rankings results.

## Changes

- Added `WHERE model != ''` filter to both the current period and previous period queries in `getModelRankingsFromMatView`, ensuring that rows with no model identifier are excluded from aggregation and ranking calculations.

## Type of change

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

## Affected areas

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

## How to test

Verify that model rankings no longer include entries with an empty model name. Query the `mv_logs_hourly` materialized view directly to confirm rows with `model = ''` exist, then check that the rankings endpoint/response omits them.

```sh
go test ./framework/logstore/...
```

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

## Security considerations

None.

## 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
…lently returning empty slices (#3759)

## Summary

The `GetAvailable*` methods on `LoggerPlugin` and `LogManager` previously swallowed errors by logging them internally and returning empty slices. This change propagates errors to callers instead, allowing them to handle failures appropriately (e.g., returning HTTP 500 responses).

## Changes

- All `GetAvailable*` methods (`GetAvailableModels`, `GetAvailableAliases`, `GetAvailableSelectedKeys`, `GetAvailableVirtualKeys`, `GetAvailableRoutingRules`, `GetAvailableRoutingEngines`, `GetAvailableStopReasons`, `GetAvailableTeams`, `GetAvailableCustomers`, `GetAvailableUsers`, `GetAvailableBusinessUnits`, `GetAvailableMCPVirtualKeys`) now return `(T, error)` instead of `T`.
- Errors are wrapped with `fmt.Errorf(...%w...)` and returned rather than being logged and discarded with an empty-value fallback.
- The `LogManager` interface signatures are updated to match.
- HTTP handler goroutines in `getAvailableFilterData` now propagate errors through the `errgroup`, and `getMCPLogsFilterData` explicitly handles the error by returning an HTTP 500 response.

## Type of change

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

## Affected areas

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

## How to test

```sh
go test ./plugins/logging/...
go test ./transports/bifrost-http/...
```

Trigger requests to the filter data endpoints (e.g., `getAvailableFilterData`, `getMCPLogsFilterData`) and verify that store-level errors result in appropriate HTTP error responses rather than silently returning empty data.

## Breaking changes

- [x] Yes
- [ ] No

Any code implementing the `LogManager` interface must update all `GetAvailable*` method signatures to return `(T, error)`. Callers that previously ignored errors by relying on empty-slice fallbacks must now handle the returned error explicitly.

## Related issues

## Security considerations

None.

## 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
## Summary

Exports the `resolvePeriod` function by renaming it to `ResolvePeriod`, making it accessible outside the `handlers` package. This allows other packages to reuse the period-to-time-range resolution logic without duplicating it.

## Changes

- Renamed `resolvePeriod` to `ResolvePeriod` in `utils.go` to export it from the package
- Updated all call sites in `logging.go` to use the new exported name

## Type of change

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

## Affected areas

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

## How to test

```sh
go test ./transports/bifrost-http/handlers/...
```

Verify that log filtering by `period` query parameter (e.g. `?period=1h`, `?period=7d`) continues to work correctly across all log endpoints.

## Screenshots/Recordings

N/A

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

N/A

## Security considerations

No security implications. This is a visibility change only; the underlying logic is unchanged.

## 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
…ser_name` (#3764)

## Summary

Fixes duplicate entries in the `mv_filter_users` materialized view caused by rows where `user_name` is null or empty falling back to `user_id` as the display name. This resulted in users appearing twice in filter dropdowns — once with their actual name and once with their ID as the name.

## Changes

- Updated the `mv_filter_users` materialized view definition to use `user_name` directly (without the `COALESCE` fallback to `user_id`) and added `user_name IS NOT NULL AND user_name != ''` to the `WHERE` clause to exclude rows that would have previously triggered the fallback.
- Added a migration (`migrationRecreateFilterUsersMatView`) that drops the existing `mv_filter_users` view so `ensureMatViews` recreates it with the corrected definition on next startup.

## Type of change

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

## Affected areas

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

## How to test

1. Ensure there are log entries where some rows have a `user_name` and some do not.
2. Deploy this change and allow the migration to run.
3. Verify that `mv_filter_users` is recreated and contains only rows where `user_name` is non-empty.
4. Confirm that the user filter dropdown no longer shows duplicate entries (one with the user's name and one with the user's ID).

```sh
go test ./framework/logstore/...
```

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

## Security considerations

None.

## 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
## Summary

Briefly explain the purpose of this PR and the problem it solves.

## Changes

- What was changed and why
- Any notable design decisions or trade-offs

## 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

Describe the steps to validate this change. Include commands and expected outcomes.

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

# UI
cd ui
pnpm i || npm i
pnpm test || npm test
pnpm build || npm run build
```

If adding new configs or environment variables, document them here.

## Screenshots/Recordings

If UI changes, add before/after screenshots or short clips.

## Breaking changes

- [ ] Yes
- [ ] No

If yes, describe impact and migration instructions.

## Related issues

Link related issues and discussions. Example: Closes #123

## Security considerations

Note any security implications (auth, secrets, PII, sandboxing, etc.).

## 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 requested a review from a team as a code owner May 26, 2026 19:27
@CLAassistant

CLAassistant commented May 26, 2026

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
5 out of 6 committers have signed the CLA.

✅ BearTS
✅ Vaibhav701161
✅ akshaydeo
✅ roroghost17
✅ impoiler
❌ TejasGhatte
You have signed the CLA already but the status is still pending? Let us recheck it.

@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7c909fb1-c145-489a-bffa-a1d247f9dd90

📥 Commits

Reviewing files that changed from the base of the PR and between 00f2e17 and 0891e20.

⛔ Files ignored due to path filters (16)
  • docs/media/access-profiles/access-profile-mcp-config.png is excluded by !**/*.png
  • docs/media/access-profiles/access-profile-provider-config.png is excluded by !**/*.png
  • docs/media/access-profiles/access-profile-rbac.png is excluded by !**/*.png
  • docs/media/access-profiles/access-profiles-duplicate.png is excluded by !**/*.png
  • docs/media/access-profiles/access-profiles-home.png is excluded by !**/*.png
  • docs/media/access-profiles/access-profiles-save-and-propagate.png is excluded by !**/*.png
  • docs/media/access-profiles/new-access-profile.png is excluded by !**/*.png
  • docs/media/dac/dac-set-visibility.png is excluded by !**/*.png
  • docs/media/enterprise-audit-logs.png is excluded by !**/*.png
  • docs/media/mcp/mcp-tool-group.png is excluded by !**/*.png
  • docs/media/mcp/mcp-tool-groups-associations.png is excluded by !**/*.png
  • docs/media/mcp/mcp-tool-groups-mcp-tool-association.png is excluded by !**/*.png
  • plugins/governance/go.sum is excluded by !**/*.sum
  • transports/go.sum is excluded by !**/*.sum
  • ui/public/images/kafka-logo.svg is excluded by !**/*.svg
  • ui/public/images/pubsub-logo.svg is excluded by !**/*.svg
📒 Files selected for processing (192)
  • .github/workflows/scripts/run-migration-tests.sh
  • core/bifrost.go
  • core/changelog.md
  • core/internal/llmtests/account.go
  • core/internal/llmtests/passthrough_api.go
  • core/internal/llmtests/provider_feature_support_test.go
  • core/mcp/pluginpipeline.go
  • core/providers/anthropic/anthropic.go
  • core/providers/anthropic/chat.go
  • core/providers/anthropic/chat_test.go
  • core/providers/anthropic/compaction_test.go
  • core/providers/anthropic/requestbuilder_test.go
  • core/providers/anthropic/responses.go
  • core/providers/anthropic/types.go
  • core/providers/anthropic/utils.go
  • core/providers/anthropic/utils_test.go
  • core/providers/azure/azure.go
  • core/providers/azure/realtime.go
  • core/providers/azure/types.go
  • core/providers/bedrock/bedrock.go
  • core/providers/bedrock/bedrock_test.go
  • core/providers/bedrock/cache_points_test.go
  • core/providers/bedrock/chat.go
  • core/providers/bedrock/responses.go
  • core/providers/bedrock/transport_test.go
  • core/providers/bedrock/types.go
  • core/providers/bedrock/utils.go
  • core/providers/gemini/gemini_test.go
  • core/providers/gemini/uniontype_test.go
  • core/providers/utils/idle_timeout_reader_test.go
  • core/providers/utils/utils.go
  • core/schemas/account.go
  • core/schemas/otelconv.go
  • core/schemas/plugin.go
  • core/schemas/trace.go
  • core/schemas/utils.go
  • core/version
  • docs/architecture/core/plugins.mdx
  • docs/deployment-guides/config-json/plugins.mdx
  • docs/deployment-guides/config-json/providers.mdx
  • docs/deployment-guides/helm/storage.mdx
  • docs/docs.json
  • docs/enterprise/access-profiles.mdx
  • docs/enterprise/advanced-governance.mdx
  • docs/enterprise/audit-logs.mdx
  • docs/enterprise/custom-plugins.mdx
  • docs/enterprise/data-access-control.mdx
  • docs/enterprise/guardrails.mdx
  • docs/enterprise/mcp-tool-groups.mdx
  • docs/enterprise/mcp-with-fa.mdx
  • docs/enterprise/moving-from-oss/cross-region.mdx
  • docs/enterprise/moving-from-oss/overview.mdx
  • docs/enterprise/moving-from-oss/security-hardening.mdx
  • docs/enterprise/moving-from-oss/sizing.mdx
  • docs/enterprise/moving-from-oss/versioning.mdx
  • docs/enterprise/overview.mdx
  • docs/features/observability/otel.mdx
  • docs/features/observability/prometheus.mdx
  • docs/features/otel.mdx
  • docs/integrations/passthrough.mdx
  • docs/openapi/openapi.json
  • docs/openapi/openapi.yaml
  • docs/openapi/paths/management/accessprofiles.yaml
  • docs/openapi/paths/management/governance.yaml
  • docs/openapi/paths/management/mcptoolgroups.yaml
  • docs/openapi/paths/management/rbac.yaml
  • docs/openapi/schemas/management/accessprofiles.yaml
  • docs/openapi/schemas/management/governance.yaml
  • docs/openapi/schemas/management/mcptoolgroups.yaml
  • docs/openapi/schemas/management/rbac.yaml
  • docs/overview.mdx
  • docs/plugins/getting-started.mdx
  • docs/plugins/writing-wasm-plugin.mdx
  • docs/providers/request-options.mdx
  • docs/providers/supported-providers/azure.mdx
  • docs/providers/test-harness-coverage.mdx
  • examples/configs/withprompushgateway/config.json
  • examples/configs/withvirtualkeys/config.json
  • framework/changelog.md
  • framework/configstore/clientconfig.go
  • framework/configstore/clientconfig_redaction_test.go
  • framework/configstore/encryption_test.go
  • framework/configstore/migrations.go
  • framework/configstore/rdb.go
  • framework/configstore/tables/clientconfig.go
  • framework/configstore/tables/encryption_test.go
  • framework/configstore/tables/key.go
  • framework/configstore/tables/virtualkey.go
  • framework/logstore/asyncjob.go
  • framework/logstore/hybrid.go
  • framework/logstore/matviews.go
  • framework/logstore/migrations.go
  • framework/oauth2/main.go
  • framework/oauth2/sync_test.go
  • framework/tracing/llmspan.go
  • framework/version
  • helm-charts/bifrost/Chart.yaml
  • helm-charts/bifrost/README.md
  • helm-charts/bifrost/templates/NOTES.txt
  • helm-charts/bifrost/templates/_helpers.tpl
  • helm-charts/bifrost/templates/deployment.yaml
  • helm-charts/bifrost/templates/postgresql-deployment.yaml
  • helm-charts/bifrost/templates/redis-deployment.yaml
  • helm-charts/bifrost/templates/secrets.yaml
  • helm-charts/bifrost/templates/service.yaml
  • helm-charts/bifrost/templates/stateful.yaml
  • helm-charts/bifrost/templates/weaviate-deployment.yaml
  • helm-charts/bifrost/templates/weaviate-pvc.yaml
  • helm-charts/bifrost/values.schema.json
  • helm-charts/bifrost/values.yaml
  • helm-charts/index.yaml
  • plugins/compat/changelog.md
  • plugins/compat/version
  • plugins/governance/blocklist_test.go
  • plugins/governance/changelog.md
  • plugins/governance/go.mod
  • plugins/governance/main.go
  • plugins/governance/resolver.go
  • plugins/governance/utils.go
  • plugins/governance/version
  • plugins/jsonparser/changelog.md
  • plugins/jsonparser/main.go
  • plugins/jsonparser/plugin_test.go
  • plugins/jsonparser/utils.go
  • plugins/jsonparser/version
  • plugins/logging/changelog.md
  • plugins/logging/cleanup_test.go
  • plugins/logging/main.go
  • plugins/logging/operations.go
  • plugins/logging/utils.go
  • plugins/logging/version
  • plugins/logging/writer.go
  • plugins/maxim/changelog.md
  • plugins/maxim/version
  • plugins/mocker/changelog.md
  • plugins/mocker/version
  • plugins/otel/changelog.md
  • plugins/otel/converter.go
  • plugins/otel/main.go
  • plugins/otel/version
  • plugins/prompts/changelog.md
  • plugins/prompts/version
  • plugins/semanticcache/changelog.md
  • plugins/semanticcache/version
  • plugins/telemetry/changelog.md
  • plugins/telemetry/go.mod
  • plugins/telemetry/main.go
  • plugins/telemetry/version
  • tests/config.json
  • tests/e2e/api/collections/bifrost-api-management.postman_collection.json
  • tests/e2e/features/virtual-keys/pages/virtual-keys.page.ts
  • tests/e2e/features/virtual-keys/virtual-keys.spec.ts
  • tests/integrations/python/config.json
  • tests/integrations/typescript/config.json
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/handlers/logging.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/handlers/middlewares_test.go
  • transports/bifrost-http/handlers/plugins.go
  • transports/bifrost-http/handlers/plugins_test.go
  • transports/bifrost-http/handlers/provider_keys.go
  • transports/bifrost-http/handlers/session.go
  • transports/bifrost-http/handlers/utils.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/lib/config_test.go
  • transports/bifrost-http/server/server.go
  • transports/changelog.md
  • transports/config.schema.json
  • transports/go.mod
  • transports/schema_test/config_schema_test.go
  • transports/version
  • ui/app/_fallbacks/enterprise/components/data-connectors/kafka/kafkaConnectorView.tsx
  • ui/app/_fallbacks/enterprise/components/data-connectors/pubsub/pubsubConnectorView.tsx
  • ui/app/login/layout.tsx
  • ui/app/workspace/config/views/mcpView.tsx
  • ui/app/workspace/mcp-sessions/auth/page.tsx
  • ui/app/workspace/observability/fragments/otelFormFragment.tsx
  • ui/app/workspace/observability/fragments/prometheusFormFragment.tsx
  • ui/app/workspace/observability/views/observabilityView.tsx
  • ui/app/workspace/observability/views/plugins/kafkaView.tsx
  • ui/app/workspace/observability/views/plugins/prometheusView.tsx
  • ui/app/workspace/observability/views/plugins/pubsubView.tsx
  • ui/app/workspace/providers/fragments/apiKeysFormFragment.tsx
  • ui/app/workspace/virtual-keys/views/virtualKeyDetailsSheet.tsx
  • ui/app/workspace/virtual-keys/views/virtualKeySheet.tsx
  • ui/lib/schemas/providerForm.ts
  • ui/lib/store/apis/sessionApi.ts
  • ui/lib/types/config.ts
  • ui/lib/types/schemas.ts
  • ui/lib/utils/envVarForm.ts
  • ui/lib/utils/loginGoto.ts

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added MCP temporary token authentication support for SSO users
    • Introduced Access Profiles and MCP Tool Groups for enterprise
    • Added Kafka and Google Cloud Pub/Sub data connectors
  • Bug Fixes

    • Fixed streaming latency and cancellation races
    • Improved structured output and tool handling
    • Enhanced provider-specific conversions and mappings
  • Improvements

    • Azure OpenAI now uses v1 API endpoints without requiring api-version
    • Enhanced OpenTelemetry attribute forwarding for better observability
    • Improved error handling in data discovery APIs
  • Documentation

    • Expanded enterprise documentation with new guides
    • Updated configuration examples

Walkthrough

This PR migrates Azure handling to v1-style APIs, expands OTel tracing and env-var config marshalling, updates Anthropic and Bedrock structured-output behavior, adds MCP temp-token auth gating, surfaces provider-level quota configs, and refreshes related UI, Helm, OpenAPI, tests, and release docs.

Changes

Platform release updates

Layer / File(s) Summary
Tracing and env-var config marshalling
core/bifrost.go, core/mcp/pluginpipeline.go, core/schemas/*, framework/tracing/llmspan.go, plugins/otel/*, plugins/telemetry/*, docs/features/observability/*, ui/app/workspace/observability/*
Tracing now emits expanded OTel and bifrost.* attributes, config marshalling is pluggable for storage/API redaction, and observability config paths support env-var-backed fields in plugins, forms, and docs.
Azure v1 routing and config cleanup
core/providers/azure/*, core/schemas/account.go, framework/configstore/*, transports/config.schema.json, ui/lib/types/*, docs/providers/supported-providers/azure.mdx, tests/config*.json, examples/configs/*
Azure request construction switches to /openai/v1/..., api_version is removed from key shapes and storage, passthrough/realtime handling is updated, and supporting schemas, tests, configs, UI, and docs follow that change.
Anthropic, Bedrock, and Gemini conversions
core/providers/anthropic/*, core/providers/bedrock/*, core/providers/gemini/*, core/internal/llmtests/*
Structured-output and tool-mapping behavior is revised across Anthropic and Bedrock, Bedrock gains cache-point stripping and richer tool-result round-tripping, and Gemini tests now assert JSON-schema passthrough and updated grouped-role expectations.
Streaming safety and plugin runtime behavior
core/providers/utils/*, plugins/jsonparser/*, plugins/logging/*, framework/logstore/*, transports/bifrost-http/handlers/logging.go
Stream cancellation now marks closed connections before closing, Responses streaming JSON repair is supported, logging cleanup drains buffered work with bounded shutdown handling, and filter-data lookups propagate store errors.
Governance quotas and MCP temp-token auth
plugins/governance/*, framework/configstore/*, framework/oauth2/*, transports/bifrost-http/handlers/{config,governance,middlewares,session}.go, ui/app/workspace/config/views/mcpView.tsx, ui/app/workspace/mcp-sessions/auth/page.tsx
Blocked-model matching is refactored, quota responses include provider configs, MCP temp-token auth is added as persisted client config and runtime gating, and session/MCP UI paths expose the new auth state and setting.
Plugin API normalization and server wiring
transports/bifrost-http/lib/config.go, transports/bifrost-http/handlers/plugins*.go, transports/bifrost-http/server/server.go
Plugin loaders and server callbacks now normalize configs for storage, expand them for API responses, preserve stored redacted env-var values on update, and cache per-plugin config marshallers.
UI updates for login, observability, and virtual keys
ui/app/login/layout.tsx, ui/app/workspace/observability/..., ui/app/workspace/virtual-keys/..., ui/app/workspace/providers/fragments/apiKeysFormFragment.tsx, ui/lib/utils/loginGoto.ts
Login redirects now honor a validated goto path, enterprise observability adds Kafka/PubSub fallback views, observability forms use env-var inputs, Azure key forms require endpoint input, and virtual-key provider configs expose blocked models.
Helm chart and deployment wiring
helm-charts/bifrost/*, docs/deployment-guides/helm/storage.mdx
The chart adds secret-handling updates for PostgreSQL, optional gRPC ports, deterministic env rendering, Redis probe/auth changes, new client config wiring, updated values/schema, and matching install documentation.
Enterprise docs and management API specs
docs/enterprise/*, docs/openapi/**, docs/docs.json
Enterprise navigation and redirects are reorganized, new pages document access profiles, DAC, MCP tool groups, and migration topics, and OpenAPI adds RBAC, access profile, and MCP tool group management schemas and paths.
Release notes, schemas, and misc support updates
*/changelog.md, */version, transports/bifrost-http/handlers/utils.go, ui/lib/store/apis/sessionApi.ts, assorted tests`
Release artifacts are bumped, ResolvePeriod is exported, auth-enabled responses include auth_type, and supporting test and fixture updates align with the runtime changes.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related issues

  • maximhq/bifrost#3688 — Bedrock message conversion tests and conversion logic now explicitly keep reasoning content before tool-use blocks.

Possibly related PRs

  • maximhq/bifrost#3661 — Both PRs migrate Azure handling away from api-version-driven configuration and routes into Azure v1-style request construction.
  • maximhq/bifrost#3651 — Both PRs add ConfigMarshallerPlugin plumbing and use it to normalize and redact env-var-backed plugin configuration.
  • maximhq/bifrost#3720 — Both PRs add mcp_enable_temp_token_auth through config, persistence, middleware, OAuth, and UI wiring.

Suggested reviewers

  • danpiths
  • roroghost17

Poem

🐇 I patched the paths and traced the light,
From Azure roads to spans so bright.
With tools and tokens tucked in line,
And docs that hop in clearer time.
The burrow ships a fuller spring.

✨ 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 dev

## Summary

Briefly explain the purpose of this PR and the problem it solves.

## Changes

- What was changed and why
- Any notable design decisions or trade-offs

## 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

Describe the steps to validate this change. Include commands and expected outcomes.

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

# UI
cd ui
pnpm i || npm i
pnpm test || npm test
pnpm build || npm run build
```

If adding new configs or environment variables, document them here.

## Screenshots/Recordings

If UI changes, add before/after screenshots or short clips.

## Breaking changes

- [ ] Yes
- [ ] No

If yes, describe impact and migration instructions.

## Related issues

Link related issues and discussions. Example: Closes #123

## Security considerations

Note any security implications (auth, secrets, PII, sandboxing, etc.).

## 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
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.

8 participants