diff --git a/core/changelog.md b/core/changelog.md index e69de29bb2d..4d430b099b9 100644 --- a/core/changelog.md +++ b/core/changelog.md @@ -0,0 +1,7 @@ +- fix: idle timeout panic in the streaming idle-timeout reader +- fix: short-circuit `IdleTimeoutReader` reads when the connection is already closed (#3672) +- fix: preserve tool call stop reason in Anthropic streaming fallback (#3640) (thanks [@dicnunz](https://github.com/dicnunz)!) +- fix: correct start-time setting for accurate TTFT metric value (#3668) +- fix: map Vertex traffic type to Bifrost service tier (#3662) +- fix: ListModels for keyless providers (#3655) +- fix: remove manual `type: custom` for Anthropic tools (#3652) diff --git a/docs/enterprise/guardrails.mdx b/docs/enterprise/guardrails.mdx index 222e753e512..1c868875848 100644 --- a/docs/enterprise/guardrails.mdx +++ b/docs/enterprise/guardrails.mdx @@ -155,7 +155,7 @@ Guardrail Rules are custom policies that define when and how content validation | `cel_expression` | string | Yes | CEL expression for rule evaluation | | `apply_to` | enum | Yes | When to apply: `input`, `output`, or `both` | | `sampling_rate` | integer | No | Percentage of requests to evaluate (0-100) | -| `timeout` | integer | No | Execution timeout in milliseconds | +| `timeout` | integer | No | Execution timeout in seconds (default: 60) | | `provider_config_ids` | array | No | IDs of profiles to use for evaluation | ### Creating Rules @@ -185,7 +185,7 @@ Guardrail Rules are custom policies that define when and how content validation - `both` - Validate both inputs and outputs - **CEL Expression**: Define the validation logic - **Sampling Rate**: Set percentage of requests to evaluate (default: 100%) -- **Timeout**: Set maximum execution time in milliseconds +- **Timeout**: Set maximum execution time in seconds (default: 60) 3. **Link Profiles** - Select one or more profiles to use for evaluation diff --git a/framework/changelog.md b/framework/changelog.md index e69de29bb2d..c5ae6b3bb2f 100644 --- a/framework/changelog.md +++ b/framework/changelog.md @@ -0,0 +1,5 @@ +- feat: `created_by` user attribution column for virtual keys (#3672) +- feat: `blacklisted_models` column for virtual key provider configs (#3653) +- fix: add monotonic `inc_number` log cursor so node usage reconciliation does not skip late async log writes (#3664) +- revert: `access_profile_id` direct access profile assignment on virtual keys (#3669) +- chore: drop the `access_profile_id` column from `governance_virtual_keys` (#3670) diff --git a/helm-charts/bifrost/values.yaml b/helm-charts/bifrost/values.yaml index 2a04817fc27..1009c51aede 100644 --- a/helm-charts/bifrost/values.yaml +++ b/helm-charts/bifrost/values.yaml @@ -725,13 +725,14 @@ bifrost: # cel_expression: "!contains(request.body, 'SSN')" # apply_to: "input" # sampling_rate: 100 - # timeout: 1000 + # timeout: 60 # Timeout in seconds for rule execution (default: 60) # max_turns_to_send: 0 providers: [] # - id: 1 # provider_name: "bedrock" # policy_name: "content-filter" # enabled: true + # timeout: 30 # Timeout in seconds for provider execution (default: 30) # config: {} # Access profiles (enterprise): seed RBAC access profile templates from Helm. diff --git a/plugins/compat/changelog.md b/plugins/compat/changelog.md index e69de29bb2d..82ae1ff69a1 100644 --- a/plugins/compat/changelog.md +++ b/plugins/compat/changelog.md @@ -0,0 +1 @@ +- chore: upgraded core to v1.5.12 and framework to v1.3.12 diff --git a/plugins/governance/changelog.md b/plugins/governance/changelog.md index e69de29bb2d..1b1dafa9b9a 100644 --- a/plugins/governance/changelog.md +++ b/plugins/governance/changelog.md @@ -0,0 +1,2 @@ +- feat: virtual key blocked-models enforcement — reject requests when the requested model is blocked at the VK provider-config level (#3653) +- fix: clear stale `governanceRejectedContextKey` on an allow decision so successful fallback retries count toward budgets and rate limits (#3645) diff --git a/plugins/jsonparser/changelog.md b/plugins/jsonparser/changelog.md index e69de29bb2d..82ae1ff69a1 100644 --- a/plugins/jsonparser/changelog.md +++ b/plugins/jsonparser/changelog.md @@ -0,0 +1 @@ +- chore: upgraded core to v1.5.12 and framework to v1.3.12 diff --git a/plugins/logging/changelog.md b/plugins/logging/changelog.md index e69de29bb2d..c38e9977c4e 100644 --- a/plugins/logging/changelog.md +++ b/plugins/logging/changelog.md @@ -0,0 +1 @@ +- feat: stamp MCP tool logs with governance ownership (user, team, customer, and business unit IDs) from the request context diff --git a/plugins/maxim/changelog.md b/plugins/maxim/changelog.md index e69de29bb2d..82ae1ff69a1 100644 --- a/plugins/maxim/changelog.md +++ b/plugins/maxim/changelog.md @@ -0,0 +1 @@ +- chore: upgraded core to v1.5.12 and framework to v1.3.12 diff --git a/plugins/mocker/changelog.md b/plugins/mocker/changelog.md index e69de29bb2d..82ae1ff69a1 100644 --- a/plugins/mocker/changelog.md +++ b/plugins/mocker/changelog.md @@ -0,0 +1 @@ +- chore: upgraded core to v1.5.12 and framework to v1.3.12 diff --git a/plugins/otel/changelog.md b/plugins/otel/changelog.md index e69de29bb2d..82ae1ff69a1 100644 --- a/plugins/otel/changelog.md +++ b/plugins/otel/changelog.md @@ -0,0 +1 @@ +- chore: upgraded core to v1.5.12 and framework to v1.3.12 diff --git a/plugins/prompts/changelog.md b/plugins/prompts/changelog.md index e69de29bb2d..82ae1ff69a1 100644 --- a/plugins/prompts/changelog.md +++ b/plugins/prompts/changelog.md @@ -0,0 +1 @@ +- chore: upgraded core to v1.5.12 and framework to v1.3.12 diff --git a/plugins/semanticcache/changelog.md b/plugins/semanticcache/changelog.md index e69de29bb2d..82ae1ff69a1 100644 --- a/plugins/semanticcache/changelog.md +++ b/plugins/semanticcache/changelog.md @@ -0,0 +1 @@ +- chore: upgraded core to v1.5.12 and framework to v1.3.12 diff --git a/plugins/telemetry/changelog.md b/plugins/telemetry/changelog.md index e69de29bb2d..82ae1ff69a1 100644 --- a/plugins/telemetry/changelog.md +++ b/plugins/telemetry/changelog.md @@ -0,0 +1 @@ +- chore: upgraded core to v1.5.12 and framework to v1.3.12 diff --git a/transports/changelog.md b/transports/changelog.md index e69de29bb2d..5aa94e82e99 100644 --- a/transports/changelog.md +++ b/transports/changelog.md @@ -0,0 +1,22 @@ +## ✨ Features + +- **Virtual Key Blocked Models** — Block specific models at the virtual key provider-config level; blocked models take priority over allowed models and are enforced by governance (#3653) +- **Virtual Key Ownership** — Virtual keys now capture and display a `created_by` user attribution (#3672) +- **MCP Log Attribution** — MCP tool logs are stamped with user, team, customer, and business unit IDs so MCP usage can be traced like LLM usage +- **Team & Business Unit Filters** — Added team and business unit filters across the dashboard and logs views (#3650) +- **Sticky Time Filters** — Time filter selections are preserved when navigating between sidebar items (#3647) + +## 🐞 Fixed + +- **Idle Timeout Panic** — Fixed a panic in the streaming idle-timeout reader and added a guard to skip reads once the connection is closed (#3672) +- **Anthropic Streaming** — Preserve the tool-call stop reason in the Anthropic streaming fallback (#3640) (thanks [@dicnunz](https://github.com/dicnunz)!) +- **TTFT Metric** — Fixed the request start-time setting so the time-to-first-token metric is accurate (#3668) +- **Vertex Service Tier** — Map the Vertex traffic type to the correct Bifrost service tier (#3662) +- **Keyless Providers** — Fixed `ListModels` for providers configured without an API key (#3655) +- **Anthropic Tools** — Stopped forcing `type: custom` on Anthropic tool definitions (#3652) +- **Node Usage Reconciliation** — Added a monotonic log cursor so reconciliation no longer skips late async log writes (#3664) +- **Fallback Budget Tracking** — Clear the stale governance rejection flag on allow so successful fallback retries count toward budgets and rate limits (#3645) +- **Virtual Keys Table** — Table now fills available height with a sticky header and scrollable body (#3676) +- **Sheet Layout** — Removed save/cancel icons and fixed sheet layout growth in routing rule and virtual key sheets (#3675) +- **Toast Click-Through** — Toasts remain clickable above modal overlays (#3674) +- **Direct Access Control** — Reverted the virtual key `access_profile_id` direct access profile assignment shipped in v1.5.3; the `access_profile_id` column has been dropped (#3669, #3670) diff --git a/ui/app/workspace/virtual-keys/views/virtualKeyDetailsSheet.tsx b/ui/app/workspace/virtual-keys/views/virtualKeyDetailsSheet.tsx index 9418e173434..796a6fe3cb2 100644 --- a/ui/app/workspace/virtual-keys/views/virtualKeyDetailsSheet.tsx +++ b/ui/app/workspace/virtual-keys/views/virtualKeyDetailsSheet.tsx @@ -22,7 +22,11 @@ import { ProviderLabels, ProviderName } from "@/lib/constants/logs"; import { VirtualKey } from "@/lib/types/governance"; import { cn } from "@/lib/utils"; import { supportsCalendarAlignment } from "@/lib/constants/governance"; -import { calculateUsagePercentage, formatCurrency, parseResetPeriod } from "@/lib/utils/governance"; +import { + calculateUsagePercentage, + formatCurrency, + parseResetPeriod, +} from "@/lib/utils/governance"; import ManagedVirtualKeyNotice from "@enterprise/components/access-profiles/managedVirtualKeyNotice"; import { formatDistanceToNow } from "date-fns"; import { Users } from "lucide-react"; @@ -49,12 +53,17 @@ function UsageLine({
- {format(current)} / {format(max)} + {format(current)} /{" "} + {format(max)} 80 ? "text-amber-500" : "text-muted-foreground", + exhausted + ? "text-red-500" + : pct > 80 + ? "text-amber-500" + : "text-muted-foreground", )} > {pct}% @@ -62,7 +71,10 @@ function UsageLine({
); @@ -73,7 +85,10 @@ interface VirtualKeyDetailSheetProps { onClose: () => void; } -export default function VirtualKeyDetailSheet({ virtualKey, onClose }: VirtualKeyDetailSheetProps) { +export default function VirtualKeyDetailSheet({ + virtualKey, + onClose, +}: VirtualKeyDetailSheetProps) { const { assignedUsers, isManagedByProfile, @@ -101,10 +116,12 @@ export default function VirtualKeyDetailSheet({ virtualKey, onClose }: VirtualKe // Rate limits exhausted (displayRateLimit?.token_current_usage && displayRateLimit?.token_max_limit && - displayRateLimit.token_current_usage >= displayRateLimit.token_max_limit) || + displayRateLimit.token_current_usage >= + displayRateLimit.token_max_limit) || (displayRateLimit?.request_current_usage && displayRateLimit?.request_max_limit && - displayRateLimit.request_current_usage >= displayRateLimit.request_max_limit); + displayRateLimit.request_current_usage >= + displayRateLimit.request_max_limit); return ( @@ -112,7 +129,8 @@ export default function VirtualKeyDetailSheet({ virtualKey, onClose }: VirtualKe {virtualKey.name} - {virtualKey.description || "Virtual key details and usage information"} + {virtualKey.description || + "Virtual key details and usage information"} @@ -141,10 +159,18 @@ export default function VirtualKeyDetailSheet({ virtualKey, onClose }: VirtualKe
- {virtualKey.is_active ? (isExhausted ? "Exhausted" : "Active") : "Inactive"} + {virtualKey.is_active + ? isExhausted + ? "Exhausted" + : "Active" + : "Inactive"}
@@ -159,7 +185,9 @@ export default function VirtualKeyDetailSheet({ virtualKey, onClose }: VirtualKe
- Last Updated + + Last Updated +
{formatDistanceToNow(new Date(virtualKey.updated_at), { addSuffix: true, @@ -169,9 +197,15 @@ export default function VirtualKeyDetailSheet({ virtualKey, onClose }: VirtualKe {entityInfo.type !== "None" && (
- Assigned To + + Assigned To +
- + {entityInfo.type} {entityInfo.name} @@ -188,14 +222,18 @@ export default function VirtualKeyDetailSheet({ virtualKey, onClose }: VirtualKe

Provider Configurations

- {!virtualKey.provider_configs || virtualKey.provider_configs.length === 0 ? ( + {!virtualKey.provider_configs || + virtualKey.provider_configs.length === 0 ? ( No providers configured (deny-by-default) ) : (
{virtualKey.provider_configs.map((config, index) => ( -
+
{/* Provider Header */}
@@ -205,7 +243,8 @@ export default function VirtualKeyDetailSheet({ virtualKey, onClose }: VirtualKe className="h-5 w-5" /> - {ProviderLabels[config.provider as ProviderName] || config.provider} + {ProviderLabels[config.provider as ProviderName] || + config.provider}
@@ -224,10 +263,15 @@ export default function VirtualKeyDetailSheet({ virtualKey, onClose }: VirtualKe All Models - ) : config.allowed_models && config.allowed_models.length > 0 ? ( + ) : config.allowed_models && + config.allowed_models.length > 0 ? (
{config.allowed_models.map((model) => ( - + {model} ))} @@ -253,7 +297,11 @@ export default function VirtualKeyDetailSheet({ virtualKey, onClose }: VirtualKe config.blacklisted_models.length > 0 ? (
{config.blacklisted_models.map((model) => ( - + {model} ))} @@ -278,7 +326,11 @@ export default function VirtualKeyDetailSheet({ virtualKey, onClose }: VirtualKe ) : config.keys && config.keys.length > 0 ? (
{config.keys.map((key) => ( - + {key.name} ))} @@ -296,7 +348,9 @@ export default function VirtualKeyDetailSheet({ virtualKey, onClose }: VirtualKe <>
-

Provider Budgets

+

+ Provider Budgets +

{config.budgets.map((b, bIdx) => (
- Resets {parseResetPeriod(b.reset_duration)} + Resets{" "} + {parseResetPeriod(b.reset_duration)} {virtualKey.calendar_aligned && - supportsCalendarAlignment(b.reset_duration) && + supportsCalendarAlignment( + b.reset_duration, + ) && " (calendar)"} {b.last_reset ? ( Last reset{" "} - {formatDistanceToNow(new Date(b.last_reset), { - addSuffix: true, - })} + {formatDistanceToNow( + new Date(b.last_reset), + { + addSuffix: true, + }, + )} ) : null}
@@ -331,7 +391,9 @@ export default function VirtualKeyDetailSheet({ virtualKey, onClose }: VirtualKe <>
-

Provider Rate Limits

+

+ Provider Rate Limits +

{/* Token Limits */} {config.rate_limit.token_max_limit != null ? ( @@ -340,7 +402,9 @@ export default function VirtualKeyDetailSheet({ virtualKey, onClose }: VirtualKe TOKEN LIMITS n.toLocaleString()} /> @@ -348,11 +412,13 @@ export default function VirtualKeyDetailSheet({ virtualKey, onClose }: VirtualKe Resets{" "} {parseResetPeriod( - config.rate_limit.token_reset_duration || "", + config.rate_limit + .token_reset_duration || "", )} {virtualKey.calendar_aligned && supportsCalendarAlignment( - config.rate_limit.token_reset_duration || "", + config.rate_limit + .token_reset_duration || "", ) && " (calendar)"} @@ -360,7 +426,9 @@ export default function VirtualKeyDetailSheet({ virtualKey, onClose }: VirtualKe Last reset{" "} {formatDistanceToNow( - new Date(config.rate_limit.token_last_reset), + new Date( + config.rate_limit.token_last_reset, + ), { addSuffix: true }, )} @@ -376,7 +444,9 @@ export default function VirtualKeyDetailSheet({ virtualKey, onClose }: VirtualKe REQUEST LIMITS n.toLocaleString()} /> @@ -384,11 +454,13 @@ export default function VirtualKeyDetailSheet({ virtualKey, onClose }: VirtualKe Resets{" "} {parseResetPeriod( - config.rate_limit.request_reset_duration || "", + config.rate_limit + .request_reset_duration || "", )} {virtualKey.calendar_aligned && supportsCalendarAlignment( - config.rate_limit.request_reset_duration || "", + config.rate_limit + .request_reset_duration || "", ) && " (calendar)"} @@ -396,7 +468,10 @@ export default function VirtualKeyDetailSheet({ virtualKey, onClose }: VirtualKe Last reset{" "} {formatDistanceToNow( - new Date(config.rate_limit.request_last_reset), + new Date( + config.rate_limit + .request_last_reset, + ), { addSuffix: true }, )} @@ -427,7 +502,8 @@ export default function VirtualKeyDetailSheet({ virtualKey, onClose }: VirtualKe

MCP Client Configurations

- {!virtualKey.mcp_configs || virtualKey.mcp_configs.length === 0 ? ( + {!virtualKey.mcp_configs || + virtualKey.mcp_configs.length === 0 ? ( No MCP clients configured (deny-by-default) @@ -442,17 +518,26 @@ export default function VirtualKeyDetailSheet({ virtualKey, onClose }: VirtualKe {virtualKey.mcp_configs.map((config, index) => ( - - {config.mcp_client?.name || "Unknown Client"} + + + {config.mcp_client?.name || "Unknown Client"} + {config.tools_to_execute?.includes("*") ? ( All Tools - ) : config.tools_to_execute && config.tools_to_execute.length > 0 ? ( + ) : config.tools_to_execute && + config.tools_to_execute.length > 0 ? (
{config.tools_to_execute.map((tool) => ( - + {tool} ))} @@ -514,7 +599,9 @@ export default function VirtualKeyDetailSheet({ virtualKey, onClose }: VirtualKe ))}
) : ( -

No budget limits configured

+

+ No budget limits configured +

)}
@@ -542,17 +629,25 @@ export default function VirtualKeyDetailSheet({ virtualKey, onClose }: VirtualKe />
- Resets {parseResetPeriod(displayRateLimit.token_reset_duration || "")} + Resets{" "} + {parseResetPeriod( + displayRateLimit.token_reset_duration || "", + )} {virtualKey.calendar_aligned && - supportsCalendarAlignment(displayRateLimit.token_reset_duration || "") && + supportsCalendarAlignment( + displayRateLimit.token_reset_duration || "", + ) && " (calendar)"} {displayRateLimit.token_last_reset ? ( Last reset{" "} - {formatDistanceToNow(new Date(displayRateLimit.token_last_reset), { - addSuffix: true, - })} + {formatDistanceToNow( + new Date(displayRateLimit.token_last_reset), + { + addSuffix: true, + }, + )} ) : null}
@@ -570,7 +665,10 @@ export default function VirtualKeyDetailSheet({ virtualKey, onClose }: VirtualKe />
- Resets {parseResetPeriod(displayRateLimit.request_reset_duration || "")} + Resets{" "} + {parseResetPeriod( + displayRateLimit.request_reset_duration || "", + )} {virtualKey.calendar_aligned && supportsCalendarAlignment( displayRateLimit.request_reset_duration || "", @@ -580,9 +678,12 @@ export default function VirtualKeyDetailSheet({ virtualKey, onClose }: VirtualKe {displayRateLimit.request_last_reset ? ( Last reset{" "} - {formatDistanceToNow(new Date(displayRateLimit.request_last_reset), { - addSuffix: true, - })} + {formatDistanceToNow( + new Date(displayRateLimit.request_last_reset), + { + addSuffix: true, + }, + )} ) : null}
@@ -591,15 +692,19 @@ export default function VirtualKeyDetailSheet({ virtualKey, onClose }: VirtualKe {displayRateLimit.token_max_limit == null && displayRateLimit.request_max_limit == null && ( -

No rate limits configured

+

+ No rate limits configured +

)}
) : ( -

No rate limits configured

+

+ No rate limits configured +

)}
); -} \ No newline at end of file +}