Conversation
…onfigs, and makes Provider Config weight optional for routing (#1932) ## Summary Changes Virtual Key provider and MCP configurations from "allow-all by default" to "deny-by-default" security model. Virtual Keys now require explicit provider and MCP client configurations to allow access, improving security posture. ## Changes - **Provider Configs**: Empty `provider_configs` now blocks all providers instead of allowing all - **MCP Configs**: Empty `mcp_configs` now blocks all MCP tools instead of allowing all - **Weight Field**: Changed provider `weight` from required `float64` to optional `*float64` - null weight excludes provider from weighted routing - **Migration**: Added automatic backfill migration to preserve existing Virtual Key behavior by adding all available providers/MCP clients to VKs with empty configs - **Documentation**: Updated all references to reflect new deny-by-default behavior - **UI Updates**: Modified Virtual Key creation/editing interface to reflect new behavior and weight handling ## Type of change - [x] Feature - [x] Refactor - [x] Documentation ## Affected areas - [x] Core (Go) - [x] Transports (HTTP) - [x] Plugins - [x] UI (Next.js) - [x] Docs ## How to test Test Virtual Key creation and provider/MCP access: ```sh # Core/Transports go version go test ./... # Test Virtual Key with no provider configs blocks requests curl -X POST http://localhost:8080/v1/chat/completions \ -H "Authorization: Bearer sk-bf-empty-vk" \ -H "Content-Type: application/json" \ -d '{"model": "gpt-4", "messages": [{"role": "user", "content": "test"}]}' # Should return error about no providers configured # Test Virtual Key with provider configs allows requests curl -X POST http://localhost:8080/v1/chat/completions \ -H "Authorization: Bearer sk-bf-configured-vk" \ -H "Content-Type: application/json" \ -d '{"model": "gpt-4", "messages": [{"role": "user", "content": "test"}]}' # Should work normally # UI cd ui pnpm i || npm i pnpm test || npm test pnpm build || npm run build ``` ## Breaking changes - [x] Yes **Impact**: Existing Virtual Keys with empty `provider_configs` or `mcp_configs` would be blocked after this change. **Migration**: Automatic migration `migrationBackfillEmptyVirtualKeyConfigs` runs on startup to backfill existing Virtual Keys with all available providers/MCP clients, preserving current behavior. New Virtual Keys created after this change will use deny-by-default. ## Security considerations This change significantly improves security posture by requiring explicit configuration of allowed providers and MCP tools for Virtual Keys. The automatic migration ensures no disruption to existing deployments while new Virtual Keys benefit from the more secure default behavior. ## 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 Adds a new configuration option `DisableAutoToolInject` to the MCP (Model Context Protocol) system that allows disabling automatic tool injection into requests. When enabled, MCP tools are only included when explicitly requested via context headers or filters, providing more granular control over tool availability. ## Changes - Added `DisableAutoToolInject` field to `MCPToolManagerConfig` schema with runtime update support - Implemented atomic boolean storage in `ToolsManager` to safely handle concurrent access - Added logic in `ParseAndAddToolsToRequest` to respect the disable flag and only inject tools when explicit context filters are present - Extended configuration management with database migration, UI controls, and API endpoints - Added hot-reload capability through `UpdateMCPDisableAutoToolInject` methods across the stack - Updated UI with a toggle switch and clear documentation about the feature's behavior ## Type of change - [x] Feature - [ ] Bug fix - [ ] Refactor - [ ] Documentation - [ ] Chore/CI ## Affected areas - [x] Core (Go) - [x] Transports (HTTP) - [ ] Providers/Integrations - [ ] Plugins - [x] UI (Next.js) - [ ] Docs ## How to test Validate the new MCP auto tool injection toggle: ```sh # Core/Transports go version go test ./... # UI cd ui pnpm i || npm i pnpm test || npm test pnpm build || npm run build ``` Test the feature: 1. Configure MCP clients and tools 2. Enable "Disable Auto Tool Injection" in the MCP configuration UI 3. Make requests without explicit tool headers - tools should not be injected 4. Make requests with `x-bf-mcp-include-tools` header - tools should be injected 5. Verify hot-reload works by toggling the setting without server restart ## Screenshots/Recordings UI changes include a new toggle switch in the MCP configuration view with descriptive text explaining when tools are injected based on explicit headers. ## Breaking changes - [ ] Yes - [x] No This is a backward-compatible addition with a default value of `false` (auto injection enabled). ## Related issues This addresses the need for more granular control over MCP tool injection behavior in request processing. ## Security considerations The feature provides better control over tool exposure by allowing administrators to require explicit opt-in for tool injection, potentially reducing unintended tool access. ## 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
This PR implements MCP tool governance by enforcing virtual key MCP configurations as an execution-time allow-list. When virtual keys have empty MCPConfigs, all MCP tools are denied. When non-empty, each tool is validated against the configured allow-list at both inference time and MCP tool execution.
## Changes
- **Context parameter updates**: Changed MCP-related functions to use `*schemas.BifrostContext` instead of `context.Context` to enable tool tracking
- **Tool tracking**: Added `BifrostContextKeyMCPAddedTools` context key to track which MCP tools are added to requests
- **Governance enforcement**: Virtual key MCP configurations now act as execution-time allow-lists with validation in both `PreMCPHook` and `evaluateGovernanceRequest`
- **Auto-injection control**: Added `DisableAutoToolInject` configuration option that respects the toggle and skips auto-injection when headers are already set by callers
- **Decision type**: Added `DecisionMCPToolBlocked` for MCP tool governance violations
- **UI improvements**: Updated MCP view description and sidebar item naming for better clarity
## Type of change
- [x] Feature
- [ ] Bug fix
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI
## Affected areas
- [x] Core (Go)
- [x] Transports (HTTP)
- [ ] Providers/Integrations
- [x] Plugins
- [x] UI (Next.js)
- [ ] Docs
## How to test
Test MCP tool governance with virtual keys:
```sh
# Core/Transports
go version
go test ./...
# Test with virtual key having empty MCPConfigs (should deny all MCP tools)
curl -X POST /v1/chat/completions \
-H "x-bf-virtual-key: test-vk-empty-mcp" \
-d '{"model": "gpt-4", "messages": [{"role": "user", "content": "test"}]}'
# Test with virtual key having specific MCP tool allowlist
curl -X POST /v1/chat/completions \
-H "x-bf-virtual-key: test-vk-with-mcp" \
-d '{"model": "gpt-4", "messages": [{"role": "user", "content": "test"}]}'
# Test disable auto tool inject configuration
curl -X PUT /v1/config/mcp/disable-auto-tool-inject \
-d '{"disable": true}'
# UI
cd ui
pnpm i || npm i
pnpm test || npm test
pnpm build || npm run build
```
New configuration options:
- `disable_auto_tool_inject`: Boolean flag to disable automatic MCP tool injection
- Virtual key `MCPConfigs`: Array of MCP client configurations that act as allow-lists
## Screenshots/Recordings
UI changes include updated MCP configuration view with clearer descriptions for the disable auto tool injection toggle and improved sidebar navigation labels.
## Breaking changes
- [x] Yes
- [ ] No
**Impact**: MCP-related function signatures now require `*schemas.BifrostContext` instead of `context.Context`. Virtual keys with empty MCPConfigs will now deny all MCP tools by default.
**Migration**: Update any custom MCP integrations to use the new context parameter type. Configure MCPConfigs on virtual keys that need MCP tool access.
## Related issues
Implements MCP tool governance and execution-time validation for virtual key configurations.
## Security considerations
- **Access control**: Virtual key MCP configurations now enforce strict allow-lists for tool execution
- **Context isolation**: Tool tracking is isolated per request context to prevent cross-request leakage
- **Validation**: Both pre-execution and execution-time validation prevent unauthorized tool access
## 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
…Allowed Keys (#2006) ## Summary Migrates VK provider config allowed keys from implicit allow-all semantics to explicit deny-by-default behavior. Adds `AllowAllKeys` boolean field to enable granular key access control while maintaining backward compatibility. ## Changes - Added `AllowAllKeys` boolean field to `TableVirtualKeyProviderConfig` with database migration - Backfilled existing configs with `allow_all_keys=true` to preserve current behavior - Updated key resolution logic: empty keys now denies all access, `["*"]` wildcard allows all keys - Modified governance resolver to set empty `includeOnlyKeys` slice when no keys are configured - Enhanced HTTP handlers to recognize `["*"]` wildcard and set `AllowAllKeys` flag appropriately - Updated UI to display "Allow All Keys" option and show deny-by-default messaging - Added JSON unmarshaling support for `["*"]` wildcard in config files ## Type of change - [x] Feature - [ ] Bug fix - [ ] Refactor - [ ] Documentation - [ ] Chore/CI ## Affected areas - [x] Core (Go) - [x] Transports (HTTP) - [ ] Providers/Integrations - [x] Plugins - [x] UI (Next.js) - [ ] Docs ## How to test Validate the migration and new key access control behavior: ```sh # Core/Transports go version go test ./... # Test migration runs successfully go run main.go migrate # UI cd ui pnpm i || npm i pnpm test || npm test pnpm build || npm run build ``` Test scenarios: 1. Create VK with empty `key_ids` - should deny all keys 2. Create VK with `key_ids: ["*"]` - should allow all keys 3. Create VK with specific key IDs - should allow only those keys 4. Verify existing VKs maintain their current behavior after migration ## Screenshots/Recordings UI now shows: - "Allow All Keys" option in key selection dropdown - "No keys allowed" vs "All keys allowed" status indicators - "No providers configured (deny-by-default)" messaging ## Breaking changes - [ ] Yes - [x] No The migration preserves existing behavior by setting `allow_all_keys=true` for configs that previously had no keys specified. ## Related issues Part of VK access control enhancement initiative. ## Security considerations Improves security posture by implementing deny-by-default semantics for key access. Existing deployments maintain current access patterns through automatic backfill migration. ## 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 Standardizes empty array conventions across Bifrost to implement deny-by-default security semantics. Previously, empty arrays for `allowed_models` and `Models` fields meant "allow all", creating potential security gaps. Now `["*"]` explicitly means "allow all" while empty arrays mean "deny all". ## Changes - **Core Logic**: Updated model filtering in `bifrost.go` and `selectKeyFromProviderForModel` to treat empty `Models` arrays as deny-all and `["*"]` as allow-all - **Database Migration**: Added `migrationBackfillAllowedModelsWildcard` to convert existing empty arrays to `["*"]` preserving current behavior for existing records - **Model Catalog**: Updated `IsModelAllowedForProvider` to use wildcard semantics with deny-by-default fallback - **Schema Defaults**: Changed default `Models` value from `[]` to `["*"]` in table definitions and form schemas - **UI Components**: Enhanced `ModelMultiselect` with `allowAllOption` prop and updated virtual key forms to handle wildcard selection - **Documentation**: Updated JSON schemas, comments, and tooltips to reflect new conventions - **Governance**: Updated provider config filtering logic to use new wildcard semantics - **Server Bootstrap**: Added wildcard filtering when loading models to prevent literal "*" from appearing as a model name ## Type of change - [x] Refactor - [ ] Bug fix - [ ] Feature - [ ] Documentation - [ ] Chore/CI ## Affected areas - [x] Core (Go) - [x] Transports (HTTP) - [x] Providers/Integrations - [x] Plugins - [x] UI (Next.js) - [x] Docs ## How to test Validate the migration and new semantics: ```sh # Core/Transports go version go test ./... # UI cd ui pnpm i || npm i pnpm test || npm test pnpm build || npm run build ``` Test scenarios: 1. Create new virtual keys - should default to `["*"]` for allowed models 2. Create new provider keys - should default to `["*"]` for models 3. Verify existing keys with empty arrays are migrated to `["*"]` 4. Test that empty arrays now deny all models/keys as expected 5. Verify UI shows "All models allowed" for wildcard and "No models (deny all)" for empty arrays ## Screenshots/Recordings UI changes include: - Model multiselect now shows "Allow All Models" option - Virtual key details display "All Models" badge for wildcard vs "No models (deny all)" for empty - Form placeholders updated to reflect new semantics ## Breaking changes - [x] Yes - [ ] No **Migration Impact**: The database migration automatically converts existing empty `allowed_models` and `models_json` arrays to `["*"]`, preserving current behavior. However, any new configurations with empty arrays will now deny access instead of allowing all. Applications relying on "empty = allow all" semantics must be updated to use `["*"]` explicitly. ## Related issues Part of security hardening initiative to implement explicit allow-lists and deny-by-default semantics across Bifrost configuration. ## Security considerations This change significantly improves security posture by: - Eliminating ambiguous "empty means allow all" semantics - Implementing explicit deny-by-default for new configurations - Requiring intentional wildcard usage via `["*"]` for broad access - Maintaining backward compatibility through automatic migration ## 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
…2125) ## Summary Introduces a new `WhiteList` type to standardize whitelist behavior across the codebase, replacing manual slice operations and string comparisons with semantic methods for handling allow/deny lists. ## Changes - Added `WhiteList` type with methods `IsAllowed()`, `IsUnrestricted()`, `IsEmpty()`, `Contains()`, and `Validate()` - Replaced `[]string` fields with `WhiteList` for model restrictions, tool filtering, and key access controls - Updated all whitelist logic to use semantic methods instead of manual `slices.Contains()` checks - Added validation to ensure wildcards ("*") aren't mixed with specific values and prevent duplicates - Improved case-insensitive matching for whitelist comparisons ## Type of change - [x] Refactor ## Affected areas - [x] Core (Go) - [x] Transports (HTTP) - [x] Plugins ## How to test Verify that whitelist behavior remains consistent across all affected components: ```sh # Core/Transports go version go test ./... # Test specific whitelist scenarios: # - Empty lists deny all access # - ["*"] allows all access # - Specific lists only allow listed items # - Mixed wildcards and specific items are rejected # - Duplicate entries are rejected ``` Test key model filtering, MCP tool execution, and virtual key configurations to ensure whitelist logic works correctly. ## Screenshots/Recordings N/A ## Breaking changes - [ ] Yes - [x] No The `WhiteList` type maintains the same JSON serialization format as `[]string`, so existing configurations remain compatible. ## Related issues N/A ## Security considerations Improves security by standardizing deny-by-default behavior and adding validation to prevent misconfigured whitelists that could inadvertently grant excessive permissions. ## 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
…2126) ## Summary This PR adds support for request-level extra headers in MCP tool execution, allowing callers to forward specific headers to MCP servers at runtime based on a per-client allowlist configuration. ## Changes - Added `AllowedExtraHeaders` field to MCP client configuration with allowlist semantics (empty array = deny all, `["*"]` = allow all) - Introduced `BifrostContextKeyMCPExtraHeaders` context key to track headers forwarded to MCP tools - Created `core/mcp/utils` package with `GetHeadersForToolExecution` function to merge static and dynamic headers - Updated MCP tool execution in both regular tool manager and Starlark code mode to use the new header forwarding system - Added database migration for `allowed_extra_headers_json` column in MCP client table - Updated UI to include allowed extra headers configuration in MCP client management - Enhanced auth demo server example to demonstrate tool-execution level authentication patterns ## Type of change - [x] Feature ## Affected areas - [x] Core (Go) - [x] Transports (HTTP) - [x] UI (Next.js) ## How to test 1. Configure an MCP client with allowed extra headers: ```json { "name": "test-client", "connection_string": "http://localhost:3002/", "auth_type": "headers", "headers": { "X-API-Key": "connection-secret" }, "allowed_extra_headers": ["X-Tool-Token"], "tools_to_execute": ["*"] } ``` 2. Make requests with extra headers that should be forwarded: ```bash curl -X POST http://localhost:8080/v1/chat/completions \ -H "Authorization: Bearer your-key" \ -H "X-Tool-Token: tool-execution-secret" \ -d '{ "model": "gpt-4", "messages": [{"role": "user", "content": "Use the secret_data tool"}], "tools": [{"type": "function", "function": {"name": "secret_data"}}] }' ``` 3. Test the auth demo server: ```bash cd examples/mcps/auth-demo-server go run main.go # Server demonstrates two-tier auth: connection-level (X-API-Key) and tool-level (X-Tool-Token) ``` 4. Run tests: ```sh go test ./core/mcp/... go test ./transports/bifrost-http/... cd ui pnpm test pnpm build ``` ## Breaking changes - [ ] Yes - [x] No This is a backward-compatible addition. Existing MCP clients will have empty `allowed_extra_headers` (deny all extra headers) which maintains current behavior. ## Security considerations - Extra headers are filtered through a strict allowlist per MCP client - Security denylist prevents auth header overrides via extra headers - Two-tier authentication pattern demonstrated: connection-level + tool-execution level - Headers are only forwarded to MCP servers that explicitly allow them ## 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
…ng bifrost as MCP gateway (#2127) ## Summary Adds support for `x-bf-mcp-include-clients` and `x-bf-mcp-include-tools` request headers to filter MCP tools/list response when using Bifrost as an MCP gateway. This ensures that tool filtering is respected at the MCP protocol level, not just during inference. ## Changes - Implemented dynamic tool filtering in MCP server handlers that respects per-request include headers - Added `makeIncludeClientsFilter()` function that filters tools based on request context values - Registered the tool filter on both global and virtual key MCP servers during initialization - Updated documentation to clarify that `mcp-include-tools` requires `clientName-toolName` format - Enhanced examples in documentation to show proper tool naming format ## Type of change - [x] Bug fix - [ ] Feature - [ ] Refactor - [ ] Documentation - [ ] Chore/CI ## Affected areas - [x] Core (Go) - [x] Transports (HTTP) - [ ] Providers/Integrations - [ ] Plugins - [ ] UI (Next.js) - [x] Docs ## How to test Test MCP gateway functionality with tool filtering: ```sh # Test tools/list filtering with include-tools header curl --location 'http://localhost:8080/mcp/tools/list' \ --header 'x-bf-mcp-include-tools: gmail-send_email,filesystem-read_file' \ --header 'Authorization: Bearer your-vk-here' # Test tools/list filtering with include-clients header curl --location 'http://localhost:8080/mcp/tools/list' \ --header 'x-bf-mcp-include-clients: gmail,filesystem' \ --header 'Authorization: Bearer your-vk-here' # Verify chat completions still respect the same headers curl --location 'http://localhost:8080/v1/chat/completions' \ --header 'x-bf-mcp-include-tools: gmail-send_email' \ --header 'Content-Type: application/json' \ --data '{ "model": "openai/gpt-4o-mini", "messages": [{"role": "user", "content": "What tools are available?"}] }' ``` ## Screenshots/Recordings N/A ## Breaking changes - [ ] Yes - [x] No ## Related issues N/A ## Security considerations The tool filtering mechanism ensures that virtual key restrictions are properly enforced at the MCP protocol level, preventing unauthorized access to tools that should be filtered out based on request headers. ## 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
… time (#2151) ## Summary Parallelizes model listing operations for providers during server startup and provider reloading to significantly reduce initialization time. Previously, model listing was performed sequentially for each provider, causing slower startup times especially when multiple providers were configured. ## Changes - Added concurrent execution using goroutines and sync.WaitGroup for model listing operations in three key functions: `ReloadProvider`, `ForceReloadPricing`, and `Bootstrap` - In `ReloadProvider`, both filtered and unfiltered model listing requests now run concurrently for the same provider - In `ForceReloadPricing` and `Bootstrap`, model listing for different providers now runs in parallel instead of sequentially - Moved provider key retrieval earlier in `ReloadProvider` to ensure it happens before concurrent model listing - Added proper context cancellation with defer statements for bifrost contexts ## Type of change - [x] Refactor ## Affected areas - [x] Transports (HTTP) ## How to test Test server startup time with multiple providers configured to verify the performance improvement: ```sh # Core/Transports go version go test ./... # Test with multiple providers configured # Measure startup time before and after the change time go run main.go ``` Configure multiple providers in your bifrost configuration and observe faster startup times, especially noticeable when providers have high latency or many models. ## Screenshots/Recordings N/A ## Breaking changes - [ ] Yes - [x] No ## Related issues N/A ## Security considerations No security implications. The change maintains the same authentication and authorization patterns while improving performance through parallelization. ## 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
…provider configs (#2158) ## Summary Fixes database migration ordering issue and ensures virtual key configurations are properly initialized with the AllowAllKeys field set to true. ## Changes - Reordered database migrations to execute `migrationAddAllowAllKeysToProviderConfig` before `migrationBackfillEmptyVirtualKeyConfigs` to ensure the AllowAllKeys column exists before backfilling - Added `AllowAllKeys: true` to provider configurations created during virtual key backfill migration to enable unrestricted key access by default ## Type of change - [x] Bug fix - [ ] Feature - [ ] Refactor - [ ] Documentation - [ ] Chore/CI ## Affected areas - [x] Core (Go) - [ ] Transports (HTTP) - [ ] Providers/Integrations - [ ] Plugins - [ ] UI (Next.js) - [ ] Docs ## How to test Verify that database migrations run successfully and virtual key configurations are created with proper defaults: ```sh # Core/Transports go version go test ./... ``` Test migration ordering by running against a fresh database to ensure no column reference errors occur. ## Screenshots/Recordings N/A ## Breaking changes - [ ] Yes - [x] No ## Related issues N/A ## Security considerations This change enables unrestricted key access by default for virtual key configurations, which may have security implications depending on the intended access control model. ## 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
…ns (#2230) ## Summary Resolves Git merge conflicts in the bifrost-http configuration loading code by cleaning up duplicate function definitions and consolidating the configuration initialization flow. ## Changes - Removed Git merge conflict markers and duplicate code blocks from `LoadConfig` function - Consolidated governance configuration loading by keeping both `loadGovernanceConfigFromFile` and `loadGovernanceConfig` functions with distinct purposes - Removed duplicate `convertSchemasMCPClientConfigToTable` function definition - Moved pricing overrides initialization logic to `initFrameworkConfig` function for better organization - Cleaned up extensive duplicate default configuration loading code that was causing merge conflicts - Changed error handling for pricing overrides from returning error to logging warning ## Type of change - [x] Bug fix - [ ] Feature - [ ] Refactor - [ ] Documentation - [ ] Chore/CI ## Affected areas - [x] Core (Go) - [x] Transports (HTTP) - [ ] Providers/Integrations - [ ] Plugins - [ ] UI (Next.js) - [ ] Docs ## How to test Verify that configuration loading works correctly without merge conflicts: ```sh # Core/Transports go version go test ./... go build ./transports/bifrost-http/... ``` Test configuration loading with various scenarios: - Config file present - Config file absent (default loading) - Store-based configuration - Governance and MCP configuration loading ## Screenshots/Recordings N/A ## Breaking changes - [ ] Yes - [x] No ## Related issues N/A ## Security considerations No security implications - this is a merge conflict resolution that maintains existing functionality. ## 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 Adds support for Stability AI image generation models (stability.stable-image-*) to the Bedrock provider, enabling text-to-image generation with models like stability.stable-image-core-v1:1 and stability.stable-image-ultra-v1:1. ## Changes - Added `isStabilityAIModel()` function to detect Stability AI models by "stability." prefix - Created `ToStabilityAIImageGenerationRequest()` to convert Bifrost requests to Stability AI's flat request format - Implemented `StabilityAIImageGenerationRequest` type with support for prompt, mode, aspect_ratio, output_format, seed, and negative_prompt parameters - Added conditional routing in `ImageGeneration()` to use Stability AI request format when appropriate - Extended known fields for image generation parameters to include "aspect_ratio" and "input_images" - Updated documentation comment to reflect Stability AI model support ## Type of change - [x] Feature - [ ] Bug fix - [ ] Refactor - [ ] Documentation - [ ] Chore/CI ## Affected areas - [x] Core (Go) - [x] Transports (HTTP) - [x] Providers/Integrations - [ ] Plugins - [ ] UI (Next.js) - [ ] Docs ## How to test Test Stability AI image generation through the Bedrock provider: ```sh # Core/Transports go version go test ./... # Test with a Stability AI model curl -X POST http://localhost:8080/v1/images/generations \ -H "Content-Type: application/json" \ -H "Authorization: Bearer your-key" \ -d '{ "model": "stability.stable-image-core-v1:1", "prompt": "A beautiful sunset over mountains", "aspect_ratio": "16:9", "output_format": "PNG" }' ``` Ensure AWS credentials are configured for Bedrock access and the Stability AI models are available in your region. ## Screenshots/Recordings N/A ## Breaking changes - [ ] Yes - [x] No ## Related issues N/A ## Security considerations No additional security implications beyond existing Bedrock provider authentication and AWS credential handling. ## 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
…2225) ## Summary Adds support for Stability AI image editing models in the Bedrock provider, expanding image editing capabilities beyond the existing Titan and Nova Canvas models. ## Changes - Added `getStabilityAIEditTaskType()` function to infer edit task types from Stability AI model names (inpaint, outpaint, recolor, search-replace, erase-object, remove-bg, control-sketch, control-structure, style-guide, style-transfer, upscale-creative, upscale-conservative, upscale-fast) - Created `ToStabilityAIImageEditRequest()` function to convert Bifrost requests to Stability AI's flat JSON format, with task-specific field validation - Added `StabilityAIImageEditRequest` struct with comprehensive field support for all Stability AI edit operations - Enhanced `BedrockImageGenerationResponse` with Seeds and FinishReasons fields for Stability AI compatibility - Modified `ImageEdit()` method to route requests to appropriate conversion function based on model type - Updated documentation to reflect expanded model support ## Type of change - [x] Feature - [ ] Bug fix - [ ] Refactor - [ ] Documentation - [ ] Chore/CI ## Affected areas - [x] Core (Go) - [ ] Transports (HTTP) - [x] Providers/Integrations - [ ] Plugins - [ ] UI (Next.js) - [ ] Docs ## How to test Test with various Stability AI edit models through the Bedrock provider: ```sh # Core/Transports go version go test ./... # Test image editing with Stability AI models # Example: stable-image-inpaint, stable-outpaint, stable-creative-upscale, etc. ``` Verify that task-specific parameters are correctly mapped and invalid fields are filtered out based on the detected task type. ## Screenshots/Recordings N/A - Backend functionality only ## Breaking changes - [ ] Yes - [x] No ## Related issues N/A ## Security considerations Image data is handled as base64-encoded strings. Mask and image parameters are properly validated before processing. ## 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
|
|
🧪 Test Suite AvailableThis PR can be tested by a repository admin. |
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 6 minutes and 8 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (43)
📝 WalkthroughWalkthroughAdded pooled plugin‑scoped contexts and plugin log draining; centralized model-listing via a ListModelsPipeline with WhiteList/BlackList and aliases/backfill; extended MCP/ToolsManager for per-user OAuth, header forwarding, and disable-auto-inject; many providers stopped injecting provider/request metadata into errors/ExtraFields; clarified resolve-pr-comments skill as local-only. (48 words) Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Client as Client
participant Bifrost as Bifrost
participant ToolsMgr as ToolsManager
participant OAuth as OAuthProvider
participant MCPClient as MCPClient
participant Tracer as Tracer
Note over Client,Bifrost: Incoming request with BifrostContext (may include user identity / include-filters)
Client->>Bifrost: POST /api (ctx *schemas.BifrostContext, req)
Bifrost->>ToolsMgr: ParseAndAddToolsToRequest(ctx, req)
ToolsMgr->>ToolsMgr: Check disableAutoToolInject & ctx include filters
alt per-user OAuth required
ToolsMgr->>OAuth: VerifyPerUserOAuthConnection(ctx, config, accessToken)
OAuth-->>ToolsMgr: discovered tools or authorize URL/session
ToolsMgr->>MCPClient: Execute tool call with temporary client (Bearer token)
else standard execution
ToolsMgr->>MCPClient: Execute tool call (persistent conn with headers from GetHeadersForToolExecution)
end
MCPClient-->>ToolsMgr: Tool result or MCPUserOAuthRequiredError
ToolsMgr-->>Bifrost: Attach tool result to request flow
Bifrost->>Bifrost: DrainPluginLogs(ctx.DrainPluginLogs)
Bifrost->>Tracer: AttachPluginLogs(traceID, logs)
Bifrost-->>Client: Final response (with plugin logs attached in trace/extra)
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120+ minutes Possibly related PRs
Suggested reviewers
Poem
✨ Finishing Touches🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Actionable comments posted: 20
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (9)
core/schemas/provider.go (1)
462-467:⚠️ Potential issue | 🟠 MajorClone
RequestPathOverridesinCheckAndSetDefaults()too.Line 462 already does the right thing for
ExtraHeaders, butCustomProviderConfig.RequestPathOverridesis still shared by reference. A post-init mutation there can race with live requests and change routing unexpectedly.♻️ Proposed fix
// Create a defensive copy of ExtraHeaders to prevent data races if config.NetworkConfig.ExtraHeaders != nil { headersCopy := make(map[string]string, len(config.NetworkConfig.ExtraHeaders)) maps.Copy(headersCopy, config.NetworkConfig.ExtraHeaders) config.NetworkConfig.ExtraHeaders = headersCopy } + if config.CustomProviderConfig != nil && config.CustomProviderConfig.RequestPathOverrides != nil { + overridesCopy := make(map[RequestType]string, len(config.CustomProviderConfig.RequestPathOverrides)) + maps.Copy(overridesCopy, config.CustomProviderConfig.RequestPathOverrides) + config.CustomProviderConfig.RequestPathOverrides = overridesCopy + } }As per coding guidelines, "Deep-copy map fields in config structs using
maps.Copy()inCheckAndSetDefaults()to prevent data races between concurrent requests."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@core/schemas/provider.go` around lines 462 - 467, CheckAndSetDefaults() currently defensively copies NetworkConfig.ExtraHeaders but does not copy CustomProviderConfig.RequestPathOverrides, leaving RequestPathOverrides shared by reference and susceptible to post-init mutation races; update CheckAndSetDefaults() to clone RequestPathOverrides when non-nil by allocating a new map with the same length and using maps.Copy (similar to the headersCopy logic) and assign that new map back to config.CustomProviderConfig.RequestPathOverrides so runtime mutations cannot affect the original shared map.docs/features/governance/routing.mdx (1)
84-99:⚠️ Potential issue | 🟡 MinorKeep this
jsonexample valid JSON.Lines 90 and 95 add
//comments inside ajsoncode fence, so readers can't paste this intoconfig.jsonas shown. Move the explanation into surrounding prose, or switch the fence to a comment-capable format if the docs renderer supports it.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/features/governance/routing.mdx` around lines 84 - 99, The JSON example contains inline `//` comments which make it invalid JSON (see the `allowed_models": ["*"]` entries); either remove the `//` comments from inside the ```json``` fence so the snippet remains valid JSON, or change the code fence to a comment-capable format supported by the renderer (for example `jsonc` or `js`) and keep the explanatory text outside the object; update the surrounding prose to explain that `["*"]` allows all and that validation is handled by the Model Catalog so readers can paste the shown object into config.json without syntax errors.transports/bifrost-http/handlers/mcp.go (1)
199-214:⚠️ Potential issue | 🟠 MajorPaginated MCP client responses still drop
allowed_extra_headers.
addMCPClientandupdateMCPClientnow persist this field, but the paginated GET path rebuildsschemas.MCPClientConfigwithout it. As soon as the UI useslimit,offset, orsearch, the returned config is incomplete.♻️ Proposed fix
clientConfig := &schemas.MCPClientConfig{ ID: dbClient.ClientID, Name: dbClient.Name, IsCodeModeClient: dbClient.IsCodeModeClient, ConnectionType: schemas.MCPConnectionType(dbClient.ConnectionType), ConnectionString: dbClient.ConnectionString, StdioConfig: dbClient.StdioConfig, AuthType: schemas.MCPAuthType(dbClient.AuthType), OauthConfigID: dbClient.OauthConfigID, ToolsToExecute: dbClient.ToolsToExecute, ToolsToAutoExecute: dbClient.ToolsToAutoExecute, Headers: dbClient.Headers, + AllowedExtraHeaders: dbClient.AllowedExtraHeaders, IsPingAvailable: &isPingAvailable, ToolSyncInterval: time.Duration(dbClient.ToolSyncInterval) * time.Minute, ToolPricing: dbClient.ToolPricing, }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@transports/bifrost-http/handlers/mcp.go` around lines 199 - 214, The paginated GET builds a schemas.MCPClientConfig (in the handler that creates clientConfig) but omits the AllowedExtraHeaders field, so configs returned for paginated queries drop that data; update the clientConfig construction to map dbClient.AllowedExtraHeaders into schemas.MCPClientConfig.AllowedExtraHeaders (same place where ToolPricing, Headers, etc. are set) so paginated GET responses include allowed_extra_headers (consistent with addMCPClient/updateMCPClient persistence).transports/bifrost-http/handlers/providers.go (1)
741-775:⚠️ Potential issue | 🟠 MajorUnknown key IDs should not widen the result set to all models.
If
keys=is present but every ID is stale or belongs to another provider, this branch still falls back to the full model list. That makesGET /api/models?keys=...behave like allow-all instead of “models accessible by these keys.”♻️ Proposed fix
allowedModels := make(map[string]bool) hasRestrictedKey := false hasUnrestrictedKey := false hasDenyAllKey := false + matchedAnyKey := false for _, keyID := range keyIDs { for _, key := range config.Keys { if key.ID == keyID { + matchedAnyKey = true if key.Models.IsUnrestricted() { // Key allows all models (wildcard) hasUnrestrictedKey = true @@ // If no keys were matched or restricted, but at least one key explicitly denies all, return nothing if !hasRestrictedKey && hasDenyAllKey { return []string{} } - // If no keys have model restrictions (e.g., unknown key IDs), return all models + // Unknown/stale key IDs should not expand access. if !hasRestrictedKey { + if len(keyIDs) > 0 && !matchedAnyKey { + return []string{} + } return models }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@transports/bifrost-http/handlers/providers.go` around lines 741 - 775, The current logic treats unknown/stale key IDs as "no keys provided" and returns the full model list; add tracking for whether any provided key ID was matched (e.g., a boolean anyMatched or matchedCount incremented inside the key.ID == keyID branch) and use it to change the fallback: after the loop, if len(keyIDs) > 0 && !anyMatched return an empty slice (no models) so stale/foreign key IDs do not widen access; also adjust the final fallback condition that currently returns models when !hasRestrictedKey to only do so when no key IDs were supplied (e.g., if !hasRestrictedKey && len(keyIDs) == 0 return models) and keep the existing hasUnrestrictedKey and hasDenyAllKey handling intact (refer to variables keyIDs, config.Keys, hasRestrictedKey, hasUnrestrictedKey, hasDenyAllKey, allowedModels, and models).ui/components/ui/modelMultiselect.tsx (1)
114-165:⚠️ Potential issue | 🟡 MinorAdd
unfilteredto theloadOptionsdependency array.The callback reads
unfilteredin thegetModelsrequest payload (line 148) but it's missing from the dependency array (line 164). If the prop changes, subsequent search requests will use the previous value until another dependency triggers a recreation.Fix
- [getModels, getBaseModels, provider, keys, shouldLoadOnEmpty, shouldUseBaseModels, allowAllOption], + [getModels, getBaseModels, provider, keys, shouldLoadOnEmpty, shouldUseBaseModels, allowAllOption, unfiltered],🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ui/components/ui/modelMultiselect.tsx` around lines 114 - 165, The loadOptions callback captures the unfiltered variable but the dependency array for useCallback omits it, so changes to unfiltered won't recreate loadOptions; update the dependency array for the useCallback that defines loadOptions to include unfiltered alongside getModels, getBaseModels, provider, keys, shouldLoadOnEmpty, shouldUseBaseModels, and allowAllOption so the closure sees the latest unfiltered value when issuing getModels requests.framework/configstore/tables/virtualkey.go (1)
47-76:⚠️ Potential issue | 🟠 MajorKeep the old
allowed_keysconfig input working for at least one release.This unmarshaller no longer reads the previous
allowed_keysfield, so existingconfig.jsonentries will deserialize toAllowAllKeys=falseandKeys=nilafter upgrade. With the new deny-by-default semantics, that silently blocks every provider key for affected virtual keys unless every config file is migrated in lockstep.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@framework/configstore/tables/virtualkey.go` around lines 47 - 76, The custom UnmarshalJSON on TableVirtualKeyProviderConfig must preserve the old allowed_keys field: update UnmarshalJSON (and TempProviderConfig) to also read an AllowedKeys []string `json:"allowed_keys"` and, when temp.KeyIDs is empty, use temp.AllowedKeys to populate pc.Keys or set pc.AllowAllKeys (treat ["*"] as allow all) so existing configs deserialize unchanged; ensure key_ids takes precedence over allowed_keys if both are present and keep the existing logic that skips conversion when pc.Keys is already set.ui/app/workspace/virtual-keys/views/virtualKeySheet.tsx (1)
747-852:⚠️ Potential issue | 🟡 MinorAdd stable selectors for the new wildcard key/tool controls.
These changed pickers drive the new allow-all flows, but they still don't expose
data-testids the way the surrounding controls do. That makes the new behavior much harder to cover in E2E.As per coding guidelines
ui/**/*.{tsx,ts}: Add new interactive UI elements with data-testid attributes following the pattern: data-testid="<entity>-<element>-<qualifier>".Also applies to: 1058-1098
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ui/app/workspace/virtual-keys/views/virtualKeySheet.tsx` around lines 747 - 852, Add stable data-testid attributes to the new wildcard key/tool pickers so E2E can target them: in the AsyncMultiSelect instance used for "Allowed Keys" (component AsyncMultiSelect) add data-testid="virtualkey-allowedkeys-select"; inside its custom multiValue renderer (multiValueProps) add data-testid="virtualkey-allowedkeys-chip" to the outer div and data-testid="virtualkey-allowedkeys-chip-remove" to the X remove button; inside the custom option renderer (optionProps) add data-testid="virtualkey-allowedkeys-option" to the Option wrapper or its label span; and repeat the same pattern for the other AsyncMultiSelect instance later in the file that implements the wildcard behavior so both pickers follow the data-testid pattern.core/bifrost.go (1)
3416-3420:⚠️ Potential issue | 🟠 MajorThese exported MCP API changes are source-incompatible.
GetAvailableMCPToolsnow requires*schemas.BifrostContext, andUpdateToolManagerConfigadds a positionalbool. Both changes compile-break existing callers; the first also stops following the usual nil-context fallback used by the rest ofBifrost. Prefer keeping the current exported signatures and converting/wrapping internally, or add new opt-in methods instead.Also applies to: 3543-3555
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@core/bifrost.go` around lines 3416 - 3420, The change to exported APIs is source-incompatible: restore the previous public signatures for GetAvailableMCPTools and UpdateToolManagerConfig and perform any new-argument conversion internally; specifically, revert GetAvailableMCPTools to accept the original parameter type (or no context) and inside that method convert or construct a *schemas.BifrostContext before calling bifrost.MCPManager.GetAvailableTools so you preserve the existing nil-context fallback behavior used across Bifrost, and for UpdateToolManagerConfig remove the new positional bool from the exported signature (or add a new opt-in method like UpdateToolManagerConfigWithOptions) and handle the boolean internally or via the new opt-in function so existing callers continue to compile. Ensure references to GetAvailableMCPTools, UpdateToolManagerConfig, and Bifrost.MCPManager are updated accordingly.framework/configstore/tables/key.go (1)
479-483:⚠️ Potential issue | 🟠 MajorReset
ModelswhenModelsJSONis empty to avoid stale runtime state.Line 479 currently skips assignment when
ModelsJSON == "", butModelsis a non-persisted field (gorm:"-"). If the same struct instance is reused, the previous row’sModelscan leak into the current row.💡 Proposed fix
- if k.ModelsJSON != "" { - if err := json.Unmarshal([]byte(k.ModelsJSON), &k.Models); err != nil { - return err - } - } + if k.ModelsJSON != "" { + if err := json.Unmarshal([]byte(k.ModelsJSON), &k.Models); err != nil { + return err + } + } else { + k.Models = nil + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@framework/configstore/tables/key.go` around lines 479 - 483, When deserializing into the struct, ensure k.Models is cleared when k.ModelsJSON is empty to avoid leaking previous in-memory state; inside the block that currently only unmarshals when k.ModelsJSON != "" (referencing k.ModelsJSON and k.Models) set k.Models = nil or an empty slice in the else case (or before the conditional) so that reused instances don't keep stale Models data.
🟡 Minor comments (13)
ui/app/workspace/logs/sheets/logDetailsSheet.tsx-173-187 (1)
173-187:⚠️ Potential issue | 🟡 MinorAdd missing test selectors on modified dropdown controls.
Line 173 (
Buttontrigger) and Line 184 (DropdownMenuItemfor delete) are interactive controls in this changed block but don’t exposedata-testid, which makes E2E targeting brittle in this sheet.Suggested patch
- <Button variant="ghost" size="icon"> + <Button variant="ghost" size="icon" data-testid="log-details-menu-button"> <MoreVertical className="h-3 w-3" /> </Button> ... - <DropdownMenuItem variant="destructive"> + <DropdownMenuItem variant="destructive" data-testid="log-details-delete-log-button"> <Trash2 className="h-4 w-4" /> Delete log </DropdownMenuItem>As per coding guidelines
ui/**/*.{tsx,ts}: “Add new interactive UI elements withdata-testidattributes following the pattern:data-testid="<entity>-<element>-<qualifier>".”🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ui/app/workspace/logs/sheets/logDetailsSheet.tsx` around lines 173 - 187, The dropdown trigger Button (component using MoreVertical) and the destructive DropdownMenuItem wrapped by AlertDialogTrigger (the "Delete log" item) lack data-testid attributes; add data-testid attributes to both following the project pattern (e.g., data-testid="logdetails-dropdown-trigger" for the Button and data-testid="logdetails-delete-button" for the delete DropdownMenuItem) so E2E tests can reliably target them—ensure you modify the Button element and the DropdownMenuItem (the one that triggers the AlertDialog for deletion) and keep existing handlers like copyRequestBody(displayLog) unchanged.docs/mcp/filtering.mdx-230-230 (1)
230-230:⚠️ Potential issue | 🟡 MinorDocument auto-generated MCP include header as conditional.
Line 230 currently reads as unconditional override behavior. With the new
DisableAutoToolInjectwiring, automaticx-bf-mcp-include-toolsinjection/override should be documented as default behavior, not guaranteed behavior.✏️ Suggested wording
-When a Virtual Key has no MCP configurations, **no MCP tools are available** (deny-by-default). You must explicitly add MCP client configurations to allow tools. When a Virtual Key has MCP configurations, it generates the `x-bf-mcp-include-tools` header automatically, overriding any manually sent header. +When a Virtual Key has no MCP configurations, **no MCP tools are available** (deny-by-default). You must explicitly add MCP client configurations to allow tools. By default, when a Virtual Key has MCP configurations, Bifrost generates the `x-bf-mcp-include-tools` header automatically, overriding any manually sent header. If `mcp_disable_auto_tool_inject` is enabled, this automatic injection/override does not occur.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/mcp/filtering.mdx` at line 230, Update the sentence that claims the `x-bf-mcp-include-tools` header is always generated/overrides manual headers to state it is the default behavior unless auto-injection is disabled; mention the new DisableAutoToolInject wiring (or flag) as the mechanism to disable automatic injection, e.g., change the unconditional phrasing to: "By default a Virtual Key with MCP configurations generates the `x-bf-mcp-include-tools` header, overriding any manually sent header, unless auto-injection is disabled via DisableAutoToolInject." Ensure the doc text around "no MCP tools are available (deny-by-default)" and the sentence referencing `x-bf-mcp-include-tools` reflects this conditional/default behavior.docs/providers/request-options.mdx-32-32 (1)
32-32:⚠️ Potential issue | 🟡 MinorDocument bare
*support and use the exported Go context key constant.The table and prose now describe only
clientName-toolNameandclientName-*formats, but the codebase treats bare["*"]as the global allow-all sentinel (documented inchangelog.mdand used in filtering semantics). Additionally, the Go SDK example usesschemas.BifrostContextKey("mcp-include-tools")while the codebase defines and uses the typed constantschemas.MCPContextKeyIncludeToolsfor all MCP context operations, and all other context keys in the docs use this typed constant pattern.📝 Proposed doc fix
-| `mcp-include-tools` | `x-bf-mcp-include-tools` | `[]string` | Filter MCP tools (`clientName-toolName` format, comma-separated) | +| `mcp-include-tools` | `x-bf-mcp-include-tools` | `[]string` | Filter MCP tools (`*`, `clientName-*`, or `clientName-toolName`, comma-separated) |-Filter MCP tools to include only the specified ones. Values must use the `clientName-toolName` format (e.g. `gmail-send_email`). Use `clientName-*` to include all tools from a client. +Filter MCP tools to include only the specified ones. Values can be `*` to include all tools, `clientName-*` to include all tools from one client, or `clientName-toolName` (for example `gmail-send_email`) to include a specific tool.-ctx = context.WithValue(ctx, schemas.BifrostContextKey("mcp-include-tools"), []string{"gmail-send_email", "filesystem-read_file"}) +ctx = context.WithValue(ctx, schemas.MCPContextKeyIncludeTools, []string{"gmail-send_email", "filesystem-read_file"})🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/providers/request-options.mdx` at line 32, Update the docs for the `mcp-include-tools` (`x-bf-mcp-include-tools`) option to mention that a bare ["*"] is a global allow-all sentinel (in addition to `clientName-*` and `clientName-toolName`), and update the Go SDK example to use the exported typed context key `schemas.MCPContextKeyIncludeTools` instead of `schemas.BifrostContextKey("mcp-include-tools")` so it matches the codebase’s context-key usage and filtering semantics.transports/bifrost-http/handlers/inference.go-749-765 (1)
749-765:⚠️ Potential issue | 🟡 MinorAvoid returning
pricing: {}when no price field is set.This now allocates
&schemas.Pricing{}up front, so models with a catalog entry but no concrete costs start serializing an empty pricing object instead of omitting the field. That changes the wire shape and can look like pricing is available when it isn't.💡 Suggested fix
- pricing := &schemas.Pricing{} + var pricing *schemas.Pricing + ensurePricing := func() *schemas.Pricing { + if pricing == nil { + pricing = &schemas.Pricing{} + } + return pricing + } if pricingEntry.InputCostPerToken != nil { - pricing.Prompt = bifrost.Ptr(fmt.Sprintf("%.10f", *pricingEntry.InputCostPerToken)) + ensurePricing().Prompt = bifrost.Ptr(fmt.Sprintf("%.10f", *pricingEntry.InputCostPerToken)) } if pricingEntry.OutputCostPerToken != nil { - pricing.Completion = bifrost.Ptr(fmt.Sprintf("%.10f", *pricingEntry.OutputCostPerToken)) + ensurePricing().Completion = bifrost.Ptr(fmt.Sprintf("%.10f", *pricingEntry.OutputCostPerToken)) } if pricingEntry.InputCostPerImage != nil { - pricing.Image = bifrost.Ptr(fmt.Sprintf("%.10f", *pricingEntry.InputCostPerImage)) + ensurePricing().Image = bifrost.Ptr(fmt.Sprintf("%.10f", *pricingEntry.InputCostPerImage)) } if pricingEntry.CacheReadInputTokenCost != nil { - pricing.InputCacheRead = bifrost.Ptr(fmt.Sprintf("%.10f", *pricingEntry.CacheReadInputTokenCost)) + ensurePricing().InputCacheRead = bifrost.Ptr(fmt.Sprintf("%.10f", *pricingEntry.CacheReadInputTokenCost)) } if pricingEntry.CacheCreationInputTokenCost != nil { - pricing.InputCacheWrite = bifrost.Ptr(fmt.Sprintf("%.10f", *pricingEntry.CacheCreationInputTokenCost)) + ensurePricing().InputCacheWrite = bifrost.Ptr(fmt.Sprintf("%.10f", *pricingEntry.CacheCreationInputTokenCost)) } - resp.Data[i].Pricing = pricing + if pricing != nil { + resp.Data[i].Pricing = pricing + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@transports/bifrost-http/handlers/inference.go` around lines 749 - 765, Do not pre-allocate pricing; only set resp.Data[i].Pricing when at least one cost exists: inspect pricingEntry fields (InputCostPerToken, OutputCostPerToken, InputCostPerImage, CacheReadInputTokenCost, CacheCreationInputTokenCost) and build a schemas.Pricing value only if any of those are non-nil, then assign it to resp.Data[i].Pricing; otherwise leave resp.Data[i].Pricing nil so the pricing field is omitted from the serialized output.ui/app/workspace/config/views/mcpView.tsx-45-53 (1)
45-53:⚠️ Potential issue | 🟡 MinorNormalize defaulted MCP fields before computing
hasChanges.On configs that omit
mcp_disable_auto_tool_inject, this comparesundefinedon the left tofalseon the right, so the page loads dirty and enables Save without any user edit. The same default-coalescing issue applies to the nearby defaulted MCP fields in this block.🛠️ Suggested fix
return ( localConfig.mcp_agent_depth !== config.mcp_agent_depth || localConfig.mcp_tool_execution_timeout !== config.mcp_tool_execution_timeout || - localConfig.mcp_code_mode_binding_level !== (config.mcp_code_mode_binding_level || "server") || - localConfig.mcp_tool_sync_interval !== (config.mcp_tool_sync_interval ?? 10) || - localConfig.mcp_disable_auto_tool_inject !== (config.mcp_disable_auto_tool_inject ?? false) + (localConfig.mcp_code_mode_binding_level || "server") !== (config.mcp_code_mode_binding_level || "server") || + (localConfig.mcp_tool_sync_interval ?? 10) !== (config.mcp_tool_sync_interval ?? 10) || + (localConfig.mcp_disable_auto_tool_inject ?? false) !== (config.mcp_disable_auto_tool_inject ?? false) );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ui/app/workspace/config/views/mcpView.tsx` around lines 45 - 53, The hasChanges computation compares localConfig fields to config fields without normalizing defaults, causing false positives (e.g., localConfig.mcp_disable_auto_tool_inject === false vs config.mcp_disable_auto_tool_inject === undefined). Update the useMemo for hasChanges to normalize/coalesce the config-side values to the same defaults you expect on localConfig (apply (config.mcp_code_mode_binding_level || "server"), (config.mcp_tool_sync_interval ?? 10), (config.mcp_disable_auto_tool_inject ?? false), etc.) before comparing; ensure you reference the existing hasChanges useMemo and the localConfig/config mcp_* fields so both sides use identical defaulting logic.transports/changelog.md-5-5 (1)
5-5:⚠️ Potential issue | 🟡 MinorMinor: Use hyphen for compound adjective.
"request level" should be hyphenated to "request-level" when used as a compound adjective modifying "extra headers".
📝 Suggested fix
-- feat: add support for request level extra headers in MCP tool execution. +- feat: add support for request-level extra headers in MCP tool execution.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@transports/changelog.md` at line 5, Update the changelog entry text to use the hyphenated compound adjective: replace "request level extra headers" with "request-level extra headers" so the line reads "feat: add support for request-level extra headers in MCP tool execution."framework/configstore/tables/pricingoverride.go-24-25 (1)
24-25:⚠️ Potential issue | 🟡 MinorAdd GORM auto-timestamp tags for
CreatedAtandUpdatedAt.Other GORM models in this codebase use
gorm:"autoCreateTime"andgorm:"autoUpdateTime"to let GORM automatically populate these fields. Without these tags, callers must manually set timestamps.🛠️ Proposed fix
- CreatedAt time.Time `gorm:"index;not null" json:"created_at"` - UpdatedAt time.Time `gorm:"index;not null" json:"updated_at"` + CreatedAt time.Time `gorm:"index;not null;autoCreateTime" json:"created_at"` + UpdatedAt time.Time `gorm:"index;not null;autoUpdateTime" json:"updated_at"`Based on learnings: "In this codebase using GORM, models with CreatedAt and UpdatedAt fields of type time.Time tagged with gorm:"autoCreateTime" and gorm:"autoUpdateTime" are populated automatically by GORM on insert/update."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@framework/configstore/tables/pricingoverride.go` around lines 24 - 25, The CreatedAt and UpdatedAt fields on the PricingOverride model are missing GORM auto-timestamp tags so GORM won't auto-populate them; update the struct tags for CreatedAt and UpdatedAt in pricingoverride.go to include gorm:"autoCreateTime" for CreatedAt and gorm:"autoUpdateTime" for UpdatedAt (preserve existing index/not null and json tags) so GORM will set timestamps automatically on insert/update.ui/components/ui/modelMultiselect.tsx-116-119 (1)
116-119:⚠️ Potential issue | 🟡 MinorInclude the wildcard option when the user searches for
"*".The backing value for “Allow All Models” is
"*", but this matcher only prepends it for empty input or text matching"allow all models". With the paired backend wildcard handling, typing*now returns every concrete model while hiding the actual allow-all option.♻️ Proposed fix
- const prefix: ModelOption[] = allowAllOption && (!query || "allow all models".includes(query.toLowerCase())) + const normalizedQuery = query.trim().toLowerCase(); + const prefix: ModelOption[] = allowAllOption && ( + !normalizedQuery || + normalizedQuery === "*" || + "allow all models".includes(normalizedQuery) + ) ? [ALL_MODELS_OPTION] : [];🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ui/components/ui/modelMultiselect.tsx` around lines 116 - 119, The prefix logic for prepending ALL_MODELS_OPTION doesn't consider the wildcard character, so when query === "*" the allow-all option is omitted; update the condition in the block that computes prefix (referencing allowAllOption, prefix, ALL_MODELS_OPTION, and query) to also treat a query of "*" (and trimmed "*" with surrounding whitespace) as a match — i.e., if allowAllOption is true and (query is empty OR query lowercased includes "allow all models" OR query trimmed equals "*"), then include ALL_MODELS_OPTION in prefix.core/mcp/toolmanager.go-197-203 (1)
197-203:⚠️ Potential issue | 🟡 MinorRecord tools only when they are actually returned.
BifrostContextKeyMCPAddedToolsis appended before theIsCodeModeClientgate, so code-mode client tool names are recorded even though they are not added toavailableTools. The actual code-mode tools appended later are never recorded, so downstream auditing/plugin logic sees the wrong tool set.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@core/mcp/toolmanager.go` around lines 197 - 203, The code currently calls schemas.AppendToContextList(ctx, schemas.BifrostContextKeyMCPAddedTools, tool.Function.Name) before checking client.ExecutionConfig.IsCodeModeClient, so code-mode tool names get recorded even though they aren’t added to availableTools and actual code-mode tools added later aren't recorded; fix by moving the AppendToContextList call to be executed only when the tool is actually appended to availableTools (i.e., place it inside the same branch that appends to availableTools), and ensure any separate code-mode branch that later appends tools also calls schemas.AppendToContextList for those tools; reference symbols: seenToolNames, schemas.AppendToContextList, BifrostContextKeyMCPAddedTools, client.ExecutionConfig.IsCodeModeClient, availableTools.docs/openapi/schemas/management/governance.yaml-1160-1207 (1)
1160-1207:⚠️ Potential issue | 🟡 MinorEncode the scope-specific ID requirements in the schema.
These requests describe
virtual_key_id,provider_id, andprovider_key_idas required for certainscope_kindvalues, but the OpenAPI shape leaves all three optional. UnlikeRoutingRuleabove, generated clients can't validate those combinations, so they can build requests the server will reject. AddoneOfvariants per scope so the contract is enforceable.Also applies to: 1208-1251
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/openapi/schemas/management/governance.yaml` around lines 1160 - 1207, The CreatePricingOverrideRequest schema currently lists virtual_key_id, provider_id, and provider_key_id as optional but they must be required conditionally by scope_kind; update CreatePricingOverrideRequest to include a oneOf array of schema variants for each scope_kind value: one variant for "global" (no extra IDs), "provider" (requires provider_id), "provider_key" (requires provider_key_id), "virtual_key" (requires virtual_key_id), "virtual_key_provider" (requires virtual_key_id and provider_id), and "virtual_key_provider_key" (requires virtual_key_id and provider_key_id); ensure each variant also includes the existing required base fields (name, scope_kind, match_type, pattern, request_types) and preserves the existing properties like patch and request_types so generated clients can validate the correct ID combinations against scope_kind.ui/lib/types/governance.ts-440-440 (1)
440-440:⚠️ Potential issue | 🟡 MinorType inconsistency between
request_typesdefinitions.
PricingOverride.request_types(line 440) andUpdatePricingOverrideRequest.request_types(line 467) are typed asstring[], butCreatePricingOverrideRequest.request_types(line 455) usesRequestType[]. For consistency and type safety, consider aligning all three to useRequestType[].💡 Suggested fix
export interface PricingOverride { id: string; name: string; scope_kind: PricingOverrideScopeKind; virtual_key_id?: string; provider_id?: string; provider_key_id?: string; match_type: PricingOverrideMatchType; pattern: string; - request_types?: string[]; + request_types?: RequestType[]; pricing_patch: string; config_hash?: string; created_at: string; updated_at: string; }export interface UpdatePricingOverrideRequest { name?: string; scope_kind?: PricingOverrideScopeKind; virtual_key_id?: string; provider_id?: string; provider_key_id?: string; match_type?: PricingOverrideMatchType; pattern?: string; - request_types?: string[]; + request_types?: RequestType[]; patch?: PricingOverridePatch; }Also applies to: 467-467
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ui/lib/types/governance.ts` at line 440, PricingOverride.request_types and UpdatePricingOverrideRequest.request_types are typed as string[] while CreatePricingOverrideRequest.request_types uses RequestType[]; change those two to RequestType[] to ensure consistency and type safety, update any import or exported type references to include RequestType where needed, and run TypeScript type checks to fix any resulting type errors referring to the properties on PricingOverride, CreatePricingOverrideRequest, and UpdatePricingOverrideRequest.ui/lib/types/schemas.ts-869-879 (1)
869-879:⚠️ Potential issue | 🟡 MinorTrim and reject blank
allowed_extra_headersentries.
[" "]and[" * "]currently pass the element schema, and the wildcard refine only checks for the exact"*". That leaves this allowlist depending on downstream normalization and can persist unusable values.Suggested validation tweak
allowed_extra_headers: z - .array(z.string()) + .array(z.string().trim().min(1, "Header name cannot be empty")) .optional() .refine( (headers) => { if (!headers || headers.length === 0) return true; const hasWildcard = headers.includes("*"); return !hasWildcard || headers.length === 1; }, { message: "Wildcard '*' cannot be combined with specific header names" }, ),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ui/lib/types/schemas.ts` around lines 869 - 879, The allowed_extra_headers Zod schema currently accepts strings with surrounding whitespace and blank-only values and only checks for an exact "*" in its refine; update the schema for allowed_extra_headers to trim each element and reject empties by applying a string transform/validation (e.g., transform to .trim() and then .refine(s => s.length > 0)), and change the array-level refine to detect wildcard after trimming (treat "*" only when the trimmed element equals "*" and reject arrays where a trimmed "*" coexists with other entries). Locate and modify the allowed_extra_headers schema definition and its element/array refinements to enforce trimming and blank-string rejection and to perform the wildcard check on trimmed values.framework/configstore/rdb.go-1320-1320 (1)
1320-1320:⚠️ Potential issue | 🟡 MinorUse a stable tie-breaker for pricing-override ordering.
These list queries sort only by
created_at. When multiple overrides share the same timestamp, rows can reshuffle between pages and produce nondeterministic list results. The rest of this file already usescreated_at ASC, id ASCfor stable pagination.Suggested fix
- if err := q.Order("created_at ASC").Find(&overrides).Error; err != nil { + if err := q.Order("created_at ASC, id ASC").Find(&overrides).Error; err != nil { return nil, s.parseGormError(err) }if err := baseQuery. - Order("created_at ASC"). + Order("created_at ASC, id ASC"). Offset(offset). Limit(limit). Find(&overrides).Error; err != nil {Also applies to: 1365-1369
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@framework/configstore/rdb.go` at line 1320, The list query orders overrides only by created_at, which can yield unstable pagination when timestamps tie; update the ordering in the queries that call q.Order("created_at ASC") (and the other similar q.Order usages around the same area, e.g., the call near the block at lines ~1365-1369) to include the id as a deterministic tie-breaker by changing the order clause to "created_at ASC, id ASC" so results remain stable across pages; ensure you update every occurrence in this file where overrides are ordered by created_at alone.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 833294d1-d927-4103-a422-a0c0a4c477e3
⛔ Files ignored due to path filters (2)
docs/media/ui-custom-pricing-form.pngis excluded by!**/*.pngdocs/media/ui-custom-pricing-table.pngis excluded by!**/*.png
📒 Files selected for processing (148)
.claude/skills/resolve-pr-comments/SKILL.mdcore/bifrost.gocore/changelog.mdcore/internal/mcptests/agent_test_helpers.gocore/internal/mcptests/codemode_stdio_test.gocore/internal/mcptests/concurrency_advanced_test.gocore/internal/mcptests/fixtures.gocore/mcp/agent.gocore/mcp/clientmanager.gocore/mcp/codemode.gocore/mcp/codemode/starlark/executecode.gocore/mcp/codemode/starlark/starlark.gocore/mcp/interface.gocore/mcp/mcp.gocore/mcp/toolmanager.gocore/mcp/utils.gocore/mcp/utils/utils.gocore/providers/anthropic/models.gocore/providers/azure/models.gocore/providers/bedrock/bedrock.gocore/providers/bedrock/images.gocore/providers/bedrock/models.gocore/providers/bedrock/types.gocore/providers/cohere/models.gocore/providers/elevenlabs/models.gocore/providers/gemini/models.gocore/providers/huggingface/models.gocore/providers/mistral/mistral.gocore/providers/mistral/models.gocore/providers/openai/models.gocore/providers/openrouter/openrouter.gocore/providers/replicate/models.gocore/providers/utils/utils.gocore/providers/vertex/models.gocore/providers/vertex/utils.gocore/providers/vertex/vertex.gocore/schemas/account.gocore/schemas/bifrost.gocore/schemas/images.gocore/schemas/mcp.gocore/schemas/provider.gocore/schemas/tracer.gocore/schemas/transcriptions.gocore/utils.godocs/architecture/framework/model-catalog.mdxdocs/docs.jsondocs/features/governance/mcp-tools.mdxdocs/features/governance/routing.mdxdocs/mcp/filtering.mdxdocs/openapi/openapi.jsondocs/openapi/openapi.yamldocs/openapi/paths/management/governance.yamldocs/openapi/schemas/management/governance.yamldocs/providers/custom-pricing.mdxdocs/providers/provider-routing.mdxdocs/providers/request-options.mdxdocs/providers/supported-providers/bedrock.mdxexamples/configs/partial/config.jsonexamples/configs/withconfigstore/config.jsonexamples/configs/withlogstore/config.jsonexamples/configs/withpostgresmcpclientsinconfig/config.jsonexamples/configs/withpricingoverridesnostore/config.jsonexamples/configs/withpricingoverridessqlite/config.jsonexamples/configs/withprompushgateway/config.jsonexamples/configs/withvirtualkeys/config.jsonexamples/mcps/auth-demo-server/main.goframework/changelog.mdframework/configstore/clientconfig.goframework/configstore/migrations.goframework/configstore/rdb.goframework/configstore/store.goframework/configstore/tables/clientconfig.goframework/configstore/tables/key.goframework/configstore/tables/mcp.goframework/configstore/tables/modelpricing.goframework/configstore/tables/pricingoverride.goframework/configstore/tables/provider.goframework/configstore/tables/virtualkey.goframework/logstore/tables.goframework/modelcatalog/main.goframework/modelcatalog/main_test.goframework/modelcatalog/overrides.goframework/modelcatalog/overrides_test.goframework/modelcatalog/pricing.goframework/modelcatalog/pricing_test.goframework/modelcatalog/utils.goframework/streaming/audio.goframework/streaming/chat.goframework/streaming/images.goframework/streaming/responses.goframework/streaming/transcription.goframework/tracing/tracer.gohelm-charts/bifrost/values.schema.jsonplugins/governance/changelog.mdplugins/governance/main.goplugins/governance/resolver.goplugins/governance/resolver_test.goplugins/governance/utils.goplugins/logging/main.goplugins/logging/operations.goplugins/telemetry/main.gotransports/bifrost-http/handlers/asyncinference.gotransports/bifrost-http/handlers/config.gotransports/bifrost-http/handlers/governance.gotransports/bifrost-http/handlers/inference.gotransports/bifrost-http/handlers/mcp.gotransports/bifrost-http/handlers/mcpinference.gotransports/bifrost-http/handlers/mcpserver.gotransports/bifrost-http/handlers/pricing_override_test.gotransports/bifrost-http/handlers/providers.gotransports/bifrost-http/handlers/utils.gotransports/bifrost-http/integrations/bedrock_test.gotransports/bifrost-http/integrations/openai.gotransports/bifrost-http/integrations/router.gotransports/bifrost-http/lib/config.gotransports/bifrost-http/lib/config_test.gotransports/bifrost-http/lib/ctx.gotransports/bifrost-http/lib/ctx_test.gotransports/bifrost-http/server/plugins.gotransports/bifrost-http/server/server.gotransports/changelog.mdtransports/config.schema.jsontransports/schema_test/config_schema_test.goui/app/workspace/config/views/mcpView.tsxui/app/workspace/custom-pricing/overrides/page.tsxui/app/workspace/custom-pricing/overrides/pricingFieldSelector.tsxui/app/workspace/custom-pricing/overrides/pricingOverrideSheet.tsxui/app/workspace/custom-pricing/overrides/pricingOverridesEmptyState.tsxui/app/workspace/custom-pricing/overrides/scopedPricingOverridesView.tsxui/app/workspace/logs/sheets/logDetailsSheet.tsxui/app/workspace/mcp-registry/views/mcpClientSheet.tsxui/app/workspace/providers/fragments/apiKeysFormFragment.tsxui/app/workspace/providers/fragments/index.tsui/app/workspace/providers/fragments/pricingOverridesFormFragment.tsxui/app/workspace/providers/views/providerKeyForm.tsxui/app/workspace/virtual-keys/views/virtualKeyDetailsSheet.tsxui/app/workspace/virtual-keys/views/virtualKeySheet.tsxui/app/workspace/virtual-keys/views/virtualKeysTable.tsxui/components/sidebar.tsxui/components/ui/asyncMultiselect.tsxui/components/ui/modelMultiselect.tsxui/components/ui/numberAndSelect.tsxui/lib/store/apis/baseApi.tsui/lib/store/apis/governanceApi.tsui/lib/types/config.tsui/lib/types/governance.tsui/lib/types/mcp.tsui/lib/types/schemas.ts
💤 Files with no reviewable changes (2)
- ui/app/workspace/providers/fragments/pricingOverridesFormFragment.tsx
- ui/app/workspace/providers/fragments/index.ts
| **Update (sparse patch):** | ||
| ```bash | ||
| curl -X PATCH http://localhost:8080/api/governance/pricing-overrides/{id} \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{ | ||
| "patch": { | ||
| "input_cost_per_token": 0.000002 | ||
| } | ||
| }' | ||
| ``` |
There was a problem hiding this comment.
Don't describe this as a sparse patch.
Line 180 suggests merge semantics, but TestUpdatePricingOverride_ReplacesFullBody in this PR verifies that patch is replaced wholesale: omitted fields are cleared. Please call that out here and show the complete desired patch object, otherwise users will accidentally wipe existing override fields.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/providers/custom-pricing.mdx` around lines 180 - 189, Remove the "sparse
patch" wording and explicitly document that the API's "patch" object is applied
wholesale (omitted fields are cleared) by aligning the docs with the test
TestUpdatePricingOverride_ReplacesFullBody; update the narrative around the curl
example in docs/providers/custom-pricing.mdx to state replacement semantics and
replace the partial JSON shown with a full desired "patch" object example so
users supply all fields they want to keep rather than accidentally wiping
omitted fields.
| log.Printf(" Connection-level: X-API-Key: %s (middleware rejects all requests without it)", connectionAPIKey) | ||
| log.Printf(" Tool-execution: X-Tool-Token: %s (only secret_data checks this, validated inside the handler)", toolExecToken) | ||
| log.Printf("\nNote: Bifrost sends all `headers` on both connection setup AND every tool call.") | ||
| log.Printf("Both X-API-Key and X-Role go in the same `headers` map.\n") | ||
| log.Printf("Both X-API-Key and X-Tool-Token go in the same `headers` map.") | ||
| log.Printf("The server enforces each at the right layer: middleware vs. handler.\n") |
There was a problem hiding this comment.
Do not log authentication secrets in plaintext.
The startup logs and printed config currently expose live X-API-Key and X-Tool-Token values, which can leak credentials via log aggregation and support dumps. Please redact or replace with placeholders.
🔐 Proposed fix
- log.Printf(" Connection-level: X-API-Key: %s (middleware rejects all requests without it)", connectionAPIKey)
- log.Printf(" Tool-execution: X-Tool-Token: %s (only secret_data checks this, validated inside the handler)", toolExecToken)
+ log.Printf(" Connection-level: X-API-Key configured (middleware rejects requests without it)")
+ log.Printf(" Tool-execution: X-Tool-Token configured (validated inside secret_data handler)")
@@
- "X-API-Key": "%s",
- "X-Tool-Token": "%s"
+ "X-API-Key": "<set-via-env-or-secret-manager>",
+ "X-Tool-Token": "<set-via-env-or-secret-manager>"
@@
-`, addr, connectionAPIKey, toolExecToken)
+`, addr)Also applies to: 123-124, 128-128
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@examples/mcps/auth-demo-server/main.go` around lines 110 - 114, The startup
logs print sensitive values connectionAPIKey and toolExecToken directly via
log.Printf; change those log statements (the ones referencing "Connection-level:
X-API-Key" and "Tool-execution: X-Tool-Token" and the other related log.Printf
calls) to avoid plaintext secrets by replacing the raw variables with a redacted
placeholder or masked form (e.g., "[REDACTED]" or show only last N chars) before
logging; implement or call a simple maskSecret(value string) helper and use it
in those log.Printf calls so no full secret is emitted.
| // GeneratePricingOverrideHash generates a SHA256 hash for a pricing override. | ||
| // Skips: CreatedAt, UpdatedAt, ConfigHash (dynamic/meta fields). | ||
| func GeneratePricingOverrideHash(p tables.TablePricingOverride) (string, error) { | ||
| hash := sha256.New() | ||
| hash.Write([]byte(p.ID)) | ||
| hash.Write([]byte(p.Name)) | ||
| hash.Write([]byte(p.ScopeKind)) | ||
| hash.Write([]byte(derefStr(p.VirtualKeyID))) | ||
| hash.Write([]byte(derefStr(p.ProviderID))) | ||
| hash.Write([]byte(derefStr(p.ProviderKeyID))) | ||
| hash.Write([]byte(p.MatchType)) | ||
| hash.Write([]byte(p.Pattern)) | ||
| hash.Write([]byte(p.RequestTypesJSON)) | ||
| hash.Write([]byte(p.PricingPatchJSON)) | ||
| return hex.EncodeToString(hash.Sum(nil)), nil | ||
| } |
There was a problem hiding this comment.
Hash RequestTypes before the row is persisted.
TablePricingOverride.RequestTypesJSON is json:"-", so config-origin overrides reach this helper with RequestTypes populated and RequestTypesJSON still empty. In that path, request_types changes don't affect the hash and reconciliation can miss the update.
Suggested fix
func GeneratePricingOverrideHash(p tables.TablePricingOverride) (string, error) {
hash := sha256.New()
hash.Write([]byte(p.ID))
hash.Write([]byte(p.Name))
hash.Write([]byte(p.ScopeKind))
hash.Write([]byte(derefStr(p.VirtualKeyID)))
hash.Write([]byte(derefStr(p.ProviderID)))
hash.Write([]byte(derefStr(p.ProviderKeyID)))
hash.Write([]byte(p.MatchType))
hash.Write([]byte(p.Pattern))
- hash.Write([]byte(p.RequestTypesJSON))
+ if p.RequestTypesJSON != "" {
+ hash.Write([]byte(p.RequestTypesJSON))
+ } else if len(p.RequestTypes) > 0 {
+ requestTypes := append([]schemas.RequestType(nil), p.RequestTypes...)
+ sort.Slice(requestTypes, func(i, j int) bool { return requestTypes[i] < requestTypes[j] })
+ data, err := sonic.Marshal(requestTypes)
+ if err != nil {
+ return "", err
+ }
+ hash.Write(data)
+ }
hash.Write([]byte(p.PricingPatchJSON))
return hex.EncodeToString(hash.Sum(nil)), nil
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // GeneratePricingOverrideHash generates a SHA256 hash for a pricing override. | |
| // Skips: CreatedAt, UpdatedAt, ConfigHash (dynamic/meta fields). | |
| func GeneratePricingOverrideHash(p tables.TablePricingOverride) (string, error) { | |
| hash := sha256.New() | |
| hash.Write([]byte(p.ID)) | |
| hash.Write([]byte(p.Name)) | |
| hash.Write([]byte(p.ScopeKind)) | |
| hash.Write([]byte(derefStr(p.VirtualKeyID))) | |
| hash.Write([]byte(derefStr(p.ProviderID))) | |
| hash.Write([]byte(derefStr(p.ProviderKeyID))) | |
| hash.Write([]byte(p.MatchType)) | |
| hash.Write([]byte(p.Pattern)) | |
| hash.Write([]byte(p.RequestTypesJSON)) | |
| hash.Write([]byte(p.PricingPatchJSON)) | |
| return hex.EncodeToString(hash.Sum(nil)), nil | |
| } | |
| // GeneratePricingOverrideHash generates a SHA256 hash for a pricing override. | |
| // Skips: CreatedAt, UpdatedAt, ConfigHash (dynamic/meta fields). | |
| func GeneratePricingOverrideHash(p tables.TablePricingOverride) (string, error) { | |
| hash := sha256.New() | |
| hash.Write([]byte(p.ID)) | |
| hash.Write([]byte(p.Name)) | |
| hash.Write([]byte(p.ScopeKind)) | |
| hash.Write([]byte(derefStr(p.VirtualKeyID))) | |
| hash.Write([]byte(derefStr(p.ProviderID))) | |
| hash.Write([]byte(derefStr(p.ProviderKeyID))) | |
| hash.Write([]byte(p.MatchType)) | |
| hash.Write([]byte(p.Pattern)) | |
| if p.RequestTypesJSON != "" { | |
| hash.Write([]byte(p.RequestTypesJSON)) | |
| } else if len(p.RequestTypes) > 0 { | |
| requestTypes := append([]schemas.RequestType(nil), p.RequestTypes...) | |
| sort.Slice(requestTypes, func(i, j int) bool { return requestTypes[i] < requestTypes[j] }) | |
| data, err := sonic.Marshal(requestTypes) | |
| if err != nil { | |
| return "", err | |
| } | |
| hash.Write(data) | |
| } | |
| hash.Write([]byte(p.PricingPatchJSON)) | |
| return hex.EncodeToString(hash.Sum(nil)), nil | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@framework/configstore/clientconfig.go` around lines 970 - 985,
GeneratePricingOverrideHash currently uses RequestTypesJSON which may be empty
for config-origin overrides; serialize and include the in-memory RequestTypes
field when RequestTypesJSON is empty so changes to RequestTypes affect the hash.
In GeneratePricingOverrideHash (and any callers), detect if p.RequestTypesJSON
is empty, marshal p.RequestTypes (using json.Marshal or a canonical JSON
serializer) and Write that byte slice into the hash (falling back to
RequestTypesJSON if non-empty), and propagate any marshal error out of
GeneratePricingOverrideHash instead of ignoring it.
| func migrationReconcilePricingOverridesTable(ctx context.Context, db *gorm.DB) error { | ||
| m := migrator.New(db, migrator.DefaultOptions, []*migrator.Migration{{ | ||
| ID: "add_provider_pricing_overrides_column", | ||
| ID: "reconcile_pricing_overrides_table", | ||
| Migrate: func(tx *gorm.DB) error { | ||
| tx = tx.WithContext(ctx) | ||
| migrator := tx.Migrator() | ||
| if !migrator.HasColumn(&tables.TableProvider{}, "pricing_overrides_json") { | ||
| if err := migrator.AddColumn(&tables.TableProvider{}, "PricingOverridesJSON"); err != nil { | ||
| return fmt.Errorf("failed to add pricing_overrides_json column: %w", err) | ||
| mgr := tx.Migrator() | ||
|
|
||
| if !mgr.HasTable(&tables.TablePricingOverride{}) { | ||
| if err := mgr.CreateTable(&tables.TablePricingOverride{}); err != nil { | ||
| return fmt.Errorf("failed to create governance_pricing_overrides table: %w", err) | ||
| } | ||
| return nil | ||
| } | ||
| if err := tx.AutoMigrate(&tables.TablePricingOverride{}); err != nil { | ||
| return fmt.Errorf("failed to automigrate governance_pricing_overrides table: %w", err) | ||
| } |
There was a problem hiding this comment.
Backfill legacy provider pricing overrides before switching to the new table.
This migration only creates/reconciles governance_pricing_overrides. The same PR stack removes provider-level pricing_overrides_json storage elsewhere, so any existing overrides stay stranded in config_providers and disappear from effective pricing after upgrade. Add a one-time copy into tables.TablePricingOverride before the old storage stops being read.
As per coding guidelines, always check the stack if there is one for the current PR. do not give localized reviews for the PR, always see all changes in the light of the whole stack of PRs.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@framework/configstore/migrations.go` around lines 4057 - 4072, The migration
migrationReconcilePricingOverridesTable currently only creates/automigrates
tables.TablePricingOverride but does not backfill existing provider-level
overrides stored in config_providers.pricing_overrides_json; add a one-time copy
step inside the Migrate function (after creating the new table or before
returning) that queries config_providers for non-null pricing_overrides_json,
unmarshals each JSON, and inserts corresponding rows into
tables.TablePricingOverride (avoiding dupes by checking provider_id or using
Upsert semantics on the new table) so legacy overrides are migrated before the
old field is removed.
| if err := h.governanceManager.DeletePricingOverride(ctx, id); err != nil { | ||
| logger.Warn("failed to delete pricing override from memory: %v", err) | ||
| } | ||
| SendJSON(ctx, map[string]interface{}{ | ||
| "message": "Pricing override deleted successfully", | ||
| }) |
There was a problem hiding this comment.
Don't return 200 after the in-memory delete fails.
At this point the DB row is already gone, so swallowing DeletePricingOverride errors leaves the active pricing catalog stale until the next reload/restart. Mirror the updateVirtualKey failure path here and fail the request instead of acknowledging success.
🔧 Proposed fix
if err := h.governanceManager.DeletePricingOverride(ctx, id); err != nil {
- logger.Warn("failed to delete pricing override from memory: %v", err)
+ logger.Error("failed to delete pricing override from memory: %v", err)
+ SendError(ctx, fasthttp.StatusInternalServerError, "Pricing override deleted in database but failed to update in-memory pricing state")
+ return
}
SendJSON(ctx, map[string]interface{}{
"message": "Pricing override deleted successfully",
})Based on learnings: if the database update succeeds but the in-memory GovernanceManager reload fails, respond with HTTP 500 rather than signaling success because the system relies on in-memory state for internal operations.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if err := h.governanceManager.DeletePricingOverride(ctx, id); err != nil { | |
| logger.Warn("failed to delete pricing override from memory: %v", err) | |
| } | |
| SendJSON(ctx, map[string]interface{}{ | |
| "message": "Pricing override deleted successfully", | |
| }) | |
| if err := h.governanceManager.DeletePricingOverride(ctx, id); err != nil { | |
| logger.Error("failed to delete pricing override from memory: %v", err) | |
| SendError(ctx, fasthttp.StatusInternalServerError, "Pricing override deleted in database but failed to update in-memory pricing state") | |
| return | |
| } | |
| SendJSON(ctx, map[string]interface{}{ | |
| "message": "Pricing override deleted successfully", | |
| }) |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@transports/bifrost-http/handlers/governance.go` around lines 3599 - 3604, The
current handler calls h.governanceManager.DeletePricingOverride and logs a
warning on error but still returns success via SendJSON; instead, mirror the
updateVirtualKey failure path: if h.governanceManager.DeletePricingOverride(ctx,
id) returns an error, log the error and return a 500 error response (using the
same error handling/response helper used by updateVirtualKey) and do not call
SendJSON to report success; only send the "Pricing override deleted
successfully" JSON when DeletePricingOverride succeeds.
| // Inject tool filter into execution context if present | ||
| if toolFilter != nil { | ||
| ctx = context.WithValue(ctx, schemas.BifrostContextKey("mcp-include-tools"), toolFilter) | ||
| ctx = context.WithValue(ctx, schemas.MCPContextKeyIncludeTools, toolFilter) | ||
| } |
There was a problem hiding this comment.
Don't overwrite the request-level tool filter here.
This shadows any MCPContextKeyIncludeTools already present on the request with the broader virtual-key toolFilter. A caller can then skip tools/list and invoke a tool that the per-request filter meant to exclude. Intersect the existing filter with toolFilter instead of replacing it.
As per coding guidelines: core/mcp/**/*.go: Tool access follows 4-level filtering hierarchy: Global → Client-level → Tool-level → Per-request. All four levels must agree for tool availability.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@transports/bifrost-http/handlers/mcpserver.go` around lines 235 - 238, The
code currently overwrites any existing per-request tool filter when setting ctx
with context.WithValue(ctx, schemas.MCPContextKeyIncludeTools, toolFilter);
instead, read the existing value from ctx (using
schemas.MCPContextKeyIncludeTools), compute the intersection between that
existing filter and the broader toolFilter, and store the intersected result
back into the context; ensure the logic handles nil/empty cases (if existing is
nil use toolFilter, if toolFilter is nil keep existing) and keep the symbol
names ctx, toolFilter, schemas.MCPContextKeyIncludeTools, and context.WithValue
to locate and update the code path.
| allowed_extra_headers?: string[]; // Allowlist of x-bf-eh-* headers forwarded to this MCP server. ["*"] = allow all. | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if backend CreateMCPClient handler accepts allowed_extra_headers
rg -n "allowed_extra_headers" --type go -C 3 transports/bifrost-http/handlers/mcp.goRepository: maximhq/bifrost
Length of output: 817
🏁 Script executed:
cat -n ui/lib/types/mcp.ts | head -100Repository: maximhq/bifrost
Length of output: 3846
🏁 Script executed:
#!/bin/bash
# Map the handlers to identify Create vs Update
rg -n "func.*CreateMCPClient\|func.*UpdateMCPClient" --type go transports/bifrost-http/handlers/mcp.goRepository: maximhq/bifrost
Length of output: 41
🏁 Script executed:
sed -n '310,325p' transports/bifrost-http/handlers/mcp.go
sed -n '510,525p' transports/bifrost-http/handlers/mcp.goRepository: maximhq/bifrost
Length of output: 1276
🏁 Script executed:
rg -n "func.*\(Create\|Update\).*MCP" --type go transports/bifrost-http/handlers/mcp.go | head -20Repository: maximhq/bifrost
Length of output: 41
🏁 Script executed:
rg -n "HandleCreateMCPClient|HandleUpdateMCPClient|POST.*mcp|PUT.*mcp" --type go transports/bifrost-http/handlers/mcp.go | head -20Repository: maximhq/bifrost
Length of output: 830
🏁 Script executed:
rg -B 5 "if err := validateAllowedExtraHeaders" --type go transports/bifrost-http/handlers/mcp.go | head -50Repository: maximhq/bifrost
Length of output: 569
🏁 Script executed:
rg -n "^func.*addMCPClient|^func.*updateMCPClient" --type go transports/bifrost-http/handlers/mcp.goRepository: maximhq/bifrost
Length of output: 193
🏁 Script executed:
sed -n '282,330p' transports/bifrost-http/handlers/mcp.goRepository: maximhq/bifrost
Length of output: 2064
Add allowed_extra_headers field to CreateMCPClientRequest to match backend behavior.
The allowed_extra_headers field is present in UpdateMCPClientRequest (line 94) but missing from CreateMCPClientRequest (lines 52-64). The Go backend handler for creating MCP clients (addMCPClient) validates this field on the request, indicating it should be supported during client creation. For consistency with the backend API and the MCPClientConfig interface, this field should be added to CreateMCPClientRequest.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@ui/lib/types/mcp.ts` around lines 94 - 95, CreateMCPClientRequest is missing
the allowed_extra_headers field present on UpdateMCPClientRequest and expected
by the backend handler addMCPClient and MCPClientConfig; add an optional
allowed_extra_headers?: string[] property to the CreateMCPClientRequest type so
create requests accept the same allowlist semantics (["*"] to allow all) and
match the backend validation behavior.
## Summary This PR fixes token usage tracking in MCP agent mode by accumulating usage across all LLM calls in the agent loop and returning the total usage in the final response. ## Changes - Added usage accumulation logic in the MCP agent execution loop to track token consumption across multiple LLM calls - Implemented `mergeUsage` function to combine token counts and costs from multiple `BifrostLLMUsage` values, handling all detail sub-fields including prompt tokens, completion tokens, and cost breakdowns - Extended agent API adapters with `extractUsage` and `applyUsage` methods to handle usage extraction and application for both Chat API and Responses API - Applied accumulated usage to the final response before returning it to the client ## Type of change - [x] Bug fix - [ ] Feature - [ ] Refactor - [ ] Documentation - [ ] Chore/CI ## Affected areas - [x] Core (Go) - [x] Transports (HTTP) - [ ] Providers/Integrations - [ ] Plugins - [ ] UI (Next.js) - [ ] Docs ## How to test Test MCP agent mode with multiple tool calls to verify usage accumulation: ```sh # Core/Transports go version go test ./... # Test MCP agent mode with multiple LLM calls # Verify that the returned usage reflects the sum of all calls in the agent loop # Check that both token counts and cost details are properly accumulated ``` ## Screenshots/Recordings N/A ## Breaking changes - [ ] Yes - [x] No ## Related issues N/A ## Security considerations No security implications - this change only affects usage tracking and reporting. ## 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
… flow in starlark, refining the prompt of executecode tool (#2206) ## Changes - **Enhanced Starlark dialect configuration**: Enabled top-level control flow statements (if/for/while), while loops, set() builtin, global variable reassignment, and recursive functions for a more Python-like experience - **Improved string escape handling**: Removed automatic `\n` to newline conversion, allowing Starlark's native string escape processing to handle `\n`, `\t`, and other escape sequences correctly - **Updated tool description**: Streamlined the executeToolCode tool description with clearer syntax notes, explicit documentation of Starlark differences from Python (no try/except, no classes, no imports, no f-strings), and emphasis on fresh isolated scope per execution - **Enhanced error hints**: Added specific error messages for unsupported Python features like try/except/finally/raise, with guidance on alternative approaches and scope persistence warnings - **Comprehensive test coverage**: Added tests for dialect options, string escape preservation, unsupported feature detection, and end-to-end JSON deserialization scenarios ## Type of change - [ ] Feature - [ ] Bug fix - [x] Refactor - [ ] Documentation - [ ] Chore/CI ## Affected areas - [x] Core (Go) - Starlark CodeMode improvements - [ ] Transports (HTTP) - [ ] Providers/Integrations - [ ] Plugins - [ ] UI (Next.js) - [ ] Docs ## How to test Test the enhanced Starlark features with MCP CodeMode: ```sh # Test dialect options (top-level control flow, while loops, etc.) make test-mcp TESTCASE=TestStarlarkDialectOptions # Test string escape handling make test-mcp PATTERN=TestStarlarkStringEscape # Test unsupported feature detection make test-mcp PATTERN=TestStarlarkUnsupportedFeatures ``` ## Breaking changes - [ ] Yes - [x] No The Starlark changes are additive and maintain backward compatibility while enabling more Python-like syntax. ## Security considerations Starlark CodeMode maintains its existing sandboxing with no additional network or filesystem access. The dialect enhancements only affect language features within the existing security boundary.
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
core/mcp/codemode/starlark/getdocs.go (1)
143-174:⚠️ Potential issue | 🟡 MinorUpdate the generated Starlark guidance for top-level control flow.
This header still says top-level
for/if/whilemust be wrapped in a function, butexecuteCodenow enablessyntax.FileOptions{TopLevelControl: true, While: true}and the new dialect tests rely on that. Please rewrite this block here and the matching"not within a function"hint path so the docs match the runtime.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@core/mcp/codemode/starlark/getdocs.go` around lines 143 - 174, The generated header incorrectly instructs that top-level for/if/while must be wrapped in a function; update the text in getdocs.go where the string builder (sb) writes the STARLARK DIFFERENCE FROM PYTHON block and the matching "not within a function" hint to reflect that executeCode now supports top-level control flow via syntax.FileOptions{TopLevelControl: true, While: true}; replace the guidance to state top-level control flow is supported (with any dialect caveats) and adjust examples/usage to show top-level loops/ifs/while are allowed directly, ensuring the change is applied in the same area that uses isToolLevel, clientName and tools to build the header so documentation matches runtime behavior.
🧹 Nitpick comments (3)
transports/changelog.md (1)
1-1: Consider splitting long entry for better readability.This entry covers three distinct aspects (wildcard support, empty array semantics, and handler optimization). Splitting into separate bullets would improve scanability.
♻️ Optional refactor for readability
-- feat: VK provider config key_ids now supports ["*"] wildcard to allow all keys; empty key_ids denies all; handler resolves wildcard to AllowAllKeys flag without DB key lookups +- feat: VK provider config key_ids now supports ["*"] wildcard to allow all keys +- feat: empty key_ids in VK provider config now denies all keys by default +- refactor: handler resolves ["*"] wildcard to AllowAllKeys flag to skip DB key lookups🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@transports/changelog.md` at line 1, The changelog entry is too dense—split the single line into three separate bullets to improve readability: one bullet stating that the VK provider config key_ids now accepts the ["*"] wildcard, a second bullet explaining that an empty key_ids array now denies all keys, and a third bullet describing that the handler resolves the wildcard to an AllowAllKeys flag to avoid DB key lookups; reference "key_ids", '["*"]', "AllowAllKeys", and "handler" in each appropriate bullet so the intent and impacted components are clear.core/mcp/agent.go (1)
366-478: Consider usingbifrost.Ptr()for pointer creation.The
&sumpattern works correctly, but per repository conventions,bifrost.Ptr()is preferred for creating pointers.♻️ Optional refactor using bifrost.Ptr()
- sum := bct + act - merged.CompletionTokensDetails.CitationTokens = &sum + merged.CompletionTokensDetails.CitationTokens = bifrost.Ptr(bct + act)Apply similarly to
NumSearchQueriesandImageTokenspointer assignments.Based on learnings: "In the maximhq/bifrost repository, prefer using bifrost.Ptr() to create pointers instead of the address operator (&) even when & would be valid syntactically."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@core/mcp/agent.go` around lines 366 - 478, In mergeUsage, replace the local idiom of creating pointer values via &sum for CompletionTokensDetails.CitationTokens, CompletionTokensDetails.NumSearchQueries and CompletionTokensDetails.ImageTokens with the repository-conventional bifrost.Ptr() helper: compute the integer sum as you already do, then assign merged.CompletionTokensDetails.CitationTokens = bifrost.Ptr(sum) (and similarly for NumSearchQueries and ImageTokens) so pointers are created via bifrost.Ptr() rather than the address-of operator; locate these assignments inside the mergeUsage function where bd/ad nullable fields are aggregated.core/mcp/codemode/starlark/starlark_test.go (1)
599-616: Share the dialect options with production code.
starlarkOpts()duplicates thesyntax.FileOptionsliteral fromexecuteCode, so a future option change can leave the tests exercising a different dialect than production. Extract the builder into package code and reuse it here.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@core/mcp/codemode/starlark/starlark_test.go` around lines 599 - 616, starlarkOpts() in the test duplicates the syntax.FileOptions literal used by executeCode; extract a shared builder (e.g., NewStarlarkFileOptions or BuildFileOptions) into the package that returns *syntax.FileOptions configured for production, replace the literal in executeCode to call that builder, and update the test to call that new exported function (remove starlarkOpts and have execStarlark use the shared builder) so tests and production share the same dialect configuration.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@core/mcp/agent.go`:
- Around line 332-338: The early-return when depth == 1 &&
len(allExecutedToolResults) == 0 returns currentResponse without applying
accumulatedUsage; call adapter.applyUsage(currentResponse, accumulatedUsage)
before that return so usage is always applied (mirroring the later path that
applies usage then calls adapter.createResponseWithExecutedTools), keeping the
rest of the logic and returned value (currentResponse) otherwise unchanged.
In `@core/mcp/codemode/starlark/utils.go`:
- Around line 366-398: The helpers getCanonicalToolName,
getCompatibilityToolAlias and matchesToolReference currently rely on
stripClientPrefix which only strips the "<client>-" prefix, but discovered MCP
tool names are sanitized to use "_" (e.g., "github_SEARCH_REPOS" →
"github_search_repos"); update stripClientPrefix (or callers) to also remove the
sanitized "<client>_" prefix (case-insensitively) before further
canonicalization so getCanonicalToolName and getCompatibilityToolAlias yield
"search_repos" and "search_repos" variants; update matchesToolReference to
compare against those normalized forms and add a regression test adjacent to the
canonicalization tests covering the "client_tool" sanitized prefix case (and
ensure callMCPTool usage of stripClientPrefix benefits from the same change).
In `@transports/changelog.md`:
- Line 5: Update the changelog entry text by replacing the unhyphenated phrase
"request level extra headers" with the hyphenated compound adjective
"request-level extra headers" so the line reads "feat: add support for
request-level extra headers in MCP tool execution."; ensure only that wording is
changed (preserve the rest of the entry).
---
Outside diff comments:
In `@core/mcp/codemode/starlark/getdocs.go`:
- Around line 143-174: The generated header incorrectly instructs that top-level
for/if/while must be wrapped in a function; update the text in getdocs.go where
the string builder (sb) writes the STARLARK DIFFERENCE FROM PYTHON block and the
matching "not within a function" hint to reflect that executeCode now supports
top-level control flow via syntax.FileOptions{TopLevelControl: true, While:
true}; replace the guidance to state top-level control flow is supported (with
any dialect caveats) and adjust examples/usage to show top-level loops/ifs/while
are allowed directly, ensuring the change is applied in the same area that uses
isToolLevel, clientName and tools to build the header so documentation matches
runtime behavior.
---
Nitpick comments:
In `@core/mcp/agent.go`:
- Around line 366-478: In mergeUsage, replace the local idiom of creating
pointer values via &sum for CompletionTokensDetails.CitationTokens,
CompletionTokensDetails.NumSearchQueries and CompletionTokensDetails.ImageTokens
with the repository-conventional bifrost.Ptr() helper: compute the integer sum
as you already do, then assign merged.CompletionTokensDetails.CitationTokens =
bifrost.Ptr(sum) (and similarly for NumSearchQueries and ImageTokens) so
pointers are created via bifrost.Ptr() rather than the address-of operator;
locate these assignments inside the mergeUsage function where bd/ad nullable
fields are aggregated.
In `@core/mcp/codemode/starlark/starlark_test.go`:
- Around line 599-616: starlarkOpts() in the test duplicates the
syntax.FileOptions literal used by executeCode; extract a shared builder (e.g.,
NewStarlarkFileOptions or BuildFileOptions) into the package that returns
*syntax.FileOptions configured for production, replace the literal in
executeCode to call that builder, and update the test to call that new exported
function (remove starlarkOpts and have execStarlark use the shared builder) so
tests and production share the same dialect configuration.
In `@transports/changelog.md`:
- Line 1: The changelog entry is too dense—split the single line into three
separate bullets to improve readability: one bullet stating that the VK provider
config key_ids now accepts the ["*"] wildcard, a second bullet explaining that
an empty key_ids array now denies all keys, and a third bullet describing that
the handler resolves the wildcard to an AllowAllKeys flag to avoid DB key
lookups; reference "key_ids", '["*"]', "AllowAllKeys", and "handler" in each
appropriate bullet so the intent and impacted components are clear.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: a19ea721-6c58-4997-94b4-bad1c0daec22
📒 Files selected for processing (10)
core/changelog.mdcore/mcp/agent.gocore/mcp/agentadaptors.gocore/mcp/codemode/starlark/executecode.gocore/mcp/codemode/starlark/getdocs.gocore/mcp/codemode/starlark/listfiles.gocore/mcp/codemode/starlark/readfile.gocore/mcp/codemode/starlark/starlark_test.gocore/mcp/codemode/starlark/utils.gotransports/changelog.md
✅ Files skipped from review due to trivial changes (1)
- core/changelog.md
| if depth == 1 && len(allExecutedToolResults) == 0 { | ||
| return currentResponse, nil | ||
| } | ||
| // Apply accumulated usage before building the final response | ||
| adapter.applyUsage(currentResponse, accumulatedUsage) | ||
| // Create response with all executed tool results from all iterations, and non-auto-executable tool calls | ||
| return adapter.createResponseWithExecutedTools(currentResponse, allExecutedToolResults, allExecutedToolCalls, nonAutoExecutableTools), nil |
There was a problem hiding this comment.
Missing usage accumulation on early return path (line 333).
When depth == 1 && len(allExecutedToolResults) == 0, the function returns currentResponse without applying accumulatedUsage. This path returns the initial response unchanged, but if usage should be consistent, consider whether this is intentional.
💡 Potential fix if usage should be applied on all paths
// Return as is if its the first iteration
if depth == 1 && len(allExecutedToolResults) == 0 {
+ adapter.applyUsage(currentResponse, accumulatedUsage)
return currentResponse, nil
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if depth == 1 && len(allExecutedToolResults) == 0 { | |
| return currentResponse, nil | |
| } | |
| // Apply accumulated usage before building the final response | |
| adapter.applyUsage(currentResponse, accumulatedUsage) | |
| // Create response with all executed tool results from all iterations, and non-auto-executable tool calls | |
| return adapter.createResponseWithExecutedTools(currentResponse, allExecutedToolResults, allExecutedToolCalls, nonAutoExecutableTools), nil | |
| if depth == 1 && len(allExecutedToolResults) == 0 { | |
| adapter.applyUsage(currentResponse, accumulatedUsage) | |
| return currentResponse, nil | |
| } | |
| // Apply accumulated usage before building the final response | |
| adapter.applyUsage(currentResponse, accumulatedUsage) | |
| // Create response with all executed tool results from all iterations, and non-auto-executable tool calls | |
| return adapter.createResponseWithExecutedTools(currentResponse, allExecutedToolResults, allExecutedToolCalls, nonAutoExecutableTools), nil |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@core/mcp/agent.go` around lines 332 - 338, The early-return when depth == 1
&& len(allExecutedToolResults) == 0 returns currentResponse without applying
accumulatedUsage; call adapter.applyUsage(currentResponse, accumulatedUsage)
before that return so usage is always applied (mirroring the later path that
applies usage then calls adapter.createResponseWithExecutedTools), keeping the
rest of the logic and returned value (currentResponse) otherwise unchanged.
| // getCanonicalToolName returns the exact callable tool identifier exposed in Starlark. | ||
| func getCanonicalToolName(clientName, originalToolName string) string { | ||
| return parseToolName(stripClientPrefix(originalToolName, clientName)) | ||
| } | ||
|
|
||
| // getCompatibilityToolAlias returns the case-preserving alias derived from the raw tool name. | ||
| // This is used as a compatibility alias when the raw name is still a valid Starlark identifier. | ||
| func getCompatibilityToolAlias(clientName, originalToolName string) string { | ||
| return strings.ReplaceAll(stripClientPrefix(originalToolName, clientName), "-", "_") | ||
| } | ||
|
|
||
| // matchesToolReference reports whether the requested tool name matches any supported identifier form. | ||
| // We accept the canonical callable name plus legacy display forms for backward compatibility. | ||
| func matchesToolReference(requestedToolName, clientName, originalToolName string) bool { | ||
| requested := strings.ToLower(requestedToolName) | ||
| if requested == "" { | ||
| return false | ||
| } | ||
|
|
||
| candidates := []string{ | ||
| getCanonicalToolName(clientName, originalToolName), | ||
| getCompatibilityToolAlias(clientName, originalToolName), | ||
| stripClientPrefix(originalToolName, clientName), | ||
| } | ||
|
|
||
| for _, candidate := range candidates { | ||
| if candidate != "" && requested == strings.ToLower(candidate) { | ||
| return true | ||
| } | ||
| } | ||
|
|
||
| return false | ||
| } |
There was a problem hiding this comment.
Strip sanitized client prefixes before canonicalizing tool names.
These helpers only remove <client>-. In this repo the discovered MCP tool names are sanitized to _, so prefixed names like github_SEARCH_REPOS keep the server prefix and then surface as github_search_repos in list/read/docs/bindings instead of search_repos. The same assumption also leaks into callMCPTool, which reuses stripClientPrefix. Please normalize against the sanitized prefix too, and add a regression case for the client_tool form next to the new canonicalization tests.
Possible fix
func stripClientPrefix(prefixedToolName, clientName string) string {
- prefix := clientName + "-"
- if strings.HasPrefix(prefixedToolName, prefix) {
- return strings.TrimPrefix(prefixedToolName, prefix)
- }
+ prefixes := []string{
+ clientName + "-",
+ clientName + "_",
+ parseToolName(clientName) + "_",
+ }
+ for _, prefix := range prefixes {
+ if prefix != "" && strings.HasPrefix(prefixedToolName, prefix) {
+ return strings.TrimPrefix(prefixedToolName, prefix)
+ }
+ }
// If prefix doesn't match, return as-is (shouldn't happen, but be safe)
return prefixedToolName
}Based on learnings: In MCP tool system under core/mcp/, tool names are sanitized by replacing '-' with '_' during discovery and this sanitized form is used throughout the system, including CallToolParams.Name and ExtraFields.ToolName.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@core/mcp/codemode/starlark/utils.go` around lines 366 - 398, The helpers
getCanonicalToolName, getCompatibilityToolAlias and matchesToolReference
currently rely on stripClientPrefix which only strips the "<client>-" prefix,
but discovered MCP tool names are sanitized to use "_" (e.g.,
"github_SEARCH_REPOS" → "github_search_repos"); update stripClientPrefix (or
callers) to also remove the sanitized "<client>_" prefix (case-insensitively)
before further canonicalization so getCanonicalToolName and
getCompatibilityToolAlias yield "search_repos" and "search_repos" variants;
update matchesToolReference to compare against those normalized forms and add a
regression test adjacent to the canonicalization tests covering the
"client_tool" sanitized prefix case (and ensure callMCPTool usage of
stripClientPrefix benefits from the same change).
## Summary Reorders middleware initialization in the Bifrost HTTP server to ensure tracing middleware is added before transport interceptor middleware in the inference pipeline. ## Changes - Moved tracing middleware initialization and setup earlier in the bootstrap process - Reordered middleware registration so tracing middleware is prepended before transport interceptor middleware - Updated comments to clarify the middleware ordering logic and rationale The change ensures that tracing context and trace IDs are properly established before other middleware components process requests. ## Type of change - [x] Bug fix - [ ] Feature - [ ] Refactor - [ ] Documentation - [ ] Chore/CI ## Affected areas - [ ] Core (Go) - [x] Transports (HTTP) - [ ] Providers/Integrations - [ ] Plugins - [ ] UI (Next.js) - [ ] Docs ## How to test Verify that tracing middleware executes before transport interceptor middleware by checking trace logs and middleware execution order. ```sh # Core/Transports go version go test ./... ``` Test with tracing enabled to ensure trace IDs are properly set in context before subsequent middleware processing. ## Screenshots/Recordings N/A ## Breaking changes - [ ] Yes - [x] No ## Related issues N/A ## Security considerations No security implications - this is a middleware ordering change that affects observability components. ## 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
There was a problem hiding this comment.
Actionable comments posted: 5
♻️ Duplicate comments (2)
core/schemas/tracer.go (1)
41-42:⚠️ Potential issue | 🟠 MajorKeep
schemas.Tracerbackward-compatible.
Traceris exported, so these three changes are all source-breaking for external tracer implementations and mocks:CreateTrace(parentID, requestID ...string),PopulateLLMResponseAttributes(*BifrostContext, ...), and the new requiredAttachPluginLogs(...)method. Please keep the existing interface stable and move request-aware/plugin-log behavior into opt-in extension interfaces or adapters.Also applies to: 72-72, 115-117
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@core/schemas/tracer.go` around lines 41 - 42, The Tracer interface change is breaking; revert the exported interface schemas.Tracer to its original shape by restoring the previous CreateTrace(parentID string, requestID ...string) signature, removing any new required methods (e.g. AttachPluginLogs) and restoring PopulateLLMResponseAttributes to its prior optional form; instead implement new functionality as separate opt-in interfaces or adapters (e.g. define a RequestAwareTracer with PopulateLLMResponseAttributes and an AttachPluginLogsTracer with AttachPluginLogs) so existing external implementations and mocks remain source-compatible while callers can type-assert to the new extension interfaces when request-aware or plugin-log behavior is needed.plugins/logging/main.go (1)
803-804:⚠️ Potential issue | 🟠 MajorUse the resolved provider for pricing scope lookup.
entry.Provideris still request-derived here and can be stale after routing or fallback. That can apply provider-scoped overrides against the wrong provider and log the wrong cost. Use the final provider fromresult.GetExtraFields().Provider(or error extra fields when only an error is available) when buildingpricingScopes.💡 Suggested fix
- pricingScopes := modelcatalog.PricingLookupScopesFromContext(ctx, string(entry.Provider)) + providerForPricing := entry.Provider + if result != nil && result.GetExtraFields().Provider != "" { + providerForPricing = string(result.GetExtraFields().Provider) + } else if bifrostErr != nil && bifrostErr.ExtraFields.Provider != "" { + providerForPricing = string(bifrostErr.ExtraFields.Provider) + } + pricingScopes := modelcatalog.PricingLookupScopesFromContext(ctx, providerForPricing) if cost := p.pricingManager.CalculateCost(result, pricingScopes); cost > 0 { entry.Cost = &cost }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@plugins/logging/main.go` around lines 803 - 804, The code currently builds pricingScopes using the request-derived entry.Provider which may be stale; update the pricing scope lookup to use the resolved provider from the final result (use result.GetExtraFields().Provider, or if result represents an error use result.GetErrorExtraFields().Provider or equivalent) when calling modelcatalog.PricingLookupScopesFromContext so p.pricingManager.CalculateCost(result, pricingScopes) applies provider-scoped overrides and logs cost for the actual provider that served the request.
🧹 Nitpick comments (1)
core/schemas/trace.go (1)
71-74: Consider dropping oversizedPluginLogsbuffers before pooling.
Reset()truncates the slice but keeps its backing capacity. BecauseTraceinstances are pooled inframework/tracing/store.go, one noisy request can pin a largePluginLogsbuffer in the pool indefinitely. Nilling the slice — or only doing so above a capacity threshold — would avoid that retention.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@core/schemas/trace.go` around lines 71 - 74, The Reset() method on Trace currently zeroes elements and truncates PluginLogs but leaves its backing array, which can retain large buffers in the Trace pool; update Trace.Reset() to drop oversized PluginLogs buffers before pooling by setting t.PluginLogs = nil (or conditionally nil-ing when cap(t.PluginLogs) > <threshold>) instead of only slicing to [:0], ensuring PluginLogs is released to the GC and preventing a single noisy request from pinning large memory in the pool.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@core/schemas/context.go`:
- Around line 401-417: WithPluginScope currently assigns the root's done channel
into the scoped BifrostContext so the scoped context keeps its own doneOnce and
err while sharing the root done; this allows a plugin to call pluginCtx.Cancel()
and close the root done via the scoped doneOnce, corrupting root cancellation
and Err() state. Change WithPluginScope (and the similar block around the
423-432 region) so the scoped context does not copy the root done/doneOnce/err
but instead sets scoped.valueDelegate = bc and leaves scoped.done
nil/uninitialized; ensure Cancel(), Done(), and Err() implementations on
BifrostContext delegate cancellation/Done/Err calls to valueDelegate when
valueDelegate is non-nil so scoped contexts forward cancellation to the root
without owning the shared done state.
In `@framework/logstore/tables.go`:
- Around line 130-131: The batch-size accounting misses the new PluginLogs field
so estimateLogEntrySize() undercounts entries and can exceed maxBatchBytes;
update estimateLogEntrySize() in plugins/logging/writer.go to include the size
of the PluginLogs string (same way RoutingEngineLogs is counted) when computing
per-entry bytes, ensuring PluginLogs contributes to the batch byte total and
prevents batches from overshooting the 100 MB ceiling.
In `@plugins/logging/main.go`:
- Around line 809-813: The code always allocates entry.SelectedKey even when
none was resolved, causing empty-object serialization; change the logic in the
"Pre-apply denormalized fields" section so that you only set entry.SelectedKey =
&schemas.Key{...} when a key was actually resolved (e.g., when
entry.SelectedKeyID and/or entry.SelectedKeyName is non-empty or otherwise
indicates a resolved key). If neither identifier is present, leave
entry.SelectedKey as nil so the payload omits selected_key.
In `@transports/bifrost-http/handlers/middlewares.go`:
- Around line 341-350: The current code incorrectly reuses HTTPTransportPostHook
as an end-of-stream hook for streaming responses; instead add a separate
best-effort streaming completer and context key so post-hooks and stream-end
hooks remain distinct. Create a new context key (e.g.
BifrostContextKeyTransportStreamEndHookCompleter) and a new runner function
(e.g. runTransportStreamEndHooks) that invokes a new plugin method or calls
plugins in a best-effort way that ignores/does not short-circuit on errors; when
deferred streaming is detected (the check using
schemas.BifrostContextKeyDeferTraceCompletion), set that new completer into
context rather than reusing BifrostContextKeyTransportPostHookCompleter, and
leave runTransportPostHooks(ctx, plugins, bifrostCtx) behavior unchanged for
non-streaming responses so HTTPTransportPostHook continues to run immediately
and retain its error-short-circuit semantics.
In `@transports/bifrost-http/server/server.go`:
- Around line 515-537: The two concurrent calls to s.Client.ListModelsRequest
are sharing the mutable bfCtx which can cause request-scoped state to be
overwritten; create a fresh BifrostContext for each goroutine (e.g., clone or
construct a new context from the existing bfCtx) and pass that per-goroutine
context into ListModelsRequest (for both the filtered and the Unfiltered=true
calls) so each request has its own independent context.
---
Duplicate comments:
In `@core/schemas/tracer.go`:
- Around line 41-42: The Tracer interface change is breaking; revert the
exported interface schemas.Tracer to its original shape by restoring the
previous CreateTrace(parentID string, requestID ...string) signature, removing
any new required methods (e.g. AttachPluginLogs) and restoring
PopulateLLMResponseAttributes to its prior optional form; instead implement new
functionality as separate opt-in interfaces or adapters (e.g. define a
RequestAwareTracer with PopulateLLMResponseAttributes and an
AttachPluginLogsTracer with AttachPluginLogs) so existing external
implementations and mocks remain source-compatible while callers can type-assert
to the new extension interfaces when request-aware or plugin-log behavior is
needed.
In `@plugins/logging/main.go`:
- Around line 803-804: The code currently builds pricingScopes using the
request-derived entry.Provider which may be stale; update the pricing scope
lookup to use the resolved provider from the final result (use
result.GetExtraFields().Provider, or if result represents an error use
result.GetErrorExtraFields().Provider or equivalent) when calling
modelcatalog.PricingLookupScopesFromContext so
p.pricingManager.CalculateCost(result, pricingScopes) applies provider-scoped
overrides and logs cost for the actual provider that served the request.
---
Nitpick comments:
In `@core/schemas/trace.go`:
- Around line 71-74: The Reset() method on Trace currently zeroes elements and
truncates PluginLogs but leaves its backing array, which can retain large
buffers in the Trace pool; update Trace.Reset() to drop oversized PluginLogs
buffers before pooling by setting t.PluginLogs = nil (or conditionally nil-ing
when cap(t.PluginLogs) > <threshold>) instead of only slicing to [:0], ensuring
PluginLogs is released to the GC and preventing a single noisy request from
pinning large memory in the pool.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: f0e50c41-5ffe-4409-a9ce-c499db1b8860
📒 Files selected for processing (22)
core/bifrost.gocore/schemas/bifrost.gocore/schemas/context.gocore/schemas/context_test.gocore/schemas/trace.gocore/schemas/tracer.goexamples/plugins/hello-world/main.goframework/logstore/migrations.goframework/logstore/rdb.goframework/logstore/store.goframework/logstore/tables.goframework/tracing/store.goframework/tracing/tracer.goplugins/logging/main.goplugins/logging/operations_test.goplugins/logging/writer.gotransports/bifrost-http/handlers/middlewares.gotransports/bifrost-http/lib/config.gotransports/bifrost-http/server/server.goui/app/workspace/logs/sheets/logDetailsSheet.tsxui/app/workspace/logs/views/pluginLogsView.tsxui/lib/types/logs.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- core/schemas/bifrost.go
👮 Files not reviewed due to content moderation or server errors (1)
- core/bifrost.go
| func (bc *BifrostContext) WithPluginScope(name *string) *BifrostContext { | ||
| // Lazily initialize the plugin log store on the root context (CAS to avoid race) | ||
| if bc.pluginLogs.Load() == nil { | ||
| newStore := pluginLogStorePool.Get().(*pluginLogStore) | ||
| if !bc.pluginLogs.CompareAndSwap(nil, newStore) { | ||
| // Another goroutine initialized first — return unused store to pool | ||
| pluginLogStorePool.Put(newStore) | ||
| } | ||
| } | ||
|
|
||
| scoped := pluginScopePool.Get().(*BifrostContext) | ||
| scoped.parent = bc.parent | ||
| scoped.done = bc.done | ||
| scoped.pluginScope = name | ||
| scoped.pluginLogs.Store(bc.pluginLogs.Load()) | ||
| scoped.valueDelegate = bc | ||
| return scoped |
There was a problem hiding this comment.
Scoped plugin contexts can corrupt the root cancellation state.
WithPluginScope reuses the root done channel, but the scoped wrapper keeps its own doneOnce and err. If a plugin calls pluginCtx.Cancel(), the scope closes the root channel through its own doneOnce; later the root can panic when it tries to cancel, and its Err() state is never updated correctly. Scoped contexts need to delegate cancellation to valueDelegate instead of owning shared done.
Also applies to: 423-432
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@core/schemas/context.go` around lines 401 - 417, WithPluginScope currently
assigns the root's done channel into the scoped BifrostContext so the scoped
context keeps its own doneOnce and err while sharing the root done; this allows
a plugin to call pluginCtx.Cancel() and close the root done via the scoped
doneOnce, corrupting root cancellation and Err() state. Change WithPluginScope
(and the similar block around the 423-432 region) so the scoped context does not
copy the root done/doneOnce/err but instead sets scoped.valueDelegate = bc and
leaves scoped.done nil/uninitialized; ensure Cancel(), Done(), and Err()
implementations on BifrostContext delegate cancellation/Done/Err calls to
valueDelegate when valueDelegate is non-nil so scoped contexts forward
cancellation to the root without owning the shared done state.
| RoutingEngineLogs string `gorm:"type:text" json:"routing_engine_logs,omitempty"` // Formatted routing engine decision logs | ||
| PluginLogs string `gorm:"type:text" json:"plugin_logs,omitempty"` // JSON serialized plugin log entries grouped by plugin name |
There was a problem hiding this comment.
Update batch-size accounting for PluginLogs.
plugins/logging/writer.go:estimateLogEntrySize() is what enforces the 100 MB maxBatchBytes ceiling, and it currently counts RoutingEngineLogs but not this new PluginLogs text field. Once plugin logs are populated, batches can overshoot the intended cap by a lot, which defeats the safeguard against memory and latency spikes.
💡 Cross-file fix
diff --git a/plugins/logging/writer.go b/plugins/logging/writer.go
@@
len(log.PassthroughRequestBody) +
len(log.PassthroughResponseBody) +
len(log.ContentSummary) +
len(log.CacheDebug) +
- len(log.RoutingEngineLogs)
+ len(log.RoutingEngineLogs) +
+ len(log.PluginLogs)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@framework/logstore/tables.go` around lines 130 - 131, The batch-size
accounting misses the new PluginLogs field so estimateLogEntrySize() undercounts
entries and can exceed maxBatchBytes; update estimateLogEntrySize() in
plugins/logging/writer.go to include the size of the PluginLogs string (same way
RoutingEngineLogs is counted) when computing per-entry bytes, ensuring
PluginLogs contributes to the batch byte total and prevents batches from
overshooting the 100 MB ceiling.
| // Pre-apply denormalized fields for WebSocket callback enrichment | ||
| entry.SelectedKey = &schemas.Key{ | ||
| ID: entry.SelectedKeyID, | ||
| Name: entry.SelectedKeyName, | ||
| } |
There was a problem hiding this comment.
Only populate SelectedKey when one was actually resolved.
This pointer is now always non-nil, so payloads that previously omitted selected_key will start serializing {"id":"","name":""}. That changes the websocket payload shape and can make the UI treat “no key selected” as a real selection.
💡 Suggested fix
- entry.SelectedKey = &schemas.Key{
- ID: entry.SelectedKeyID,
- Name: entry.SelectedKeyName,
- }
+ if entry.SelectedKeyID != "" || entry.SelectedKeyName != "" {
+ entry.SelectedKey = &schemas.Key{
+ ID: entry.SelectedKeyID,
+ Name: entry.SelectedKeyName,
+ }
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Pre-apply denormalized fields for WebSocket callback enrichment | |
| entry.SelectedKey = &schemas.Key{ | |
| ID: entry.SelectedKeyID, | |
| Name: entry.SelectedKeyName, | |
| } | |
| // Pre-apply denormalized fields for WebSocket callback enrichment | |
| if entry.SelectedKeyID != "" || entry.SelectedKeyName != "" { | |
| entry.SelectedKey = &schemas.Key{ | |
| ID: entry.SelectedKeyID, | |
| Name: entry.SelectedKeyName, | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@plugins/logging/main.go` around lines 809 - 813, The code always allocates
entry.SelectedKey even when none was resolved, causing empty-object
serialization; change the logic in the "Pre-apply denormalized fields" section
so that you only set entry.SelectedKey = &schemas.Key{...} when a key was
actually resolved (e.g., when entry.SelectedKeyID and/or entry.SelectedKeyName
is non-empty or otherwise indicates a resolved key). If neither identifier is
present, leave entry.SelectedKey as nil so the payload omits selected_key.
| // For streaming responses, store a callback to run post-hooks after the stream ends. | ||
| // The streaming handler calls this before traceCompleter. | ||
| if deferred, ok := ctx.UserValue(schemas.BifrostContextKeyDeferTraceCompletion).(bool); ok && deferred { | ||
| ctx.SetUserValue(schemas.BifrostContextKeyTransportPostHookCompleter, func() { | ||
| runTransportPostHooks(ctx, plugins, bifrostCtx) | ||
| }) | ||
| return | ||
| } | ||
|
|
||
| // Acquire pooled response for post-hooks (non-streaming only) | ||
| httpResp := schemas.AcquireHTTPResponse() | ||
| defer schemas.ReleaseHTTPResponse(httpResp) | ||
| fasthttpResponseToHTTPResponse(ctx, httpResp) | ||
| // Run http post-hooks in reverse order | ||
| for i := len(plugins) - 1; i >= 0; i-- { | ||
| plugin := plugins[i] | ||
| err := plugin.HTTPTransportPostHook(bifrostCtx, req, httpResp) | ||
| if err != nil { | ||
| logger.Warn("error in HTTPTransportPostHook for plugin %s: %s", plugin.GetName(), err.Error()) | ||
| // Short-circuit with response | ||
| applyHTTPResponseToCtx(ctx, httpResp) | ||
| return | ||
| runTransportPostHooks(ctx, plugins, bifrostCtx) |
There was a problem hiding this comment.
Don’t repurpose HTTPTransportPostHook as a streaming end-of-stream hook.
The plugin contract currently says HTTPTransportPostHook is not called for streaming responses and that a returned error short-circuits the response. Deferring it until after the stream ends breaks both guarantees at once: existing transport plugins start receiving unexpected streaming post-hooks, and once bytes are already written there is no way to honor a returned error. This needs a separate best-effort end-of-stream hook, not reuse of HTTPTransportPostHook.
Also applies to: 355-393
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@transports/bifrost-http/handlers/middlewares.go` around lines 341 - 350, The
current code incorrectly reuses HTTPTransportPostHook as an end-of-stream hook
for streaming responses; instead add a separate best-effort streaming completer
and context key so post-hooks and stream-end hooks remain distinct. Create a new
context key (e.g. BifrostContextKeyTransportStreamEndHookCompleter) and a new
runner function (e.g. runTransportStreamEndHooks) that invokes a new plugin
method or calls plugins in a best-effort way that ignores/does not short-circuit
on errors; when deferred streaming is detected (the check using
schemas.BifrostContextKeyDeferTraceCompletion), set that new completer into
context rather than reusing BifrostContextKeyTransportPostHookCompleter, and
leave runTransportPostHooks(ctx, plugins, bifrostCtx) behavior unchanged for
non-streaming responses so HTTPTransportPostHook continues to run immediately
and retain its error-short-circuit semantics.
…2751) ## Summary Improves chart card layout consistency and removes unnecessary width constraints from date picker components. ## Changes - Applied className prop to ChartCard loading state for consistent styling - Reduced bottom margin in chart card header from `mb-3` to `mb-2` - Removed inline `marginBottom: 6` style from chart skeleton container - Added `min-h-5` class to chart header legend for consistent height - Removed fixed width constraints from DateTimePickerWithRange button - Simplified logs layout by removing unused Outlet and child match logic ## Type of change - [x] Refactor - [ ] Bug fix - [ ] Feature - [ ] Documentation - [ ] Chore/CI ## Affected areas - [ ] Core (Go) - [ ] Transports (HTTP) - [ ] Providers/Integrations - [ ] Plugins - [x] UI (React) - [ ] Docs ## How to test Verify chart cards display consistently across different loading states and that date picker components adapt to their container width properly. ```sh # UI cd ui pnpm i || npm i pnpm test || npm test pnpm build || npm run build ``` ## Screenshots/Recordings If UI changes, add before/after screenshots or short clips. ## Breaking changes - [x] No - [ ] Yes ## Related issues Link related issues and discussions. Example: Closes #123 ## Security considerations No security implications. ## 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
…ogs (#2752) ## Summary Replace skeleton loading states with animated NumberFlow components for statistics displays in logs and MCP logs pages. This provides smoother visual transitions when data loads and updates. ## Changes - Replaced skeleton loading placeholders with NumberFlow components for all statistics (total requests, success rate, average latency, total tokens, total cost) - Removed `fetchingStats` state variables and related loading logic since NumberFlow handles transitions gracefully - Added `@number-flow/react` dependency for animated number transitions - Created `COMPACT_NUMBER_FORMAT` utility for consistent number formatting - Moved chart loading skeleton to render within the chart container instead of replacing the entire component - Updated import ordering for better organization ## Type of change - [x] Feature - [ ] Bug fix - [ ] Refactor - [ ] Documentation - [ ] Chore/CI ## Affected areas - [ ] Core (Go) - [ ] Transports (HTTP) - [ ] Providers/Integrations - [ ] Plugins - [x] UI (React) - [ ] Docs ## How to test Navigate to the logs and MCP logs pages and verify that statistics display with smooth number animations when data loads or filters change. ```sh # UI cd ui pnpm i || npm i pnpm test || npm test pnpm build || npm run build ``` ## Screenshots/Recordings Statistics now show animated number transitions instead of skeleton loading states when data updates. ## Breaking changes - [ ] Yes - [x] No ## Related issues Improves user experience with smoother loading states for numerical data. ## Security considerations No security implications - this is a UI enhancement for data presentation. ## 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
…istence (#2134) ## Summary Adds support for variables in prompt sessions and versions, allowing users to define and persist template variables that can be used in Jinja-style templating within prompts. ## Changes - Added `variables` field to `PromptVersion`, `PromptSession`, and related request/response types - Updated session creation and saving logic to include variables when they exist - Modified prompt context to restore variables from sessions and initialize variables from versions - Enabled the variables table view in the settings panel (previously commented out) - Improved Jinja variable regex pattern to be more restrictive and accurate - Variables are conditionally included in API requests only when they contain data ## Type of change - [x] Feature - [ ] Bug fix - [ ] Refactor - [ ] Documentation - [ ] Chore/CI ## Affected areas - [ ] Core (Go) - [ ] Transports (HTTP) - [ ] Providers/Integrations - [ ] Plugins - [x] UI (Next.js) - [ ] Docs ## How to test 1. Create or open a prompt with Jinja-style variables (e.g., `{{ variable_name }}`) 2. Verify that variables are automatically detected and displayed in the settings panel 3. Set values for the variables in the variables table 4. Save the session and verify variables persist when reloading 5. Test that variables are properly initialized from versions vs restored from sessions ```sh # UI cd ui pnpm i || npm i pnpm test || npm test pnpm build || npm run build ``` ## Screenshots/Recordings N/A ## Breaking changes - [ ] Yes - [x] No ## Related issues N/A ## Security considerations Variables are stored as key-value pairs and should be validated on the backend to prevent injection attacks when used in template rendering. ## 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
…base migration (#2154) ## Summary Adds support for Jinja2 template variables in prompt sessions and versions. This enables users to define reusable prompts with placeholders like `{{ name }}` and `{{ topic }}` that can be dynamically replaced with values. ## Changes - Added `variables_json` database columns to `prompt_sessions` and `prompt_versions` tables with migration - Created `PromptVariables` type as a map of variable names to values - Added JSON serialization/deserialization hooks for the new variables fields - Updated HTTP handlers to accept and process variables in create/update requests - Modified session commit logic to store variable keys (with empty values) in versions while preserving full key-value pairs in sessions - Enhanced frontend variable extraction to skip assistant/response messages (variables only processed for system and user messages) - Improved variable replacement logic to handle edge cases with extra curly braces ## Type of change - [x] Feature ## Affected areas - [x] Core (Go) - [x] Transports (HTTP) - [x] UI (Next.js) ## How to test Test the database migration and variable functionality: ```sh # Core/Transports go version go test ./... # UI cd ui pnpm i || npm i pnpm test || npm test pnpm build || npm run build ``` Create a prompt session with variables like `{{ name }}` in message content, verify they can be set and replaced correctly. Test that committing a session preserves variable structure in the created version. ## Breaking changes - [ ] Yes - [x] No ## Security considerations Variables are stored as JSON text in the database and processed server-side. No direct template execution occurs - only simple string replacement, reducing security risks from template injection. ## 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
…nu portal support (#2237) ## Summary Refactored the CEL Rule Builder component to be reusable across different contexts by extracting it from routing-specific code into a shared UI component library. ## Changes - Extracted CEL Rule Builder components from `ui/app/workspace/routing-rules/components/celBuilder/` to `ui/components/ui/custom/celBuilder/` to make them reusable - Created a generic `CELRuleBuilder` component that accepts field definitions, operators, and conversion functions as props - Refactored the routing-specific CEL builder to use the new reusable component as a thin wrapper - Enhanced `AsyncMultiSelect` and `ModelMultiselect` components with `menuPortalTarget` and `menuPosition` props for better portal control in different contexts - Added optional regex validation support through context props - Added VS Code workspace configuration for bifrost development ## Type of change - [x] Refactor ## Affected areas - [x] UI (Next.js) ## How to test Verify that the routing rules CEL builder continues to work as expected: ```sh # UI cd ui pnpm i || npm i pnpm test || npm test pnpm build || npm run build ``` Test the routing rules page to ensure the CEL builder functionality remains intact and that multiselect components render properly in different contexts. ## Screenshots/Recordings N/A - This is a refactoring change that maintains existing functionality. ## Breaking changes - [ ] Yes - [x] No ## Related issues N/A ## Security considerations No security implications - this is a pure refactoring change that maintains existing functionality. ## 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 Restructures the prompt repository navigation by moving the main prompts view to the parent route and converting the nested prompts page to a redirect. This simplifies the URL structure from `/workspace/prompt-repo/prompts` to `/workspace/prompt-repo` while maintaining backward compatibility. ## Changes - Created new `/workspace/prompt-repo/page.tsx` that renders the main prompts view with PromptProvider context - Modified `/workspace/prompt-repo/prompts/page.tsx` to redirect to the parent route while preserving query parameters - Simplified sidebar navigation by removing the nested "Prompts" and "Deployments" sub-items and making "Prompt Repository" a direct link - Removed unused imports (Router, SquareTerminal icons) from the sidebar component ## Type of change - [ ] Bug fix - [x] Feature - [x] Refactor - [ ] Documentation - [ ] Chore/CI ## Affected areas - [ ] Core (Go) - [ ] Transports (HTTP) - [ ] Providers/Integrations - [ ] Plugins - [x] UI (Next.js) - [ ] Docs ## How to test Navigate to the prompt repository section and verify: 1. `/workspace/prompt-repo` displays the prompts view correctly 2. `/workspace/prompt-repo/prompts` redirects to `/workspace/prompt-repo` 3. Query parameters are preserved during redirect 4. Sidebar shows "Prompt Repository" as a direct link without sub-items ```sh # UI cd ui pnpm i || npm i pnpm test || npm test pnpm build || npm run build ``` ## Screenshots/Recordings N/A ## Breaking changes - [ ] Yes - [x] No The old `/workspace/prompt-repo/prompts` URL continues to work via redirect, ensuring backward compatibility. ## Related issues N/A ## Security considerations No security implications - this is a navigation restructure that maintains the same 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
…ng (#2352) ## Summary Integrates the prompt deployments enterprise feature into the UI by adding the PromptDeploymentView component to the settings panel and updating its visual styling. ## Changes - Added PromptDeploymentView component to the prompts settings panel to display enterprise prompt deployment information - Updated PromptDeploymentView styling to use a more compact card-like design with top alignment, smaller icon, and border styling - Added enterprise UI source path to CSS configuration to include enterprise component styles ## Type of change - [x] Feature - [ ] Bug fix - [ ] Refactor - [ ] Documentation - [ ] Chore/CI ## Affected areas - [ ] Core (Go) - [ ] Transports (HTTP) - [ ] Providers/Integrations - [ ] Plugins - [x] UI (Next.js) - [ ] Docs ## How to test Verify the prompt deployments enterprise feature appears in the settings panel with proper styling. ```sh # UI cd ui pnpm i || npm i pnpm test || npm test pnpm build || npm run build ``` Navigate to the prompts page and open the settings panel to verify the PromptDeploymentView component renders correctly with the updated card-style layout. ## Screenshots/Recordings The PromptDeploymentView now appears as a compact card in the settings panel instead of a full-height centered view. ## Breaking changes - [ ] Yes - [x] No ## Related issues Related to enterprise prompt deployments feature integration. ## Security considerations No security implications - this is a UI enhancement for displaying enterprise feature information. ## 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 Extracted routing-related CEL (Common Expression Language) functionality into a dedicated framework package to improve code organization and reusability. This refactoring moves CEL environment creation, expression validation, and map key normalization functions from the governance plugin to a shared routing framework. ## Changes - Created new `framework/routing/routing.go` package with exported functions for CEL operations - Moved `createCELEnvironment`, `validateCELExpression`, and `normalizeMapKeysInCEL` functions from governance plugin to routing framework - Updated governance plugin to import and use the new routing framework functions - Modified test files to reference the new exported functions from the routing package - Removed duplicate regex patterns and function definitions from governance plugin ## Type of change - [x] Refactor - [ ] Bug fix - [ ] Feature - [ ] Documentation - [ ] Chore/CI ## Affected areas - [x] Core (Go) - [x] Plugins - [ ] Transports (HTTP) - [ ] Providers/Integrations - [ ] UI (Next.js) - [ ] Docs ## How to test Validate that the refactoring maintains existing functionality by running the test suite: ```sh # Core/Transports go version go test ./... # Specifically test the affected areas go test ./framework/routing/... go test ./plugins/governance/... ``` ## Screenshots/Recordings N/A - This is a code refactoring with no UI changes. ## Breaking changes - [ ] Yes - [x] No This is an internal refactoring that maintains the same public API and functionality. ## Related issues N/A ## Security considerations No security implications - this is a code organization refactoring that maintains the same CEL expression validation and normalization logic. ## 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
## Changes Refactors the prompts plugin to improve error handling, simplify the resolver interface, and add support for enterprise prompts plugin routing. The changes prepare the plugin for better integration with enterprise features while maintaining backward compatibility. - **Simplified PromptResolver interface**: Removed `versionSpecified` boolean return value from `Resolve` method and simplified version resolution logic - **Added enterprise support**: Added `BifrostContextKeyPromptsPluginName` context key and routing logic to support enterprise prompts plugin selection - **Removed streaming logic**: Eliminated `BifrostContextKeyPromptStreamRequest` and related stream detection from model parameters in transport hooks - **Interface standardization**: Changed `promptStore` to exported `InMemoryStore` interface for better extensibility - **Enhanced context tracking**: Added `BifrostContextKeySelectedPromptName`, `BifrostContextKeySelectedPromptVersion`, and `BifrostContextKeySelectedPromptID` for governance and observability - **Updated header names**: Changed prompt headers from `bf-prompt-id`/`bf-prompt-version` to `x-bf-prompt-id`/`x-bf-prompt-version` - **Refactored version resolution**: Simplified `resolveVersion` method to handle explicit version numbers vs latest version more clearly - **Improved documentation**: Added comprehensive package and function documentation throughout the plugin ## Type of change - [x] Refactor - [x] Feature ## Affected areas - [x] Core (Go) - [x] Transports (HTTP) - [x] Plugins - [x] UI (Next.js) ## 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 ``` Test prompt resolution with both header-based and custom resolvers. Verify enterprise prompts plugin routing works when `BifrostContextKeyPromptsPluginName` is set. ## Breaking changes - [x] Yes The `PromptResolver` interface signature changed from `Resolve(ctx, req) (string, int, bool, error)` to `Resolve(ctx, req) (string, int, error)`. Custom resolver implementations will need to be updated to remove the `versionSpecified` boolean return value. Additionally, prompt headers changed from `bf-prompt-id`/`bf-prompt-version` to `x-bf-prompt-id`/`x-bf-prompt-version`. ## Security considerations No security implications - this is an internal refactoring that maintains the same access patterns and validation logic.
## Summary Refactored the prompt settings panel to use an accordion layout, separating configuration and deployments into collapsible sections for improved organization and space utilization. ## Changes - Converted the settings panel from a scrollable layout to an accordion-based interface with "Configuration" and "Deployments" sections - Added `PromptDeploymentsAccordionItem` component to handle the deployments section within the accordion - Enhanced the `AccordionContent` component with a `containerClassName` prop for better layout control - Modified `PromptDeploymentView` to accept an optional `omitTitle` prop for cleaner integration - Improved accordion trigger styling for better alignment and spacing ## Type of change - [ ] Bug fix - [x] Feature - [ ] Refactor - [ ] Documentation - [ ] Chore/CI ## Affected areas - [ ] Core (Go) - [ ] Transports (HTTP) - [ ] Providers/Integrations - [ ] Plugins - [x] UI (Next.js) - [ ] Docs ## How to test Validate the accordion functionality and layout in the prompt settings panel: 1. Navigate to the prompts interface 2. Select a prompt to view the settings panel 3. Verify the "Configuration" section expands/collapses properly 4. Verify the "Deployments" section appears when a prompt is selected 5. Test that only one section can be open at a time 6. Ensure all existing functionality (provider selection, model parameters, etc.) works within the accordion ```sh # UI cd ui pnpm i || npm i pnpm test || npm test pnpm build || npm run build ``` ## 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 layout refactor that doesn't affect authentication, data handling, or API interactions. ## 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 support for tracking and displaying selected prompt information in the logging system. This enhancement allows users to see which prompt name and version were used for each logged request in the UI. ## Changes - Added `selected_prompt_name` and `selected_prompt_version` columns to the Log table schema - Created database migration to add the new columns with proper rollback support - Updated logging plugin to extract prompt information from context and populate the new fields - Enhanced UI log detail view to display selected prompt name and version when available - Added TypeScript type definitions for the new prompt fields ## Type of change - [x] Feature - [ ] Bug fix - [ ] Refactor - [ ] Documentation - [ ] Chore/CI ## Affected areas - [x] Core (Go) - [ ] Transports (HTTP) - [ ] Providers/Integrations - [x] Plugins - [x] UI - [ ] Docs ## How to test Verify the database migration runs successfully and the UI displays prompt information: ```sh # Core/Transports go version go test ./... # UI cd ui pnpm i || npm i pnpm test || npm test pnpm build || npm run build ``` Test that logs with prompt information display the "Selected Prompt" field in the log detail view, showing both name and version when available. ## Screenshots/Recordings The UI now displays a "Selected Prompt" field in the log detail view showing the prompt name and version (e.g., "my-prompt · v1.2"). ## Breaking changes - [ ] Yes - [x] No ## Related issues Enhances logging capabilities for prompt tracking in enterprise deployments and prompts plugin usage. ## Security considerations No security implications - this only adds display fields for prompt metadata that's already available in the system context. ## 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
Updates the Prompts plugin to use standardized `x-bf-` prefixed headers and improves documentation clarity around parameter merging, streaming behavior, and observability integration.
## Changes
- Changed prompt plugin headers from `bf-prompt-id`/`bf-prompt-version` to `x-bf-prompt-id`/`x-bf-prompt-version` for consistency
- Clarified that streaming is controlled entirely by client requests, not merged from committed versions
- Added documentation for prompt tracking in observability logs (name, version, ID)
- Enhanced playground documentation to describe the collapsible settings panel sections
- Improved Go SDK documentation with `PromptResolver` interface details and context key information
- Updated all code examples and curl commands to use the new header names
## 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
Test the updated prompt plugin headers:
```sh
# Test with new headers
curl -X POST http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-H "x-bf-prompt-id: YOUR-PROMPT-UUID" \
-H "x-bf-prompt-version: 4" \
-H "x-bf-vk: sk-bf-your-virtual-key" \
-d '{"model": "openai/gpt-4", "messages": [{"role": "user", "content": "Hello"}]}'
# Verify observability logs capture prompt tracking information
# Check that logs show Selected Prompt name, version, and ID
# Core/Transports
go version
go test ./...
```
Verify that the old `bf-prompt-id` headers no longer work and the new `x-bf-prompt-id` headers function correctly.
## Screenshots/Recordings
N/A
## Breaking changes
- [x] Yes
- [ ] No
**Breaking change**: The prompt plugin now requires `x-bf-prompt-id` and `x-bf-prompt-version` headers instead of `bf-prompt-id` and `bf-prompt-version`.
**Migration**: Update all client code to use the new header names:
- `bf-prompt-id` → `x-bf-prompt-id`
- `bf-prompt-version` → `x-bf-prompt-version`
## Related issues
N/A
## Security considerations
Header name changes do not introduce security implications. The functionality remains the same with standardized naming.
## 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
…2771) ## Summary Restructures the Helm deployment documentation into a comprehensive multi-page guide with dedicated sections for each configuration area. The main Helm page now provides quickstart instructions for both OSS and Enterprise deployments, while detailed configuration is split into focused sub-pages. ## Changes - **Restructured main Helm page**: Condensed from 740+ lines to 103 lines with clear quickstart tabs for OSS vs Enterprise - **Added 8 new dedicated configuration pages**: - `values.mdx` - Complete values reference with examples and common patterns - `client.mdx` - Client configuration (pool size, logging, CORS, auth, compat shims) - `providers.mdx` - Provider setup for all 23+ supported LLM providers with cloud-native auth - `storage.mdx` - Storage backends (SQLite, PostgreSQL, object storage, vector stores) - `plugins.mdx` - Plugin configuration (telemetry, logging, semantic cache, OTel, Datadog) - `governance.mdx` - Governance setup (budgets, rate limits, virtual keys, routing rules) - `cluster.mdx` - Multi-replica HA with gossip-based peer discovery - `troubleshooting.mdx` - Common issues and diagnostic commands - **Updated chart version**: Bumped from 1.5.0 to 2.1.0 - **Enhanced navigation**: Added nested Helm section in docs.json with proper icons and organization ## Type of change - [x] Documentation ## Affected areas - [x] Docs ## How to test Navigate through the new Helm documentation structure: 1. Visit the main Helm page for quickstart instructions 2. Follow the quickstart for either OSS or Enterprise deployment 3. Use the sub-pages for detailed configuration of specific areas 4. Verify all internal links work correctly 5. Test the troubleshooting commands on a real deployment The documentation now provides both quick-start paths and comprehensive reference material for production deployments. ## Screenshots/Recordings N/A - Documentation changes only ## Breaking changes - [ ] Yes - [x] No This is purely a documentation restructure with no functional changes to the Helm chart itself. ## Related issues Improves Helm documentation organization and usability for both new users and production deployments. ## Security considerations The new documentation emphasizes security best practices: - Kubernetes Secrets for all sensitive values - Cloud-native authentication (IRSA, Workload Identity, Managed Identity) - Proper RBAC setup for cluster mode - Compliance considerations (HIPAA, PCI) for content logging ## 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 Adds dynamic metadata columns to the logs table that display all available metadata keys from the API, ensuring metadata columns are always visible even when no data is present on the current page. ## Changes - Added `useGetAvailableFilterDataQuery` hook to fetch available metadata keys from the API - Modified `createColumns` function to accept a `metadataKeys` parameter and generate dynamic metadata columns - Updated column labels logic to include dynamically generated metadata column headers - Metadata columns display with proper formatting and truncation for long values ## Type of change - [x] Feature - [ ] Bug fix - [ ] Refactor - [ ] Documentation - [ ] Chore/CI ## Affected areas - [ ] Core (Go) - [ ] Transports (HTTP) - [ ] Providers/Integrations - [ ] Plugins - [x] UI (React) - [ ] Docs ## How to test Verify that metadata columns appear in the logs table even when the current page has no log entries with metadata: 1. Navigate to the logs page 2. Ensure metadata columns are visible in the table header 3. Verify metadata values display correctly when present, and show "-" when absent 4. Test with different metadata keys to confirm dynamic column generation ```sh # UI cd ui pnpm i || npm i pnpm test || npm test pnpm build || npm run build ``` ## 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 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
## Summary Adds a virtual key count field to team queries to display the number of virtual keys associated with each team without requiring additional database queries or loading the full virtual keys collection. ## Changes - Added `VirtualKeyCount` field to `TableTeam` struct as a computed field populated via correlated subquery - Modified `GetTeams`, `GetTeamsPaginated`, and `GetTeam` methods to include virtual key count in SELECT queries - Updated TypeScript types to allow nullable `team_id` and `customer_id` in `UpdateVirtualKeyRequest` - Changed `VirtualKeys` field in team table to be omitempty for cleaner JSON responses ## Type of change - [x] Feature - [ ] Bug fix - [ ] Refactor - [ ] Documentation - [ ] Chore/CI ## Affected areas - [x] Core (Go) - [ ] Transports (HTTP) - [ ] Providers/Integrations - [ ] Plugins - [x] UI (React) - [ ] Docs ## How to test Verify that team queries return the virtual key count and that virtual key updates handle nullable team/customer IDs: ```sh # Core/Transports go version go test ./... # UI cd ui pnpm i || npm i pnpm test || npm test pnpm build || npm run build ``` Test API endpoints that retrieve teams to ensure `virtual_key_count` field is populated correctly. ## Screenshots/Recordings N/A ## Breaking changes - [ ] Yes - [x] No ## Related issues N/A ## Security considerations The correlated subquery only counts records and doesn't expose sensitive virtual key data. No additional security implications. ## 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 API endpoint `/api/models/details` to provide detailed model information through the HTTP transport layer. ## Changes - Added new GET route `/api/models/details` that maps to the `listModelDetails` handler method - Route follows existing pattern with middleware chaining for consistent request processing ## Type of change - [x] Feature - [ ] Bug fix - [ ] Refactor - [ ] Documentation - [ ] Chore/CI ## Affected areas - [ ] Core (Go) - [x] Transports (HTTP) - [ ] Providers/Integrations - [ ] Plugins - [ ] UI (React) - [ ] Docs ## How to test Test the new endpoint by making a GET request to verify it returns model details: ```sh # Test the new endpoint curl -X GET http://localhost:8080/api/models/details # Run existing tests to ensure no regressions go test ./transports/bifrost-http/... ``` ## Screenshots/Recordings N/A ## Breaking changes - [ ] Yes - [x] No ## Related issues ## Security considerations The endpoint uses the same middleware chain as other model-related endpoints, maintaining consistent authentication and authorization patterns. ## 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
…2760) ## Summary Consolidates the Bifrost context key used for user identity: removes the governance-scoped `BifrostContextKeyGovernanceUserID` in favor of a single `BifrostContextKeyUserID` (`bifrost-user-id`), and adds a companion `BifrostContextKeyUserName` (`bifrost-user-name`). The user name is threaded through the logging + realtime relay paths and persisted as a new `user_name` column on the `logs` table so downstream UIs can display it without a separate lookup. ## Changes - `core/schemas/bifrost.go`: drop `BifrostContextKeyGovernanceUserID`; change `BifrostContextKeyUserID` value from `"user_id"` to `"bifrost-user-id"`; add companion `BifrostContextKeyUserName` next to it. Both carry matching doc annotations ("set by enterprise auth middleware — DO NOT SET THIS MANUALLY") so the pair is discoverable together. - Update every caller of the old governance-scoped key to use `BifrostContextKeyUserID`: `core/mcp/toolmanager.go`, `framework/oauth2/main.go`, `plugins/governance/main.go`, `transports/bifrost-http/handlers/mcpserver.go`, `transports/bifrost-http/handlers/realtime_client_secrets.go` (+ its test), `transports/bifrost-http/handlers/webrtc_realtime.go`. - `plugins/logging/main.go` + `plugins/logging/writer.go`: read `UserName` from context and thread it through `applyOutputFieldsToEntry` to the log entry. - `transports/bifrost-http/handlers/realtime_client_secrets.go` + `webrtc_realtime.go`: propagate `UserName` from the request context onto the bifrost context (and through the realtime relay context). - `framework/logstore/tables.go`: add nullable `UserName *string` to the `Log` model with a `varchar(255)` type. - `framework/logstore/migrations.go`: add `migrationAddUserNameColumn` — nullable column add, safe on Postgres (metadata only, no rewrite). ## Type of change - [ ] Bug fix - [x] Feature - [x] Refactor - [ ] Documentation - [ ] Chore/CI ## Affected areas - [x] Core (Go) - [x] Transports (HTTP) - [ ] Providers/Integrations - [x] Plugins - [ ] UI (React) - [ ] Docs ## How to test ```sh # Core/Transports go version go test ./... # Logstore migration smoke test: apply the migration on a pre-existing DB and # confirm the user_name column is present on the `logs` table. ``` Verify end-to-end: 1. Attach the `BifrostContextKeyUserName` value via the enterprise auth middleware (or `X-Bf-User-Id` / OAuth flow that sets user identity). 2. Issue a chat completion through a governed virtual key. 3. Inspect the resulting log row — `user_name` should be populated alongside the existing `user_id`. Realtime: 1. Mint a client secret via `/v1/realtime/client_secrets` with user-identity headers set and confirm the governance evaluation receives the consolidated `UserID` and the relay context carries both `UserID` and `UserName`. No new configs or environment variables are introduced. ## Screenshots/Recordings N/A — backend-only change. ## Breaking changes - [ ] Yes - [x] No The old `BifrostContextKeyGovernanceUserID` constant is removed, but it was only documented as "set by enterprise governance plugin — DO NOT SET THIS MANUALLY", so external callers should not be referencing it. Plugins and handlers in this repo are updated in-place. The `BifrostContextKeyUserID` string value changes from `"user_id"` to `"bifrost-user-id"`. Any external code that serialized this context key by its raw string must be updated. ## Related issues N/A ## Security considerations - The new `user_name` column stores a user-supplied display name on log rows. It inherits the same retention, access-control, and PII posture as the existing `user_id` column. - Context keys remain Bifrost-internal and are still not settable from request headers without explicit plugin/middleware cooperation. ## 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
Reworks the virtual-keys table and details sheet to reflect live usage when a
VK is managed by an access profile, and extracts the supporting rate-limit
display + number formatting into shared pieces that the dashboard model
rankings tab also adopts. When a VK is attached to users via an access
profile, the governance plugin tracks usage on the AP rather than the VK (to
avoid double-counting); the UI now mirrors that — polling the managing AP
every 5s so bars update without a full page refresh — and soft-locks the VK
edit form to name/description only for managed VKs.
The "is this VK AP-managed?" signal is resolved strictly: a VK is treated as
managed only when an access profile explicitly lists it in `virtual_key_ids`
(no fallback to first-active / first AP), so multi-AP setups can't
misattribute one profile's budgets to another's VKs. Row status, budget
cells, and rate-limit cells all derive exhaustion from that same source, so
the `Active` / `Exhausted` badge stays in lockstep with the bars next to it.
## Changes
Shared:
- `ui/lib/utils/governance.ts`: new `formatCompactNumber(n)` (e.g.
`10_000 → "10K"`, `1_500_000 → "1.5M"`) and `formatRateLimitLines(rl)`
helpers. `formatCompactNumber` is backed by `Intl.NumberFormat` compact
notation so trailing zeros are dropped (`10_000 → "10K"`, not `"10.0K"`)
and boundary values promote correctly (`999_950 → "1M"`, not `"1000.0K"`).
- `ui/components/rateLimitDisplay.tsx` (new): shared `<RateLimitDisplay>`
component with compact bars + tooltips, a `limitOnly` mode for template
entities (access profiles), and a `compact` prop for tight cells.
- `ui/app/workspace/dashboard/components/modelRankingsTab.tsx`: drop the
local `formatNumber` copy in favor of the shared `formatCompactNumber`.
OSS build support:
- `ui/app/_fallbacks/enterprise/lib/types/accessProfile.ts`,
`.../types/user.ts`,
`.../store/apis/accessProfileApi.ts`,
`.../store/apis/virtualKeyUsersApi.ts` (new): stub types + no-op hooks
that resolve the `@enterprise/*` imports used by `useVirtualKeyUsage`
when building OSS. The hooks return `{ data: undefined, isLoading:
false }` so in OSS the hook never finds a managing AP and transparently
falls through to the VK's own budgets / rate limits (identical behavior
to pre-PR OSS). Mirrors the existing `largePayload.ts` fallback pattern.
Virtual keys:
- `ui/app/workspace/virtual-keys/hooks/useVirtualKeyUsage.ts` (new): resolves
the managing access profile for a VK via `useGetVirtualKeyUsersQuery` +
`useGetUserAccessProfilesQuery` (polled every 5s). The managing profile
is matched strictly by `virtual_key_ids.includes(vk.id)`, and
`isManagedByProfile = managingProfile !== undefined` — a VK with
direct-attached users but no AP relation is not treated as AP-managed.
When profile-managed, `displayBudgets` / `displayRateLimit` mirror the AP's
values (including empty ones — an AP with no rate limits shows nothing,
instead of leaking the VK's own stale rate limits). When not
profile-managed, the hook returns the VK's own budgets / rate limits as
before. Also exposes `assignedUsers` and `isExhausted` (derived from the
same source) so consumers don't reimplement the logic.
- `ui/app/workspace/virtual-keys/views/virtualKeysTable.tsx`:
- Budget, rate-limit, and status cells all call `useVirtualKeyUsage` (RTK
Query dedupes the underlying request across the per-row hook call
sites) so row status and the bars beside it never disagree.
- New "Rate Limits" column.
- New `VKStatusBadge` component derives `Active` / `Exhausted` / `Inactive`
from the hook's `isExhausted`, so a managed key never shows "Active"
next to exhausted-looking bars.
- For managed VKs, the row's delete button is a disabled button wrapped in
a `<span>` with `cursor-not-allowed` and a tooltip (300ms delay) that
explains the profile must be detached first — instead of luring the user
into confirm-then-403.
- Delete-confirm `AlertDialog` Cancel / Confirm buttons carry
`data-testid="vk-delete-cancel-${vk.name}"` /
`data-testid="vk-delete-confirm-${vk.name}"` per the project's
`entity-element-qualifier` convention.
- `ui/app/workspace/virtual-keys/views/virtualKeyDetailsSheet.tsx`: new
`UsageLine` component with threshold-colored progress bars, managed-by
info alert that explicitly calls out name/description as still editable,
"Assigned To" section, and section headers that attribute budgets /
rate limits to `managingProfile.name` when managed. `Last reset {...}`
timestamps render only when the underlying `token_last_reset` /
`request_last_reset` field is populated — no placeholder "just now" when
the AP has never reset its counters.
- `ui/app/workspace/virtual-keys/views/virtualKeySheet.tsx`: detect
AP-managed VK on edit via the same `useVirtualKeyUsage` hook; if managed,
submit only name/description updates and wrap the rest of the form in a
disabled `<fieldset>` with a lock alert and assigned-users display. The
detection uses the hook's strict `isManagedByProfile` so a VK with
direct-attached (non-AP) users still gets the full edit form and its edits
aren't silently dropped by the restricted submit path.
## Type of change
- [ ] Bug fix
- [x] Feature
- [x] 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
```
Functional:
1. Create an access profile with budgets + rate limits and attach it to a
user. The user gets a VK minted through the profile.
2. Issue traffic against that VK and watch the virtual-keys table — the
budget and rate-limit cells should update roughly every 5s without
refreshing the page.
3. Open the VK details sheet: the info banner should mention
name/description remain editable, and the Budget/Rate Limits sections
should be annotated "(from {access profile name})".
4. Click the row's delete button — it should be disabled with a
not-allowed cursor and show a tooltip (after ~300ms hover).
5. Click "Edit" on the same VK — all fields except name/description should
be locked by a visible `<fieldset disabled>` with an accompanying alert.
6. For a non-managed VK, confirm table cells and the details sheet still
render the VK's own counters and the full edit form is unlocked.
7. Dashboard → Model Rankings: numbers like `10K`, `1.5M` still render
correctly (now from the shared `formatCompactNumber`).
## Screenshots/Recordings
UI change — screenshots/recordings to be attached in the PR description.
## Breaking changes
- [ ] Yes
- [x] No
## Related issues
N/A
## Security considerations
No auth, secret, or PII surface changes. The new hook only reads from
already-authorized RTK Query endpoints.
## 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 Surfaces user identity in the logs UI: the log detail view now prefers the display name over the raw user ID (falling back when no name is available) and the log filter popover gains a User ID input so users can drill down to a specific user's traffic. Pairs with the backend change that writes `user_name` onto log rows. ## Changes - `ui/lib/types/logs.ts`: add optional `user_name?: string` to `LogEntry` to match the new log column. - `ui/app/workspace/logs/sheets/logDetailView.tsx`: render `log.user_name || log.user_id` in the User field link, wrapped in a tooltip that shows the raw user ID when a name is present and a "Filter by user" hint otherwise. `font-mono` is applied only when the link renders the raw ID — human-readable names use the normal link style (matching the neighbouring Team / Customer / Business Unit identity fields). - `ui/components/filters/filterPopover.tsx`: add a User command group with a text input bound to the existing `user_ids` filter (single-value slot, matching how the same filter is already invoked from the log detail link). ## 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 ``` Functional: 1. Generate some log entries for a user whose `user_name` is populated (after the paired backend change). The log detail sheet should show the name as the primary label with the raw ID surfaced via tooltip. 2. Generate log entries for a user without a `user_name` — the detail sheet should fall back to the user ID as the label and show a "Filter by user" tooltip. 3. Open the log list filter popover → User section → type a user ID → the list should narrow to that user's entries. Clearing the input should remove the filter. 4. Clicking the user link inside the log detail sheet should still navigate to the logs list pre-filtered by that user ID. ## Screenshots/Recordings UI change — screenshots/recordings to be attached in the PR description. ## Breaking changes - [ ] Yes - [x] No `user_name` on `LogEntry` is optional; existing consumers that only look at `user_id` remain unaffected. ## Related issues N/A ## Security considerations No auth or secret surface changes. The filter uses the existing authorized logs endpoint; the display name rendered in the detail view is already stored server-side and served under the same access controls as the rest of the log row. ## 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 Bumps the AWS S3 SDK and related internal packages to their latest versions across all modules in the repository. ## Changes - `github.com/aws/aws-sdk-go-v2/service/s3` upgraded from `v1.94.0` → `v1.97.3` - `github.com/aws/aws-sdk-go-v2/internal/v4a` upgraded from `v1.4.16` → `v1.4.22` - `github.com/aws/aws-sdk-go-v2/service/internal/checksum` upgraded from `v1.9.7` → `v1.9.13` - `github.com/aws/aws-sdk-go-v2/service/internal/s3shared` upgraded from `v1.19.16` → `v1.19.21` ## Type of change - [ ] Bug fix - [ ] Feature - [ ] Refactor - [ ] Documentation - [x] Chore/CI ## Affected areas - [x] Core (Go) - [x] Transports (HTTP) - [ ] Providers/Integrations - [x] Plugins - [ ] UI (React) - [ ] Docs ## How to test ```sh go test ./... ``` ## Breaking changes - [ ] Yes - [x] No ## Related issues ## Security considerations Keeping AWS SDK dependencies up to date reduces exposure to any vulnerabilities patched in newer releases. ## 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
This reverts commit 0f6fa0b.
* fix: delete fallbacks from anthropic req (#2754)
## Summary
Remove the `fallbacks` field from Anthropic provider request bodies to ensure compatibility with the Anthropic API specification.
## Changes
- Added logic to delete the `fallbacks` field from JSON request bodies in the Anthropic provider's `getRequestBodyForResponses` function
- Implemented proper error handling for the field deletion operation with appropriate Bifrost error wrapping
## Type of change
- [x] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI
## Affected areas
- [ ] Core (Go)
- [ ] Transports (HTTP)
- [x] Providers/Integrations
- [ ] Plugins
- [ ] UI (Next.js)
- [ ] Docs
## How to test
Test Anthropic provider requests to ensure the `fallbacks` field is properly removed and requests succeed:
```sh
# Core/Transports
go version
go test ./...
# Test specific Anthropic provider functionality
go test ./core/providers/anthropic/...
```
Verify that requests to the Anthropic API no longer include the `fallbacks` field and complete successfully.
## Screenshots/Recordings
N/A
## Breaking changes
- [ ] Yes
- [x] No
## Related issues
N/A
## Security considerations
No security implications - this change only removes an unsupported field from API requests.
## 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
* fix: preserve context values in async requests (#2703)
## Summary
Refactors async job execution to pass the full BifrostContext instead of just the virtual key value, enabling proper context preservation for background operations including virtual keys, tracing headers, and other request metadata.
## Changes
- Modified `AsyncJobExecutor.SubmitJob()` to accept `*schemas.BifrostContext` instead of `*string` for virtual key
- Updated `executeJob()` to restore all original request context values in the background goroutine
- Added `getVirtualKeyFromContext()` helper function to extract virtual key from BifrostContext
- Updated all async handler methods to pass BifrostContext directly to `SubmitJob()`
- Removed redundant virtual key extraction logic from HTTP handlers
## Type of change
- [x] Refactor
## Affected areas
- [x] Core (Go)
- [x] Transports (HTTP)
## How to test
Verify async job execution preserves request context properly:
```sh
# Core/Transports
go version
go test ./...
# Test async endpoints with virtual keys and tracing headers
curl -X POST http://localhost:8080/v1/async/chat/completions \
-H "Authorization: Bearer vk_test_key" \
-H "X-Trace-Id: test-trace-123" \
-d '{"model": "gpt-3.5-turbo", "messages": [{"role": "user", "content": "Hello"}]}'
# Verify job execution maintains context
curl http://localhost:8080/v1/async/jobs/{job_id}
```
## Screenshots/Recordings
N/A
## Breaking changes
- [ ] Yes
- [x] No
## Related issues
N/A
## Security considerations
Improves security by ensuring proper context isolation and virtual key handling in async operations.
## 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
* [fix]: Gemini provider - handle content block tool outputs in Responses API path (#2692)
When function_call_output messages arrive via the Anthropic Responses API
format, their output is an array of content blocks (ResponsesFunctionToolCallOutputBlocks),
not a plain string (ResponsesToolCallOutputStr). The Gemini provider's
convertResponsesMessagesToGeminiContents only checked the string case,
silently dropping all tool result content and sending empty {} responses
to Gemini. This caused the model to loop endlessly retrying tool calls
it never saw results for.
Other providers (Bedrock, OpenAI, Cohere) already handle both output
formats. This aligns the Gemini provider with them.
Affected packages:
- core/providers/gemini/responses.go - Add ResponsesFunctionToolCallOutputBlocks handling
- core/providers/gemini/gemini_test.go - Add test for content block outputs
Co-authored-by: tom <tom@asteroid.ai>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Akshay Deo <akshay@akshaydeo.com>
* fix: gemini thinking level and finish reason round-trip preservation (#2697)
## Summary
Fixes two critical regressions in the Gemini provider's GenAI integration: preserves `thinkingLevel` parameters during round-trip conversions and ensures `MAX_TOKENS` finish reasons survive Bifrost transformations.
## Changes
- **Fixed thinking level preservation**: Modified `convertGenerationConfigToResponsesParameters()` to only set effort from `thinkingLevel` without deriving a `thinkingBudget`, preventing unwanted behavior changes in Gemini 3.x models
- **Enhanced finish reason handling**: Added bidirectional conversion between Gemini and Bifrost finish reasons, prioritizing `StopReason` over `IncompleteDetails` to preserve `MAX_TOKENS` finish reasons
- **Expanded finish reason support**: Added new Gemini finish reason constants for image generation, tool calls, and malformed responses
- **Improved response conversion**: Updated response conversion logic to properly handle error finish reasons and set appropriate status/error fields
## Type of change
- [x] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI
## Affected areas
- [ ] Core (Go)
- [ ] Transports (HTTP)
- [x] Providers/Integrations
- [ ] Plugins
- [ ] UI (Next.js)
- [ ] Docs
## How to test
Validate the thinking level and finish reason preservation:
```sh
# Run Gemini provider tests
go test ./core/providers/gemini/... -v
# Specifically test the regression fixes
go test ./core/providers/gemini/... -run "TestGenAIThinkingLevel_RoundTripPreservesLevelNotBudget|TestGenAIFinishReasonMaxTokens_PersistsThroughBifrostRoundTrip" -v
```
Test with actual Gemini API calls using thinking levels and verify that:
- `thinkingLevel` parameters are preserved without generating unwanted `thinkingBudget` values
- Responses with `MAX_TOKENS` finish reason maintain that status through the conversion pipeline
## Screenshots/Recordings
N/A
## Breaking changes
- [ ] Yes
- [x] No
## Related issues
Addresses regressions in GenAI path where thinking configuration and finish reasons were being incorrectly transformed during Bifrost conversions.
## Security considerations
No security implications - this change only affects internal data structure conversions and doesn't modify authentication, secrets handling, or data exposure.
## 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
* fix: remove cc user agent guard from streaming in anthropic (#2706)
## Summary
Fixes WebSearch tool argument handling for all clients by removing the Claude Code user agent restriction. Previously, only Claude Code clients received proper WebSearch query arguments in the streaming response, while other clients lost the query data due to skipped argument deltas.
## Changes
- Removed the `IsClaudeCodeRequest(ctx)` check that was restricting WebSearch argument sanitization and synthetic delta generation to only Claude Code clients
- WebSearch tool arguments are now sanitized and synthetic `input_json_delta` events are generated for all clients during `output_item.done` events
- Added comprehensive test coverage for the WebSearch tool flow including argument delta skipping, synthetic delta generation, and full end-to-end streaming scenarios
- Enhanced code comments to clarify the WebSearch tool handling logic
## Type of change
- [x] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI
## Affected areas
- [ ] Core (Go)
- [ ] Transports (HTTP)
- [x] Providers/Integrations
- [ ] Plugins
- [ ] UI (Next.js)
- [ ] Docs
## How to test
Validate the WebSearch tool behavior with the new test suite:
```sh
# Run the new WebSearch tests
go test ./core/providers/anthropic -run TestWebSearch -v
# Run all provider tests to ensure no regressions
go test ./core/providers/anthropic/...
# Full test suite
go test ./...
```
Test with different user agents to verify WebSearch queries are properly streamed to all clients, not just Claude Code.
## Screenshots/Recordings
N/A - This is a backend streaming API fix.
## Breaking changes
- [ ] Yes
- [x] No
This change expands functionality to previously broken clients without affecting existing working behavior.
## Related issues
Fixes WebSearch tool argument streaming for non-Claude Code clients.
## Security considerations
The change maintains existing argument sanitization for WebSearch tools while expanding it to all clients, preserving the same security posture.
## 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
* remove unnecessary marshalling of payload (#2770)
## Summary
Optimized JSON parsing in the Anthropic integration by replacing full JSON unmarshaling with targeted field extraction using gjson for retrieving the "type" field from streaming responses.
## Changes
- Replaced `sonic.Unmarshal()` with `gjson.Get()` to extract only the "type" field from Anthropic stream events
- Eliminated the need to unmarshal the entire JSON response into an `AnthropicStreamEvent` struct
- Improved performance by avoiding unnecessary JSON parsing overhead
## Type of change
- [x] Refactor
- [ ] Bug fix
- [ ] Feature
- [ ] Documentation
- [ ] Chore/CI
## Affected areas
- [ ] Core (Go)
- [x] Transports (HTTP)
- [x] Providers/Integrations
- [ ] Plugins
- [ ] UI (Next.js)
- [ ] Docs
## How to test
Test streaming responses from the Anthropic integration to ensure the type field is correctly extracted:
```sh
# Core/Transports
go version
go test ./...
# Test specifically the Anthropic integration
go test ./transports/bifrost-http/integrations/
```
## Screenshots/Recordings
N/A
## Breaking changes
- [x] No
- [ ] Yes
## Related issues
N/A
## Security considerations
No security implications - this is a performance optimization that maintains the same functionality.
## 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
* feat: claude opus 4.7 compatibility (#2773)
## Summary
Adds support for Claude Opus 4.7 model with specific parameter handling and reasoning configuration changes. Opus 4.7 rejects temperature, top_p, and top_k parameters and only supports adaptive thinking mode without budget tokens.
## Changes
- Added `IsOpus47()` function to detect Claude Opus 4.7 models
- Modified parameter handling to skip temperature, top_p, and top_k for Opus 4.7 models
- Updated reasoning configuration to use adaptive thinking only for Opus 4.7 (no budget_tokens)
- Added support for `display` parameter in thinking configuration to control output visibility
- Extended adaptive thinking support to include Sonnet 4.6 models
- Added task budget support with new beta header `task-budgets-2026-03-13`
- Updated effort mapping to handle Opus 4.7's "xhigh" effort level
- Added comprehensive test coverage for Opus 4.7 specific behaviors
- Fixed OpenAI responses to filter out Anthropic-specific summary:"none" parameter
## Type of change
- [x] Feature
- [x] Bug fix
## Affected areas
- [x] Core (Go)
- [x] Providers/Integrations
## How to test
Validate the changes with the following tests:
```sh
# Core/Transports
go version
go test ./core/providers/anthropic/...
# Specific test cases for Opus 4.7
go test -run TestToAnthropicChatRequest_Opus47 ./core/providers/anthropic/
go test -run TestSupportsAdaptiveThinking ./core/providers/anthropic/
go test -run TestAddMissingBetaHeadersToContext_TaskBudgets ./core/providers/anthropic/
```
Test with Claude Opus 4.7 model requests to ensure:
- Temperature, top_p, top_k parameters are stripped
- Reasoning uses adaptive thinking without budget_tokens
- Task budget beta headers are properly added
## Breaking changes
- [ ] Yes
- [x] No
The changes maintain backward compatibility while adding new model support.
## Security considerations
No security implications. Changes only affect parameter handling and model-specific configurations for Anthropic's Claude models.
## 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
* docs: restructure helm guide into comprehensive multi-page reference (#2776)
* docs: restructure helm guide into comprehensive multi-page reference (#2771)
## Summary
Restructures the Helm deployment documentation into a comprehensive multi-page guide with dedicated sections for each configuration area. The main Helm page now provides quickstart instructions for both OSS and Enterprise deployments, while detailed configuration is split into focused sub-pages.
## Changes
- **Restructured main Helm page**: Condensed from 740+ lines to 103 lines with clear quickstart tabs for OSS vs Enterprise
- **Added 8 new dedicated configuration pages**:
- `values.mdx` - Complete values reference with examples and common patterns
- `client.mdx` - Client configuration (pool size, logging, CORS, auth, compat shims)
- `providers.mdx` - Provider setup for all 23+ supported LLM providers with cloud-native auth
- `storage.mdx` - Storage backends (SQLite, PostgreSQL, object storage, vector stores)
- `plugins.mdx` - Plugin configuration (telemetry, logging, semantic cache, OTel, Datadog)
- `governance.mdx` - Governance setup (budgets, rate limits, virtual keys, routing rules)
- `cluster.mdx` - Multi-replica HA with gossip-based peer discovery
- `troubleshooting.mdx` - Common issues and diagnostic commands
- **Updated chart version**: Bumped from 1.5.0 to 2.1.0
- **Enhanced navigation**: Added nested Helm section in docs.json with proper icons and organization
## Type of change
- [x] Documentation
## Affected areas
- [x] Docs
## How to test
Navigate through the new Helm documentation structure:
1. Visit the main Helm page for quickstart instructions
2. Follow the quickstart for either OSS or Enterprise deployment
3. Use the sub-pages for detailed configuration of specific areas
4. Verify all internal links work correctly
5. Test the troubleshooting commands on a real deployment
The documentation now provides both quick-start paths and comprehensive reference material for production deployments.
## Screenshots/Recordings
N/A - Documentation changes only
## Breaking changes
- [ ] Yes
- [x] No
This is purely a documentation restructure with no functional changes to the Helm chart itself.
## Related issues
Improves Helm documentation organization and usability for both new users and production deployments.
## Security considerations
The new documentation emphasizes security best practices:
- Kubernetes Secrets for all sensitive values
- Cloud-native authentication (IRSA, Workload Identity, Managed Identity)
- Proper RBAC setup for cluster mode
- Compliance considerations (HIPAA, PCI) for content logging
## 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
* docs: update guardrails docs
* v1.4.23 cut (#2778)
## Summary
Release version 1.4.20-1.4.23 with bug fixes for provider integrations, streaming error handling, and migration test improvements. This release addresses critical issues in Gemini, Bedrock, and Anthropic providers while adding support for Claude Opus 4.7.
## Changes
- **Provider Fixes**: Fixed Gemini tool outputs handling, Bedrock streaming events, and image content preservation in tool results
- **Streaming Improvements**: Added proper error capture for Responses streaming API to prevent silent failures
- **Migration Tests**: Added support for v1.4.22 governance model pricing flex tier columns in both PostgreSQL and SQLite migration tests
- **Anthropic Enhancements**: Removed fallback fields from outgoing requests and added Claude Opus 4.7 compatibility
- **Framework Fixes**: Improved async context propagation and custom provider model validation
- **Plugin Updates**: Enhanced OTEL metrics and configuration defaults
## Type of change
- [x] Bug fix
- [x] Feature
- [ ] Refactor
- [ ] Documentation
- [x] Chore/CI
## Affected areas
- [x] Core (Go)
- [x] Transports (HTTP)
- [x] Providers/Integrations
- [x] Plugins
- [ ] UI (Next.js)
- [ ] Docs
## How to test
Validate the migration test changes and provider fixes:
```sh
# Test migration scripts
./.github/workflows/scripts/run-migration-tests.sh
# Core/Transports
go version
go test ./...
# Test provider integrations
go test ./transports/...
go test ./plugins/...
```
Test the new governance model pricing columns are properly handled in migration scenarios.
## Screenshots/Recordings
N/A
## Breaking changes
- [ ] Yes
- [x] No
## Related issues
Addresses multiple provider integration issues and streaming API error handling improvements.
## Security considerations
No security implications - changes are focused on bug fixes and migration test improvements.
## 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
* validator fix (#2780)
## Summary
Enhanced GitHub Actions security by transitioning from audit-only to strict network egress control using step-security/harden-runner. This change blocks all outbound network traffic by default and explicitly allows only required endpoints for each workflow.
## Changes
- Changed `egress-policy` from `audit` to `block` across all GitHub Actions workflows
- Added comprehensive `allowed-endpoints` lists for each job, specifying only the necessary external services
- Updated step names from "Harden the runner (Audit all outbound calls)" to "Harden Runner" for consistency
- Fixed schema validation script to use correct JSON paths for concurrency and SCIM configuration validation
- Reformatted JSON schema file for improved readability (whitespace and formatting changes only)
## Type of change
- [x] Chore/CI
## Affected areas
- [x] Core (Go)
- [x] Transports (HTTP)
- [x] Providers/Integrations
- [x] Plugins
- [x] UI (Next.js)
- [x] Docs
## How to test
Verify that all GitHub Actions workflows continue to function properly with the new network restrictions:
```sh
# Trigger workflows by pushing to a branch or creating a PR
git push origin feature-branch
# Monitor workflow runs in GitHub Actions tab to ensure:
# - All jobs complete successfully
# - No network connectivity errors occur
# - All required external services remain accessible
```
Key endpoints that should remain accessible include:
- GitHub API and release assets
- Package registries (npm, PyPI, Go modules)
- Docker registries
- Cloud storage services
- External APIs used by tests and integrations
## Screenshots/Recordings
N/A - Infrastructure/CI changes only
## Breaking changes
- [ ] Yes
- [x] No
## Related issues
N/A
## Security considerations
This change significantly improves security posture by:
- Preventing unauthorized outbound network connections from CI runners
- Creating an explicit allowlist of required external services
- Reducing attack surface for supply chain attacks
- Providing better visibility into network dependencies
The transition from audit to block mode ensures that any new network dependencies must be explicitly approved and documented.
## 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
* fix: token usage for vllm --skip-pipeline (#2784)
## Summary
Fixed token usage attribution for vLLM by treating empty-string content the same as nil content in streaming responses. vLLM sends `delta.content=""` (instead of `delta: null`) in finish_reason chunks, which was being forwarded and causing the synthesis chunk to lose its finish_reason, breaking usage attribution in logs and UI.
## Changes
- Modified streaming content handling to check for both nil and empty string content before processing chunks
- This prevents empty content deltas from being forwarded, ensuring finish_reason is preserved for proper token usage tracking
- Removed extraneous whitespace and formatting inconsistencies throughout the OpenAI provider code
## Type of change
- [x] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI
## Affected areas
- [x] Core (Go)
- [ ] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [ ] UI (Next.js)
- [ ] Docs
## How to test
Test with vLLM provider to ensure token usage is properly attributed:
```sh
# Core/Transports
go version
go test ./...
# Test streaming chat completion with vLLM
# Verify that finish_reason is preserved in final chunks
# Check that token usage appears correctly in logs/UI
```
## Screenshots/Recordings
N/A
## Breaking changes
- [ ] Yes
- [x] No
## Related issues
Fixes token usage tracking issues with vLLM provider.
## 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
* [fix]: OpenAI provider - flatten array-form tool_result output for Responses API (#2781) --skip-pipeline
When Anthropic tool_result blocks arrive with array-form content (the
standard shape for multi-turn tool exchanges), the OpenAI provider's
MarshalJSON emitted the output as a JSON array on the wire. The OpenAI
Responses API defines function_call_output.output as a string — strict
upstreams (Ollama Cloud, openai-go typed models) reject the array form
with HTTP 400.
Fix: before marshaling, collapse text-only
ResponsesFunctionToolCallOutputBlocks into a newline-joined string.
Non-text blocks (images, files) are left as-is. The schema type is
unchanged; the transformation lives in the OpenAI provider's outbound
marshaler only.
Closes #2779
Affected packages:
- core/providers/openai/types.go - Flatten text-only output blocks to string
- core/providers/openai/responses_marshal_test.go - Three regression tests
- core/changelog.md - Changelog entry
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: prevent send on closed channel panic in provider queue shutdown --skip-pipeline (#2725)
## Summary
Fixes a race condition in provider queue shutdown that caused "send on closed channel" panics in production. The issue occurred when producers passed the `isClosing()` check but then attempted to send to a queue that was closed before they reached the select statement.
## Changes
- **Removed queue channel closure**: Queue channels are never closed to prevent "send on closed channel" panics
- **Updated worker exit mechanism**: Workers now exit via the `done` channel signal instead of waiting for queue closure
- **Enhanced shutdown handling**: Workers drain remaining buffered requests and send shutdown errors when `done` is signaled
- **Added producer re-routing**: Stale producers can transparently re-route to new queues during `UpdateProvider`
- **Improved error handling**: Added rollback logic for failed provider updates with proper cleanup
- **Enhanced transfer logic**: Buffered requests are transferred before signaling shutdown to ensure they reach new workers
- **Added comprehensive tests**: Race condition demonstration and validation of the fix across multiple scenarios
## Type of change
- [x] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI
## Affected areas
- [x] Core (Go)
- [ ] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [ ] UI (Next.js)
- [ ] Docs
## How to test
Run the new race condition test to verify the fix:
```sh
go test -run TestProviderQueue_SendOnClosedChannel_Race ./core -v
```
Run the comprehensive provider lifecycle tests:
```sh
go test -run TestProviderQueue ./core -v
go test -run TestUpdateProvider ./core -v
go test -run TestRemoveProvider ./core -v
```
Run the full test suite to ensure no regressions:
```sh
go test ./...
```
## Screenshots/Recordings
N/A
## Breaking changes
- [ ] Yes
- [x] No
## Related issues
Fixes production panics related to concurrent provider queue operations during shutdown/updates.
## Security considerations
None - this is an internal concurrency fix that doesn't affect external interfaces or data handling.
## 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
* feat: preserve MCP tool annotations in bidirectional conversion --skip-pipeline (#2746)
## Summary
Adds support for preserving MCP tool annotations when converting between MCP tools and Bifrost schemas. This enables MCP servers to provide behavioral hints (read-only, destructive, idempotent, open-world) that help agents make better reasoning decisions about tool usage.
## Changes
- Added `MCPToolAnnotations` struct to capture MCP spec hints including title, read-only, destructive, idempotent, and open-world indicators
- Modified `convertMCPToolToBifrostSchema` to preserve MCP tool annotations when converting from MCP tools to Bifrost chat tools
- Updated `ChatToolFunction` to include optional annotations field
- Enhanced MCP server sync logic to map Bifrost annotations back to MCP tool annotations for bidirectional compatibility
## Type of change
- [x] Feature
- [ ] Bug fix
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI
## Affected areas
- [x] Core (Go)
- [x] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [ ] UI (Next.js)
- [ ] Docs
## How to test
Test with an MCP server that provides tool annotations to verify they are preserved through the conversion process:
```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 MCP tools with annotations maintain their behavioral hints when converted to Bifrost schemas and back.
## Screenshots/Recordings
N/A
## Breaking changes
- [ ] Yes
- [x] No
## Related issues
N/A
## Security considerations
No security implications - this change only preserves metadata hints that help with tool behavior classification.
## 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
* fix: add support for Anthropic structured output and response format (#1972)
* fix: add support for Anthropic structured output and response format conversion
* fix: refactor output configuration setting in ToBedrockResponsesRequest
* run go fmt on responses.go
* fix: streamline response format conversion for Anthropic models
* fix: enhance merging of additional model request fields and output configuration
* fix: remove koanf/maps dependency and replace its usage with internal merge function
* preserve order in output_config
* update type casting
* add non-anthropic test-case
* check for output_config first
* diversify anthropic output formats
* move bifrost ctx update
* guard tested field
* guard format.jsonschema
* test fixes --skip-pipeline (#2782)
## Summary
Updates test configurations to align with current API specifications and replaces deprecated utility function usage.
## Changes
- Replaced `schemas.Ptr("test")` with `new("test")` in Anthropic chat test for string pointer creation
- Updated MCP client configuration tests to use `sse` connection type instead of `websocket` with simplified `connection_string` field
- Modified HTTP MCP client config to use `connection_string` instead of nested `http_config` object
- Changed OpenTelemetry plugin tests to use `genai_extension` trace type instead of `otel`
## Type of change
- [x] Chore/CI
## Affected areas
- [x] Core (Go)
- [x] Transports (HTTP)
- [x] Providers/Integrations
## How to test
Validate that all tests pass with the updated configurations:
```sh
# Core/Transports
go version
go test ./...
```
## Screenshots/Recordings
N/A
## Breaking changes
- [ ] Yes
- [x] No
## Related issues
N/A
## Security considerations
No security implications - these are test configuration updates only.
## 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
* anthropic container changes --skip-pipeline (#2783)
## 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 (Next.js)
- [ ] 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
* core schema changes --skip-pipeline (#2787)
## Summary
Promotes Anthropic-native parameters to the neutral ChatParameters layer, enabling direct access to advanced Anthropic features like containers, MCP servers, task budgets, and enhanced tool configurations without requiring ExtraParams.
## Changes
- Added neutral fields to `ChatParameters` for Anthropic-specific features: `TopK`, `Speed`, `InferenceGeo`, `MCPServers`, `Container`, `CacheControl`, `TaskBudget`, and `ContextManagement`
- Enhanced `ChatTool` with Anthropic tool flags: `DeferLoading`, `AllowedCallers`, `InputExamples`, and `EagerInputStreaming`
- Added `Display` field to `ChatReasoning` for Anthropic adaptive thinking control
- Implemented `StripUnsupportedAnthropicFields` function to remove unsupported features based on provider capabilities
- Updated parameter mapping logic to prefer neutral fields over ExtraParams with fallback support
- Added comprehensive JSON marshaling/unmarshaling for union types like `ChatContainer`
The design maintains backward compatibility by falling back to ExtraParams when neutral fields are not set, while providing type-safe access to advanced Anthropic features.
## Type of change
- [x] Feature
- [ ] Bug fix
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI
## Affected areas
- [x] Core (Go)
- [ ] Transports (HTTP)
- [x] Providers/Integrations
- [ ] Plugins
- [ ] UI (Next.js)
- [ ] Docs
## How to test
Validate the new parameter handling and provider feature gating:
```sh
# Core/Transports
go version
go test ./...
# Test Anthropic provider parameter mapping
go test ./core/providers/anthropic/...
# Verify schema validation
go test ./core/schemas/...
```
Test with requests containing the new neutral fields to ensure proper mapping to Anthropic API format and appropriate stripping for unsupported providers.
## Screenshots/Recordings
N/A - Backend API changes only.
## Breaking changes
- [ ] Yes
- [x] No
This change is fully backward compatible. Existing ExtraParams usage continues to work, while new neutral fields provide enhanced type safety.
## Related issues
N/A
## Security considerations
The new MCP server configuration includes authorization tokens. Ensure proper handling of sensitive credentials in the `ChatMCPServer.AuthorizationToken` field.
## 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
* dependabot fixes --skip-pipeline (#2788)
## Summary
This PR adds the Hono web framework as a direct dependency to all MCP server examples and updates various dependencies across the project to their latest versions.
## Changes
- Added `hono@^4.12.14` as a direct dependency to all MCP server examples (edge-case-server, error-test-server, parallel-test-server, temperature, test-tools-server)
- Upgraded Hono from version 4.11.4 to 4.12.14 and changed it from a peer dependency to a direct dependency
- Updated Python dependencies including authlib (1.6.6 → 1.6.11), langchain-core (1.2.28 → 1.2.31), langchain-openai (1.1.4 → 1.1.14), langchain-text-splitters (1.1.0 → 1.1.2), langsmith (0.5.0 → 0.7.32), openai (2.13.0 → 2.32.0), and python-multipart (0.0.20 → 0.0.26)
- Updated TypeScript dependencies including langsmith (0.5.18 → 0.5.19) and added it as a direct dependency
- Added `github.com/tidwall/gjson v1.18.0` as a direct dependency in Go transports module
- Updated UI dependencies including dompurify (3.3.3 → 3.4.0) and follow-redirects (1.15.11 → 1.16.0) via package overrides
## Type of change
- [x] Chore/CI
## Affected areas
- [x] Core (Go)
- [x] Transports (HTTP)
- [x] Providers/Integrations
- [x] UI (Next.js)
## How to test
Validate that all dependencies are properly installed and examples still function:
```sh
# Test MCP server examples
cd examples/mcps/temperature
npm install
npm run build
# Test Go transports
cd transports
go mod tidy
go test ./...
# Test Python integrations
cd tests/integrations/python
uv sync
uv run python -m pytest
# Test TypeScript integrations
cd tests/integrations/typescript
npm install
npm test
# Test UI
cd ui
pnpm install
pnpm test
pnpm build
```
## Screenshots/Recordings
N/A - dependency updates only
## Breaking changes
- [ ] Yes
- [x] No
## Related issues
N/A
## Security considerations
The dependency updates include security patches, particularly for dompurify and follow-redirects which are explicitly overridden in the UI package.json for security reasons.
## 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
* move back go to 1.26.1 (#2792)
## Summary
Downgrade Go version from 1.26.2 to 1.26.1 across all GitHub Actions workflows, Go modules, and Docker images to address compatibility issues.
## Changes
- Downgraded Go version from 1.26.2 to 1.26.1 in all GitHub Actions workflows (e2e-tests, pr-tests, release-cli, release-pipeline, snyk)
- Updated go.mod files for core, CLI, examples, and test modules to use Go 1.26.1
- Updated Docker base images in transports/Dockerfile and transports/Dockerfile.local to use golang:1.26.1-alpine3.23
- Added stream cancellation safety improvements with guarded channel sends and finalizer protection to prevent goroutine leaks when clients disconnect
- Enhanced stream error checking with context cancellation support to properly drain upstream channels
## Type of change
- [x] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [x] Chore/CI
## Affected areas
- [x] Core (Go)
- [x] Transports (HTTP)
- [x] Providers/Integrations
- [ ] Plugins
- [ ] UI (Next.js)
- [ ] Docs
## How to test
Validate the Go version downgrade and streaming improvements:
```sh
# Verify Go version
go version
# Core/Transports
go test ./...
# Test streaming endpoints with client disconnection scenarios
# to verify proper cleanup and no goroutine leaks
```
## Screenshots/Recordings
N/A
## Breaking changes
- [ ] Yes
- [x] No
## Related issues
N/A
## Security considerations
The streaming improvements enhance resource cleanup and prevent potential goroutine leaks when clients disconnect unexpectedly, improving overall system stability.
## 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
* temp gotoolchain auto (#2809)
* temp hack for tests (#2810)
## Summary
The Go workspace setup script was not specifying a `go` directive or toolchain version, which caused `GOTOOLCHAIN=auto` to select a Go version lower than what `core@v1.4.19` requires. This adds an explicit `go 1.26.2` and `toolchain go1.26.2` directive to the workspace so the correct toolchain is used automatically.
## Changes
- Added `go work edit -go=1.26.2 -toolchain=go1.26.2` to `setup-go-workspace.sh` so that `GOTOOLCHAIN=auto` selects Go >= 1.26.2, satisfying the minimum version required by the published `core@v1.4.19` module referenced in `transports/go.mod`.
## Type of change
- [ ] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [x] Chore/CI
## Affected areas
- [ ] Core (Go)
- [x] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [ ] UI (Next.js)
- [ ] Docs
## How to test
```sh
# Verify the workspace is initialized with the correct Go version
bash .github/workflows/scripts/setup-go-workspace.sh
grep -E "^go |^toolchain" go.work
# Expected output:
# go 1.26.2
# toolchain go1.26.2
go test ./...
```
## 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
* temp block docker build (#2811)
## Summary
Temporarily disables the `test-docker-image-amd64` and `test-docker-image-arm64` CI jobs in the release pipeline by commenting them out.
## Changes
- Both Docker image test jobs (`test-docker-image-amd64` and `test-docker-image-arm64`) have been commented out rather than removed, preserving the full job definitions for easy re-enablement later.
## Type of change
- [ ] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [x] Chore/CI
## Affected areas
- [ ] Core (Go)
- [ ] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [ ] UI (Next.js)
- [ ] Docs
## How to test
No functional code changes. Verify the release pipeline runs without executing the Docker image test jobs.
## Breaking changes
- [ ] Yes
- [x] No
## Related issues
N/A
## Security considerations
No security implications.
## 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
* removed docker build steps (#2812)
## Summary
The `test-docker-image-amd64` and `test-docker-image-arm64` CI jobs have been removed from the release pipeline. These jobs were already commented out and non-functional, and all references to them as dependencies and gate conditions in downstream release jobs have been cleaned up.
## Changes
- Deleted the commented-out `test-docker-image-amd64` and `test-docker-image-arm64` job definitions from the release pipeline.
- Removed `test-docker-image-amd64` and `test-docker-image-arm64` from the `needs` arrays of `core-release`, `framework-release`, `plugins-release`, `bifrost-http-release`, the Docker build/push jobs, the manifest job, and the final notification job.
- Removed the corresponding result checks for those two jobs from all `if` conditions in the affected release jobs.
## Type of change
- [ ] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [x] Chore/CI
## Affected areas
- [ ] Core (Go)
- [ ] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [ ] UI (Next.js)
- [ ] Docs
## How to test
Trigger the release pipeline and confirm that all release jobs proceed without waiting on or referencing the removed Docker image test jobs.
## 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
* moves tests to 1.26.2 and 1.26.1 (#2813)
## Summary
Bumps the Go version used across all release pipeline jobs from `1.26.1` to `1.26.2` to keep the CI environment on the latest patch release.
## Changes
- Updated Go version from `1.26.1` to `1.26.2` in all `setup-go` steps within the release pipeline workflow.
## Type of change
- [ ] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [x] Chore/CI
## Affected areas
- [ ] Core (Go)
- [ ] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [ ] UI (Next.js)
- [ ] Docs
## How to test
The release pipeline will use the updated Go version on the next run. No additional manual steps are required beyond verifying the CI pipeline passes.
```sh
go version
go test ./...
```
## Screenshots/Recordings
N/A
## Breaking changes
- [ ] Yes
- [x] No
## Related issues
N/A
## Security considerations
Patch releases often include security and bug fixes. Staying on the latest patch version reduces exposure to known vulnerabilities in the Go toolchain.
## Checklist
- [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
* ocr test fixes (#2814)
## Summary
Adds an operation-allowed check for OCR requests before they are dispatched to a provider, and fixes the Mistral provider to return its custom provider name when one is configured.
## Changes
- Added a `CheckOperationAllowed` guard for `OCRRequest` in `handleProviderRequest`, consistent with how other request types are gated. If the operation is not permitted, a `BifrostError` is returned with the provider key, request type, and requested model populated.
- Updated `MistralProvider.GetProviderKey()` to use `providerUtils.GetProviderName` so that custom provider configurations are respected, rather than always returning the hardcoded `schemas.Mistral` value.
## Type of change
- [ ] Bug fix
- [x] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI
## Affected areas
- [x] Core (Go)
- [ ] Transports (HTTP)
- [x] Providers/Integrations
- [ ] Plugins
- [ ] UI (Next.js)
- [ ] Docs
## How to test
```sh
go version
go test ./...
```
- Configure a custom provider wrapping Mistral and verify that `GetProviderKey()` returns the custom provider name rather than `mistral`.
- Attempt an OCR request against a provider where the operation is not allowed and confirm a `BifrostError` is returned with the correct `Provider`, `RequestType`, and `ModelRequested` fields set.
- Attempt an OCR request against a provider where the operation is allowed and confirm the request proceeds normally.
## 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
* revert to old schema (#2815)
## Summary
This PR simplifies and consolidates the `config.schema.json` by removing several features, collapsing provider-specific schema variants, and restructuring key configuration definitions to reduce complexity and align with updated runtime semantics.
## Changes
- Removed the top-level `version` field that controlled allow-list semantics for empty arrays
- Removed the `compat` plugin configuration block (including `convert_text_to_chat`, `convert_chat_to_responses`, `should_drop_params`, `should_convert_params`)
- Replaced `compat` with a simpler `enable_litellm_fallbacks` boolean for Groq text completion fallbacks
- Removed `mcp_disable_auto_tool_inject` and `routing_chain_max_depth` from server config
- Collapsed `provider_with_ollama_config`, `provider_with_sgl_config`, and `provider_with_replicate_config` into the generic `provider` definition; removed their corresponding key types (`ollama_key`, `sgl_key`, `replicate_key`) and `network_config_without_base_url`
- Removed providers `nebius`, `xai`, and `runway` from the providers block
- Moved `calendar_aligned` from `virtual_key` to the `budget` object; removed `virtual_key_id` and `provider_config_id` from budget in favor of a standalone `budget_id` reference on virtual keys
- Removed `chain_rule` from routing rules and relaxed the `scope_id` conditional requirement
- Simplified `virtual_key_provider_config` to inline key definitions with full provider-specific key configs (Azure, Vertex, Bedrock, VLLM), replacing the separate `key_ids` and `keys` split
- Removed `mcp_client_name` and `allow_on_all_virtual_keys` from MCP configs; removed `allowed_extra_headers` and `disable_auto_tool_inject` from MCP client config
- Added `websocket` as a supported MCP connection type with a dedicated `websocket_config` block; removed `inprocess` connection type
- Removed `per_user_oauth` as an MCP auth type and dropped the conditional `oauth_config_id` requirement
- Renamed `concurrency_and_buffer_size` to `concurrency_config`; renamed `retry_backoff_initial`/`retry_backoff_max` to `retry_backoff_initial_ms`/`retry_backoff_max_ms`; removed `enforce_http2` and `openai_config` from network config
- Moved `pricing_overrides` from the top-level config into individual provider definitions
- Simplified `provider_pricing_override` schema, removing scoped fields (`scope_kind`, `virtual_key_id`, `provider_id`, `provider_key_id`) and replacing `pattern` with `model_pattern`; added `regex` as a valid `match_type`; expanded supported `request_types`
- Renamed `scim_config` to `saml_config` in the top-level schema
- Removed `apiToken` from Okta config and made `clientSecret` optional; updated required fields to only `issuerUrl` and `clientId`
- Removed `object_storage` and `retention_days` from the logs store config
- Removed `id` and `description` fields from provider config entries in the `provider_configs` array
- Removed `websocket_responses` and `realtime` from `custom_provider_config` allowed requests; removed the enum constraint on `base_provider_type`
- Removed `disable_auto_tool_inject` from `mcp_client_config` VFS settings
- Added `deployments` mapping to `azure_key_config` and `vertex_key_config`
- Updated `otel` plugin `trace_type` to only accept `"otel"` (removed `genai_extension`, `vercel`, `open_inference`)
- Removed `prompts` from the built-in plugin name list
- Removed `builtin` as a valid plugin `placement` value
- Changed `cluster_config` discovery `dial_timeout` from a Go duration string to an integer (nanoseconds)
- Reformatted many inline `required` arrays to multi-line style for readability
## Type of change
- [ ] Bug fix
- [ ] Feature
- [x] Refactor
- [ ] Documentation
- [ ] Chore/CI
## Affected areas
- [ ] Core (Go)
- [x] Transports (HTTP)
- [x] Providers/Integrations
- [x] Plugins
- [ ] UI (Next.js)
- [ ] Docs
## How to test
Validate existing configs against the updated schema to confirm they parse correctly. Verify that configs using removed fields (`version`, `compat`, `mcp_disable_auto_tool_inject`, `chain_rule`, etc.) are rejected by the schema validator.
```sh
go test ./...
```
Confirm that provider configs for Ollama, SGL, and Replicate continue to work using the generic `provider` definition. Confirm MCP clients using `websocket` connection type validate correctly with a `websocket_config` block.
## Breaking changes
- [x] Yes
- [ ] No
The following fields have been removed and configs using them will fail schema validation:
- `version` (top-level)
- `compat` block under server config
- `mcp_disable_auto_tool_inject` and `routing_chain_max_depth` under server config
- `chain_rule` on routing rules
- `calendar_aligned` on virtual keys (now on budgets)
- `virtual_key_id` / `provider_config_id` on budgets
- `apiToken` on Okta config (now optional `clientSecret` only)
- `object_storage` and `retention_days` on logs store
- `id`, `description` on provider config entries
- `allow_on_all_virtual_keys`, `allowed_extra_headers`, `disable_auto_tool_inject` on MCP client config
- `inprocess` MCP connection type and `per_user_oauth` auth type
- `enforce_http2` and `openai_config` from network config
- `builtin` plugin placement value; `prompts` built-in plugin name
- `nebius`, `xai`, `runway` provider entries
Migrate by removing or replacing these fields according to the updated schema definitions.
## Related issues
## Security considerations
Removal of `per_user_oauth` as an MCP auth type should be reviewed to ensure no active integrations depend on it. The relaxed `scope_id` requirement on routing rules should be validated to confirm it does not inadvertently broaden access scope.
## 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
* reduced release pipeline for this cut for go downgrade (#2816)
## Summary
This PR removes all test jobs from the release pipeline and decouples them from the release gate conditions, allowing releases to proceed without waiting for (often flaky) provider API test results. It also significantly expands and restructures `config.schema.json` to reflect new features, provider support, and breaking semantic changes introduced in v1.5.0.
## Changes
- **Release pipeline**: Removed `test-core`, `approve-flaky-test-core`, `test-framework`, `test-plugins`, `test-bifrost-http`, `test-migrations`, and `test-e2e-ui` jobs entirely from `release-pipeline.yml`. All release jobs (`core-release`, `framework-release`, `plugins-release`, `bifrost-http-prep`, `docker-build-amd64`, `docker-build-arm64`, `push-mintlify-changelog`) now depend only on change detection and upstream release jobs, not on test outcomes.
- **Schema: deny-by-default semantics (v1.5.0)**: Empty arrays in `provider_configs`, `mcp_configs`, `allowed_models`, `key_ids`, and `tools_to_execute` now mean "deny all" rather than "allow all". Use `["*"]` to allow all. A top-level `version` field (enum `1` or `2`, default `2`) controls which semantic applies, with `1` restoring v1.4.x behavior.
- **Schema: new providers**: Added `nebius`, `xai`, and `runway` as first-class provider entries.
- **Schema: provider key restructuring**: Replaced the inline key object definition in `virtual_key_provider_config` with a flat `key_ids` string array. Introduced dedicated key types `ollama_key`, `sgl_key`, and `replicate_key` with their own `_key_config` blocks. Removed `deployments` from `azure_key_config` and `vertex_key_config` (replaced by `aliases` on `base_key`). Added `aliases` to `base_key` for model-to-deployment/inference-profile mappings.
- **Schema: provider variants**: `ollama` and `sgl` now reference `provider_with_ollama_config` and `provider_with_sgl_config` respectively, which use `network_config_without_base_url` (URL is per-key). `replicate` references `provider_with_replicate_config`. Added `openai_config` def with `disable_store` for the Responses API. Renamed `concurrency_config` to `concurrency_and_buffer_size`.
- **Schema: network config**: Split `network_config` into `network_config` (with `base_url`) and `network_config_without_base_url`. Added `enforce_http2`, `stream_idle_timeout_in_seconds`, `max_conns_per_host`, `beta_header_overrides`, and `ca_cert_pem` fields. Renamed `retry_backoff_initial_ms`/`retry_backoff_max_ms` to `retry_backoff_initial`/`retry_backoff_max`.
- **Schema: MCP changes**: Removed `websocket` connection type; added `inprocess`. Added `per_user_oauth` auth type. Added `mcp_client_name` for config-file resolution. Added `allowed_extra_headers` and `allow_on_all_virtual_keys` to `mcp_client_config`. Added `disable_auto_tool_inject` to MCP plugin config. Added global `mcp_disable_auto_tool_inject` and `routing_chain_max_depth` to server config.
- **Schema: routing rules**: Added `chain_rule` boolean to `routing_rule`. Made `scope_id` required (non-null string) when `scope` is `team`, `customer`, or `virtual_key`.
- **Schema: budgets**: Moved `calendar_aligned` from the budget object to the virtual key level. Replaced `budget_id` on virtual key with `virtual_key_id`/`provider_config_id` on the budget object itself. Removed `budget_id` from `virtual_key_provider_config`.
- **Schema: logs store**: Added `object_storage` (S3/GCS) and `retention_days` to the logs store config.
- **Schema: pricing overrides**: Moved `pricing_overrides` from per-provider to a top-level array with scoped `provider_pricing_override` objects supporting `scope_kind`, `virtual_key_id`, `provider_id`, `provider_key_id`, `match_type`, `pattern`, `request_types`, and `pricing_patch`.
- **Schema: compat plugin**: Replaced `enable_litellm_fallbacks` with a structured `compat` object supporting `convert_text_to_chat`, `convert_chat_to_responses`, `should_drop_params`, and `should_convert_params`.
- **Schema: OTEL plugin**: Expanded `trace_type` enum to `genai_extension`, `vercel`, `open_inference` (was only `otel`).
- **Schema: SCIM**: Renamed `saml_config` to `scim_config`. Added `apiToken` to `okta_config` and made `clientSecret` and `apiToken` required. Changed cluster `dial_timeout` from integer (nanoseconds) to Go duration string.
- **Schema: misc**: Added `prompts` and `builtin` to plugin name/placement enums. Added `provider_configs` fields `id`, `description`, `network_config`, `proxy_config`, `custom_provider_config`, `concurrency_and_buffer_size`, and `openai_config`. Added `scim_config` top-level ref. Normalized multi-item `required` arrays to single-line format throughout.
## Type of change
- [ ] Bug fix
- [x] Feature
- [x] Refactor
- [ ] Documentation
- [x] Chore/CI
## Affected areas
- [x] Core (Go)
- [x] Transports (HTTP)
- [x] Providers/Integrations
- [x] Plugins
- [ ] UI (Next.js)
- [ ] Docs
## How to test
```sh
# Validate schema against existing configs
npx ajv validate -s transports/config.schema.json -d your-config.json
# Verify release pipeline runs without test gate
# Push a tagged commit and confirm release jobs trigger directly after detect-changes
```
If upgrading from v1.4.x, set `"version": 1` in your config to preserve allow-all semantics for empty arrays, or migrate empty arrays to `["*"]` and adopt v2 deny-by-default semantics.
## Breaking changes
- [x] Yes
- [ ] No
**Empty arrays in `allowed_models`, `key_ids`, `tools_to_execute`, `provider_configs`, and `mcp_configs` now deny all access by default (v2 semantics).** To allow all, use `["*"]`. To restore v1.4.x behavior, set `"version": 1` at the top level of your config.
`enable_litellm_fallbacks` has been removed; replace with the `compat` object. `saml_config` has been renamed to `scim_config`. `budget_id` has been removed from virtual keys and `virtual_key_provider_config`. `calendar_aligned` has moved from the budget object to the virtual key. `deployments` has been removed from `azure_key_config` and `vertex_key_config`; use `aliases` on the key instead. `retry_backoff_initial_ms`/`retry_backoff_max_ms` renamed to `retry_backoff_initial`/`retry_backoff_max`. The `websocket` MCP connection type has been removed; use `http` or `sse`. Okta SCIM config now requires `clientSecret` and `apiToken`.
## Related issues
N/A
## Security considerations
The `insecure_skip_verify` and `ca_cert_pem` fields on `network_config` expose TLS bypass options; these should only be used in controlled environments. The `per_user_oauth` auth type for MCP introduces per-user credential flows that require careful OAuth config management. Removal of test gates from the release pipeline means regressions from flaky provider APIs will no longer block releases, but also means real failures could ship if not caught by other means.
## 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
* force verstion back to go 1.26.1 (#2817)
## Summary
Bumps `core` to v1.4.21 and updates `transports` to depend on `core` v1.4.20, while removing a now-unnecessary workspace Go directive workaround that was previously required to satisfy the toolchain constraint introduced by `core` v1.4.19.
## Changes
- Incremented `core` version from `1.4.20` to `1.4.21`
- Updated `transports/go.mod` to reference `core` v1.4.20 (previously v1.4.19)
- Removed the `go work edit -go=1.26.2 -toolchain=go1.26.2` workaround from the workspace setup script, which was only needed to satisfy the toolchain requirement imposed by the published `core` v1.4.19
## Type of change
- [ ] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [x] Chore/CI
## Affected areas
- [x] Core (Go)
- [x] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [ ] UI (Next.js)
- [ ] Docs
## How to test
```sh
go work sync
go test ./...
```
Verify the workspace initializes without the explicit Go/toolchain directive and that all modules resolve correctly.
## 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
* revert everything to go1.26.1 (#2818)
## Summary
Bumps the core version to `1.4.22` and rolls back dependency versions across the framework, plugins, and transports to align with a prior stable set of releases. This resolves a version inconsistency introduced by forward-referencing newer module versions that were not yet intended to be consumed by downstream packages.
## Changes
- Incremented `core/version` from `1.4.21` to `1.4.22`
- Downgraded `bifrost/core` from `v1.4.19` → `v1.4.17` across `framework`, `governance`, `jsonparser`, `litellmcompat`, `logging`, `maxim`, `mocker`, `otel`, `semanticcache`, and `telemetry` plugins
- Downgraded `bifrost/framework` from `v1.2.38` → `v1.2.36` (or `v1.2.35` for `semanticcache`) across all dependent plugins
- Downgraded `bifrost/core` in `transports` from `v1.4.20` → `v1.4.19`
- Downgraded all plugin versions referenced in `transports` (governance, litellmcompat, logging, maxim, otel, semanticcache, telemetry) to their corresponding prior releases
- Downgraded `go.opentelemetry.io/otel/sdk` and `go.opentelemetry.io/otel/sdk/metric` from `v1.43.0` → `v1.40.0` in affected plugins
- Bumped Go toolchain version in `transports/go.mod` from `1.26.1` to `1.26.2`
## Type of change
- [ ] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [x] Chore/CI
## Affected areas
- [x] Core (Go)
- [x] Transports (HTTP)
- [ ] Providers/Integrations
- [x] Plugins
- [ ] UI (Next.js)
- [ ] Docs
## How to test
```sh
go test ./...
```
Verify that all modules resolve correctly with the pinned dependency versions and that no import errors occur during build.
## Breaking changes
- [ ] Yes
- [x] No
## Related issues
N/A
## Security considerations
None. These are internal module version adjustments with no changes to auth, secrets, or data 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
* bumped up hello-world dep (#2819)
## Summary
Pins the `bifrost/core` dependency in the example plugin modules to a consistent released version (`v1.4.17`), removing a local `replace` directive that was pointing to the local `core` module path.
## Changes
- Replaced the local `replace` directive in `hello-world-wasm-go/go.mod` with a direct reference to `github.com/maximhq/bifrost/core v1.4.17`
- Downgraded `hello-world/go.mod` from `v1.4.19` to `v1.4.17` to align both example plugins on the same released version
## Type of change
- [ ] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [x] Chore/CI
## Affected areas
- [ ] Core (Go)
- [ ] Transports (HTTP)
- [ ] Providers/Integrations
- [x] Plugins
- [ ] UI (Next.js)
- [ ] Docs
## How to test
```sh
cd examples/plugins/hello-world-wasm-go
go mod tidy
go build ./...
cd examples/plugins/hello-world
go mod tidy
go build ./...
```
## Breaking changes
- [ ] Yes
- [x] No
## Related issues
## Security considerations
No security implications. This change only affects dependency resolution for example plugin modules.
## 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
* framework: bump core to v1.4.22 --skip-pipeline
* plugins/governance: bump core to v1.4.22 and framework to v1.2.39 --skip-pipeline
* plugins/jsonparser: bump core to v1.4.22 and framework to v1.2.39 --skip-pipeline
* plugins/litellmcompat: bump core to v1.4.22 and framework to v1.2.39 --skip-pipeline
* plugins/logging: bump core to v1.4.22 and framework to v1.2.39 --skip-pipeline
* plugins/maxim: bump core to v1.4.22 and framework to v1.2.39 --skip-pipeline
* plugins/mocker: bump core to v1.4.22 and framework to v1.2.39 --skip-pipeline
* plugins/otel: bump core to v1.4.22 and framework to v1.2.39 --skip-pipeline
* plugins/semanticcache: bump core to v1.4.22 and framework to v1.2.39 --skip-pipeline
* plugins/telemetry: bump core to v1.4.22 and framework to v1.2.39 --skip-pipeline
* enforce go 1.26.1 (#2820)
* transports: update dependencies --skip-pipeline
* Adds changelog for v1.4.23 --skip-pipeline
* V1.5.0 (#2245)
* refactor: standardize empty array conventions for VK Provider & MCP Configs, and makes Provider Config weight optional for routing (#1932)
## Summary
Changes Virtual Key provider and MCP configurations from "allow-all by default" to "deny-by-default" security model. Virtual Keys now require explicit provider and MCP client configurations to allow access, improving security posture.
## Changes
- **Provider Configs**: Empty `provider_configs` now blocks all providers instead of allowing all
- **MCP Configs**: Empty `mcp_configs` now blocks all MCP tools instead of allowing all
- **Weight Field**: Changed provider `weight` from required `float64` to optional `*float64` - null weight excludes provider from weighted routin…
Summary
Briefly explain the purpose of this PR and the problem it solves.
Changes
Type of change
Affected areas
How to test
Describe the steps to validate this change. Include commands and expected outcomes.
If adding new configs or environment variables, document them here.
Screenshots/Recordings
If UI changes, add before/after screenshots or short clips.
Breaking changes
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
docs/contributing/README.mdand followed the guidelines