[oss] feat(governance): add owner FK columns, UsageObserver hook, and Each* iterators for alerting - #3819
[oss] feat(governance): add owner FK columns, UsageObserver hook, and Each* iterators for alerting#3819SahilChoudhary22 wants to merge 43 commits into
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis PR records which entity (virtual key, team, customer, or provider-config) owns each rate-limit/budget: schema columns and validation, migration and backfill, store load/backfill and iteration APIs, in-memory stamping on create/update, handler stamping for provider-configs, and an optional usage-observer hook. ChangesOwner scope tracking for rate-limits and budgets
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 4❌ Failed checks (4 warnings)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
|
tejas ghatte seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account. You have signed the CLA already but the status is still pending? Let us recheck it. |
aca7125 to
370d962
Compare
8c3e42e to
b95e8e7
Compare
370d962 to
db64a81
Compare
There was a problem hiding this comment.
Actionable comments posted: 25
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.rpiv/artifacts/designs/2026-05-25_11-26-45_alerting-fixes.md:
- Around line 392-393: Pick one canonical location for the
decryptString/idempotency change and make both the file map and the architecture
docs point to it: either keep the implementation in decryptString within the
encryption.go module (function decryptString) or move/alias it to
alertchannel.go, then update the other reference to match. Ensure the
architecture section references the exact symbol name decryptString and the file
listed in the diff (stores/configstore/tables/encryption.go or
stores/configstore/tables/alertchannel.go) so reviewers verify the same
location, and search/update any other mentions to avoid drift.
- Around line 114-125: The doc signature for EachRequestLimit is missing
ownerScopeType and ownerScopeID but the LocalGovernanceStore implementation
expects them; update the design doc (and any other documented signatures at the
noted locations) so the callback signature becomes fn(rateLimitID string,
ownerScopeType string, ownerScopeID string, requestCurrentUsage, requestMaxLimit
int64), and ensure the implementation in LocalGovernanceStore (the
EachRequestLimit method that iterates gs.rateLimits.Range and calls fn) passes
the owner scope values when invoking fn to match the documented contract.
In @.rpiv/artifacts/plans/2026-05-25_15-56-04_alerting-fixes.md:
- Around line 57-65: The plan currently documents EachRequestLimit as a 3-arg
callback but the real API on LocalGovernanceStore now passes owner/scope as
additional arguments; update the declaration and all usage examples to match the
current method signature (e.g., change fn func(rateLimitID string,
requestCurrentUsage, requestMaxLimit int64) to include owner/scope as the first
parameter, such as fn func(owner string, rateLimitID string,
requestCurrentUsage, requestMaxLimit int64)), and update every consumption
example that calls EachRequestLimit to accept and forward the owner parameter
(also update the similar snippets at the other occurrence mentioned).
In @.rpiv/artifacts/reviews/2026-05-25T14_30_working.md:
- Line 4: Remove the developer-local absolute path string
"/Users/sahilchoudhary/..." from the committed review artifact file
".rpiv/artifacts/reviews/2026-05-25T14_30_working.md"; replace it with a
repo-relative path or omit the path entirely so no machine-specific identifiers
are stored, then update any associated metadata entries in that same file to use
relative paths or sanitized placeholders.
In @.rpiv/artifacts/reviews/alerting-channels-stack-review.md:
- Around line 334-341: The review report contains contradictory gates: the
header shows "APPROVED FOR MERGE" and "Must-Fix: None" while the body lists
pre-release changes (thread safety, logging, timeout) — choose a single
governance outcome and make the report consistent by either (A) removing the
"Must-Fix: None" / the pre-release list and keeping "APPROVED FOR MERGE", or (B)
changing the header to a non-approved status (e.g., "APPROVED WITH REQUIRED
CHANGES" or "CHANGES REQUIRED") and keep the Should-Fix list; update the
corresponding sections that reference these lines (the headings "APPROVED FOR
MERGE", "Must-Fix: None", and the three Should-Fix items) so they consistently
reflect the chosen gate.
In `@bifrost-enterprise/ui/assets/apis--bk1jnj5.js`:
- Line 1: The file creates a new API slice named p via createApi
(reducerPath:`baseApi`) but the hooks are from the imported shared API instance
i.injectEndpoints, causing the wrong reducer (p.reducer) to be registered and
hooks to use a different cache; fix by exporting and registering the same RTK
Query slice used to generate the hooks (i) instead of the locally created
p—i.e., replace uses/exports of p.reducer and any reference to the locally
created createApi instance with the imported shared API instance i (reducer and
reducerPath) so the injected endpoints and hooks operate on the same slice.
In `@bifrost-enterprise/ui/assets/baseApi-DpxxZDYn.js`:
- Line 1: The prepareHeaders function awaits ma() but ma() currently always
returns null, so Authorization is never set; update ma() to actually load the
bearer token (e.g., read stored token from localStorage/sessionStorage or call
the existing dr wrapper) and return it as a Promise<string|null>, ensure
prepareHeaders continues to set Authorization when ma() yields a token, and keep
ha() clearing the same storage key (`bifrost-auth-token`) so logout removes the
stored token; modify the implementations of ma, prepareHeaders (the
prepareHeaders passed into Zr), and ha to use the same storage key and retrieval
method.
In `@bifrost-enterprise/ui/assets/button-7R416S74.js`:
- Line 1: The isLoading branch in function c() currently replaces children with
just the spinner and leaves the control interactive and unlabeled; change c()
and l() so that when isLoading is true we: 1) disable the native button path by
passing disabled true into l() (or when asChild is true set
aria-disabled="true"), 2) add aria-busy="true" to the rendered control, and 3)
preserve the original accessible label by rendering the spinner visually
alongside a screen-reader-only element containing the original children (so
assistive tech still reads the button text while the spinner displays). Ensure
these changes are applied where c() calls the loader component a and where l()
renders the actual element so both native and asChild usage get consistent
behavior.
In `@bifrost-enterprise/ui/assets/chartUtils-c3p7y9jb.js`:
- Line 1: The Session filter component W currently sets autoFocus: true on the
parent_request_id input which forcibly moves focus; remove the unconditional
autoFocus prop in function W (the input element inside W that uses
a.{value:e.parent_request_id||``, onChange:..., placeholder:`Parent request ID`,
... , autoFocus:!0}) so the input no longer receives focus automatically; if you
need conditional focus only apply autoFocus based on an explicit prop (e.g.,
defaultOpen or a new shouldAutoFocus flag) and wire that through W's parameters
instead of hardcoding true.
In `@bifrost-enterprise/ui/assets/ClientOnly-BTJWKFby.js`:
- Line 1: The R function currently substitutes required-but-missing params with
the string "undefined"; instead, detect missing required params inside R (use
the existing isMissingParams flag and usedParams map) and do not append a
placeholder when a required param is missing — return the interpolated result
with isMissingParams=true (or throw) so callers like buildLocation can surface
the error; specifically remove the `?? "undefined"` fallback for the required
param branch in R (the substring handled where d===1) and ensure L()/R populate
usedParams and isMissingParams consistently so no literal "undefined" segment is
produced.
In `@bifrost-enterprise/ui/assets/columnPinning-D-ejkIeA.js`:
- Line 1: The pinned-offset Map computed in function r(...) only runs when the
pinned id lists (a,o) or ref object change, so header width changes aren't
detected; fix by adding a ResizeObserver inside the useLayoutEffect in r to
observe every element in e.current (the headerCellRefs from i()), and on any
resize callback recompute the offsets exactly as the existing effect body does
and call the state setter (the second element returned from useState in r) with
the new Map; ensure the observer is disconnected in the effect cleanup and also
add a fallback window 'resize' listener for browsers without ResizeObserver.
In `@bifrost-enterprise/ui/assets/combobox-CmP7fdup.js`:
- Line 1: The trigger currently renders nested <button> elements (see functions
v and T) which is invalid; remove nested buttons from inside the main trigger
(the element rendered by s with "data-testid":"combobox-trigger-button" /
role="combobox") by relocating the clear/remove buttons
("data-testid":"combobox-clear-button", "combobox-remove-<value>",
"combobox-select-clear-button") to be sibling elements outside that trigger
container (or alternatively render the trigger as a non-button container instead
of s when those actions must remain inside), and ensure onClick handlers still
call the same handlers (e.g., f(null), e.onValueChange) and stopPropagation as
needed so focus and accessibility are preserved.
In `@bifrost-enterprise/ui/assets/context-DRdo5A2P-CkYLoPA2.js`:
- Around line 1-2: The u function hand-escapes only a few characters in query
parameter names which leaves % / spaces / non-ASCII unencoded; update u (and its
use of o/d) to percent-encode keys via encodeURIComponent (instead of the manual
replace chain) before joining with the value serialization (d) so keys
round-trip correctly; locate function u and replace its key encoding with
encodeURIComponent(n) while preserving the existing call to d(r) for values.
In `@bifrost-enterprise/ui/assets/devProfiler-hT4stDpR.js`:
- Line 1: The profiler polling gate N currently uses m() and l but ignores the
minimized visibility state e, so the queries g(...) and h(...) keep polling when
the panel is minimized; change the N calculation to include e (e.g. N = m() &&
!l && e) and keep using N for pollingInterval/skip in the g and h calls so
polling stops when e is false (reference V component, the N variable, and the
g(...) and h(...) query calls).
In `@bifrost-enterprise/ui/assets/dialog-B_DACXYn.js`:
- Line 1: The dialog content component function _ currently sets
onInteractOutside based on disableOutsideClick but then spreads ...o which can
contain another onInteractOutside that overrides the guard; modify _ so you
extract onInteractOutside from the incoming props (e.g. const {
onInteractOutside: userOnInteractOutside, ...rest } = o or similar), then create
a composed handler that, when disableOutsideClick is true, e.preventDefault()
and still optionally calls userOnInteractOutside (or when false just calls
userOnInteractOutside), and finally pass that composed handler as
onInteractOutside while spreading the rest props (rest) to ensure callers cannot
bypass the guard.
In `@bifrost-enterprise/ui/assets/dist-CW1kZn9_.js`:
- Line 1: The date picker component ir currently hard-codes id="date" on the
trigger button which causes duplicate DOM ids; update ir to accept a prop (e.g.,
triggerId or id) and use that if provided, otherwise generate a stable unique id
with React's useId hook, then replace the literal id:`date` passed to the l
component with the resolved id; reference the ir function and the l usage (the
JSX call with id:`date`) when making the change and ensure the prop is forwarded
in the component signature and usages.
In `@bifrost-enterprise/ui/assets/dist-Ddf49gE6.js`:
- Line 1: The effect is skipping re-subscribe because O() calls T() which
deep-compares arbitrary objects (like options.document/ShadowRoot) that have no
enumerable keys; change the memoization so host/root objects are compared by
reference instead of deep-equality: update T() (or O()) to detect non-plain host
objects (e.g., instances of Document/ShadowRoot/Node or objects with no
enumerable keys) and return reference equality for those cases, so j()'s O(f)
will re-run when the document/root reference changes.
In `@bifrost-enterprise/ui/assets/dist-Dw_XTMqb.js`:
- Line 1: The visibility hook ee incorrectly reads document.hidden during render
(causing SSR crashes) and removes the listener from window even though it was
added to document; update ee to (1) initialize state only when document exists
(guard with typeof document/window) to avoid reading document.hidden on the
server, (2) only add the visibilitychange listener when document is available,
and (3) remove the same listener from document in the cleanup (use
document.removeEventListener rather than window.removeEventListener) so handlers
are not leaked on remount.
In `@bifrost-enterprise/ui/assets/esm-CHpVij2M.css`:
- Line 1: The CSS uses uppercase keyword values that Stylelint rejects; change
pointer-events:visibleStroke to pointer-events:visiblestroke and
fill:currentColor to fill:currentcolor. Update the rules for the
.react-flow__edge selector (pointer-events value) and the
.react-flow__controls-button svg rule (fill value), replacing those keyword
values with their lowercase equivalents everywhere in the file.
In `@bifrost-enterprise/ui/assets/form-BGXMe1jq.js`:
- Line 1: The context providers p and g are initialized with empty objects and h
(useFormField) dereferences e.name before checking for presence, so the guard
never fires; change the initializers to u.createContext(null) for both p and g
and update h() to check that e is non-null before accessing e.name (i.e., move
or add the if(!e) throw before using e.name and any other property), ensuring
consumers outside <FormField> hit the invariant; update any related type
annotations if required.
In `@bifrost-enterprise/ui/assets/fullPageLoader-Bim9Nvk5.js`:
- Line 1: The spinner-only component r (exported as t) lacks accessibility
semantics; update the root div returned by r to include a status role and live
region (e.g., role="status" and aria-live="polite" or aria-busy) and add
visually hidden text (e.g., "Loading…" or "Page is loading") inside the div
alongside the <t> spinner so screen readers announce the loading state; ensure
the hidden text uses a CSS utility class or aria-hidden appropriately so it’s
announced but not visible.
In `@bifrost-enterprise/ui/assets/governance-BkdJ4q2P.js`:
- Line 1: The duration validator function h (exported as r) currently tests
suffixes with /[dwMY]$/ which excludes minute and hour values; update the regex
in h to include 'm' and 'h' (for example use /[mhdwMY]$/) so values like "1m"
and "1h" pass validation—ensure you modify the pattern inside the h function and
run any related tests that use the m array and g object for allowed options.
In `@bifrost-enterprise/ui/assets/highlighted-body-B3W2YXNL-CQfIigjz.js`:
- Line 1: The effect in the HighlightedCodeBlockBody component (function c)
calls d.highlight(...) and directly calls the state setter p from its callback,
allowing stale async results to overwrite newer renders or update after unmount;
update the useEffect to add a cancel guard or request token: create a local
boolean (or requestId) captured by the callback, set it to true in the cleanup,
and only call p(result) if not cancelled and the token matches; also ensure the
cleanup marks cancelled to prevent setState after unmount and use the
token/check around any synchronous return path (r&&p(r)) as well as the async
callback.
In `@bifrost-enterprise/ui/assets/icons-DSJc63AE.js`:
- Line 1: The gemini icon uses hard-coded gradient IDs (e.g.,
lobe-icons-gemini-fill-0/1/2) causing DOM collisions; update the gemini
component to generate a per-instance uniqueId (e.g., React's useId or a small
uid helper) and append it to every id and matching url(#...) reference inside
the SVG so the defs and their usages remain paired and unique across instances;
change the gemini renderer (function/component named gemini) to accept or create
the uniqueId, interpolate it into each id and corresponding fill="url(#...)" or
other url(...) attributes, and ensure any exported icon mapping that returns
gemini forwards the id if applicable.
In `@bifrost-enterprise/ui/assets/Matches-BswdqR0x.js`:
- Line 1: The navigate function calls blockerFn with nextLocation set to
this.latestLocation, hiding the real destination; change navigate (the code that
computes t via this.buildLocation and earlier assigns n/r) to compute the built
target location (call this.buildLocation(...) result) and pass that object as
nextLocation to each blockerFn instead of this.latestLocation so blockers
receive the true destination; update the blocker invocation site in navigate to
use the built location variable (the result returned from buildLocation) when
calling blockerFn.
🪄 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: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: b7443f43-0225-43a3-8e17-c68f223a5c82
📒 Files selected for processing (300)
.rpiv/artifacts/designs/2026-05-25_11-26-45_alerting-fixes.md.rpiv/artifacts/plans/2026-05-25_15-56-04_alerting-fixes.md.rpiv/artifacts/reviews/2026-05-25T14_30_working.md.rpiv/artifacts/reviews/alerting-channels-stack-review.mdbifrost-enterprise/ui/assets/AreaChart-BiqeIl8D.jsbifrost-enterprise/ui/assets/CartesianChart-BPagt4PR.jsbifrost-enterprise/ui/assets/ClientOnly-BTJWKFby.jsbifrost-enterprise/ui/assets/Combination-BSurmwHn.jsbifrost-enterprise/ui/assets/GithubLogo.es-BlCJsZEy.jsbifrost-enterprise/ui/assets/Matches-BswdqR0x.jsbifrost-enterprise/ui/assets/accessProfileSheet-DA_oxYYk.jsbifrost-enterprise/ui/assets/accordion-Bk01CsTU.jsbifrost-enterprise/ui/assets/activity-DslTkDcf.jsbifrost-enterprise/ui/assets/alert-g_iAczPV.jsbifrost-enterprise/ui/assets/alertDialog-Br7DYh_F.jsbifrost-enterprise/ui/assets/apis--bk1jnj5.jsbifrost-enterprise/ui/assets/array-BklbqEql.jsbifrost-enterprise/ui/assets/arrow-up-DeD5hCsV.jsbifrost-enterprise/ui/assets/arrow-up-down-BLl4wyzu.jsbifrost-enterprise/ui/assets/arrow-up-right-96iRHoI6.jsbifrost-enterprise/ui/assets/asyncMultiselect-CtCfPBz9.jsbifrost-enterprise/ui/assets/badge-DqqtOHKt.jsbifrost-enterprise/ui/assets/baseApi-DpxxZDYn.jsbifrost-enterprise/ui/assets/bash-BmVK7wHU.jsbifrost-enterprise/ui/assets/building-2-DqeGRc4k.jsbifrost-enterprise/ui/assets/businessUnitsApi-lkY1a5W9.jsbifrost-enterprise/ui/assets/button-7R416S74.jsbifrost-enterprise/ui/assets/calendar-BAeJhmIB.jsbifrost-enterprise/ui/assets/card-BsDcw87R.jsbifrost-enterprise/ui/assets/celRuleBuilder-Cha4gzZm.jsbifrost-enterprise/ui/assets/celRuleBuilder-Dck-Mqh8.cssbifrost-enterprise/ui/assets/chartUtils-c3p7y9jb.jsbifrost-enterprise/ui/assets/check-B2hQurL0.jsbifrost-enterprise/ui/assets/checkbox-BjSOeXyt.jsbifrost-enterprise/ui/assets/chevron-down-CtJZVQo7.jsbifrost-enterprise/ui/assets/chevron-left-C_D8M-jT.jsbifrost-enterprise/ui/assets/chevron-right-DEBCDsrR.jsbifrost-enterprise/ui/assets/chevron-up-CcwkPMdU.jsbifrost-enterprise/ui/assets/chunk-DECur_0Z.jsbifrost-enterprise/ui/assets/chunk-RLXIAIE6-DOEKdA2j.jsbifrost-enterprise/ui/assets/circle-alert-9rFJVGLO.jsbifrost-enterprise/ui/assets/circle-check-big-z7b4iVTg.jsbifrost-enterprise/ui/assets/circle-check-tICMUhdv.jsbifrost-enterprise/ui/assets/circle-question-mark-BAtYsBU4.jsbifrost-enterprise/ui/assets/clock-CgMZUGw6.jsbifrost-enterprise/ui/assets/clsx-Dvlz95bE.jsbifrost-enterprise/ui/assets/codeEditor-jGzQEGZA.jsbifrost-enterprise/ui/assets/collapsible-DKgz0n_X.jsbifrost-enterprise/ui/assets/columnPinning-D-ejkIeA.jsbifrost-enterprise/ui/assets/combobox-CmP7fdup.jsbifrost-enterprise/ui/assets/command-B7LBrawg.jsbifrost-enterprise/ui/assets/config-nQrPinVb.jsbifrost-enterprise/ui/assets/configSyncAlert-rnGDnLOt.jsbifrost-enterprise/ui/assets/context-DRdo5A2P-CkYLoPA2.jsbifrost-enterprise/ui/assets/createLucideIcon-gi3-rHSV.jsbifrost-enterprise/ui/assets/crown-kYjwe33e.jsbifrost-enterprise/ui/assets/css-BKnWUd0q.jsbifrost-enterprise/ui/assets/css-DzjybMKB.jsbifrost-enterprise/ui/assets/defineProperty-L1WUY2gb.jsbifrost-enterprise/ui/assets/devProfiler-hT4stDpR.jsbifrost-enterprise/ui/assets/dialog-B_DACXYn.jsbifrost-enterprise/ui/assets/differenceInCalendarDays-sd5SEEa1.jsbifrost-enterprise/ui/assets/dist-B006vWEU.jsbifrost-enterprise/ui/assets/dist-BKulpn1X.jsbifrost-enterprise/ui/assets/dist-BNn6boC5.jsbifrost-enterprise/ui/assets/dist-BsSdK9T_.jsbifrost-enterprise/ui/assets/dist-CW1kZn9_.jsbifrost-enterprise/ui/assets/dist-CWQUmRcK.jsbifrost-enterprise/ui/assets/dist-CeUQqI-N.jsbifrost-enterprise/ui/assets/dist-CfXYTkZU.jsbifrost-enterprise/ui/assets/dist-ChtU8PqI.jsbifrost-enterprise/ui/assets/dist-DGdZwKe9.jsbifrost-enterprise/ui/assets/dist-DI5ULOoV.jsbifrost-enterprise/ui/assets/dist-DXQP25As.jsbifrost-enterprise/ui/assets/dist-DZhjjogE.jsbifrost-enterprise/ui/assets/dist-Ddf49gE6.jsbifrost-enterprise/ui/assets/dist-DeokAr_j.jsbifrost-enterprise/ui/assets/dist-DijGUPWh.jsbifrost-enterprise/ui/assets/dist-Dw_XTMqb.jsbifrost-enterprise/ui/assets/dist-NbHR1uji.jsbifrost-enterprise/ui/assets/dist-O3s4_NHw.jsbifrost-enterprise/ui/assets/dist-b_OJbTwN2.jsbifrost-enterprise/ui/assets/dist-yopsvIGa.jsbifrost-enterprise/ui/assets/dollar-sign-DPk7GpXe.jsbifrost-enterprise/ui/assets/download-BTGIUCD5.jsbifrost-enterprise/ui/assets/dropdownMenu-Dwyki0Dz.jsbifrost-enterprise/ui/assets/ellipsis-Cq-icwpO.jsbifrost-enterprise/ui/assets/en-US-HbptL1uB.jsbifrost-enterprise/ui/assets/envVarInput-C1FFCV1q.jsbifrost-enterprise/ui/assets/esm-Bki7XBrL.jsbifrost-enterprise/ui/assets/esm-CHpVij2M.cssbifrost-enterprise/ui/assets/esm-Dz5DEAus.jsbifrost-enterprise/ui/assets/external-link-CiGA1J_0.jsbifrost-enterprise/ui/assets/eye-C62AC8Pg.jsbifrost-enterprise/ui/assets/eye-off-B_ConXlO.jsbifrost-enterprise/ui/assets/file-text-CX5SG0Mi.jsbifrost-enterprise/ui/assets/flag-DyTvPwFH.jsbifrost-enterprise/ui/assets/form-BGXMe1jq.jsbifrost-enterprise/ui/assets/formatDistanceToNow-BOuLEajm.jsbifrost-enterprise/ui/assets/fullPageLoader-Bim9Nvk5.jsbifrost-enterprise/ui/assets/git-branch-dffXsvg9.jsbifrost-enterprise/ui/assets/github-dark-B8wThxhL.jsbifrost-enterprise/ui/assets/github-light-BE6sxIlE.jsbifrost-enterprise/ui/assets/go-G2B5rlx1.jsbifrost-enterprise/ui/assets/governance-BkdJ4q2P.jsbifrost-enterprise/ui/assets/governance-ClJjPkgo.jsbifrost-enterprise/ui/assets/guardrailsConfigurationView-Bf7a_WJO.jsbifrost-enterprise/ui/assets/hard-drive-B1HfWpQe.jsbifrost-enterprise/ui/assets/headersTable-nSFCdBX1.jsbifrost-enterprise/ui/assets/highlighted-body-B3W2YXNL-CQfIigjz.jsbifrost-enterprise/ui/assets/html-WqzC1KWp.jsbifrost-enterprise/ui/assets/html2canvas-Cd4DtbGh.jsbifrost-enterprise/ui/assets/html2canvas-pro.esm-2kp479Ey.jsbifrost-enterprise/ui/assets/icons-DSJc63AE.jsbifrost-enterprise/ui/assets/index-BDz0EhCT.jsbifrost-enterprise/ui/assets/index-DWcj02wY.cssbifrost-enterprise/ui/assets/index.browser-Cpt4eNRa.jsbifrost-enterprise/ui/assets/index.es-TJuRENlm.jsbifrost-enterprise/ui/assets/index.esm-j-YfaO1_.jsbifrost-enterprise/ui/assets/info-fW9BdvRp.jsbifrost-enterprise/ui/assets/input-Bg_cv_me.jsbifrost-enterprise/ui/assets/javascript-6okyotJi.jsbifrost-enterprise/ui/assets/javascript-CKl41_LE.jsbifrost-enterprise/ui/assets/json-CcUNEEzx.jsbifrost-enterprise/ui/assets/jspdf.es.min-CRUS52N9.jsbifrost-enterprise/ui/assets/jsx-BRJIsmoE.jsbifrost-enterprise/ui/assets/jsx-runtime-6meTaj9M.jsbifrost-enterprise/ui/assets/key-C-D9bUKO.jsbifrost-enterprise/ui/assets/key-round-B_q_tMPk.jsbifrost-enterprise/ui/assets/label-CMqEh3rS.jsbifrost-enterprise/ui/assets/layout-B-j28UjP2.jsbifrost-enterprise/ui/assets/layout-B5JY2lUM.jsbifrost-enterprise/ui/assets/layout-B8-Gc0vq.jsbifrost-enterprise/ui/assets/layout-BBMUmJsU.jsbifrost-enterprise/ui/assets/layout-BBvjMa9f2.jsbifrost-enterprise/ui/assets/layout-BDDEzv-M.jsbifrost-enterprise/ui/assets/layout-BG-3ZW3t.jsbifrost-enterprise/ui/assets/layout-BHjieN4L.jsbifrost-enterprise/ui/assets/layout-BK6vMQbF.jsbifrost-enterprise/ui/assets/layout-BMKg_Kih.jsbifrost-enterprise/ui/assets/layout-BNqYuUcP.jsbifrost-enterprise/ui/assets/layout-BOPEOEXm2.jsbifrost-enterprise/ui/assets/layout-BOzvrkU3.jsbifrost-enterprise/ui/assets/layout-BUgQe9Lp.jsbifrost-enterprise/ui/assets/layout-BWds9cnm.jsbifrost-enterprise/ui/assets/layout-B_ZoFpGx2.jsbifrost-enterprise/ui/assets/layout-BdASNmQY.jsbifrost-enterprise/ui/assets/layout-BdhNtySo2.jsbifrost-enterprise/ui/assets/layout-Bmh0GQHP.jsbifrost-enterprise/ui/assets/layout-BqAAiGUB.jsbifrost-enterprise/ui/assets/layout-BrutbENR2.jsbifrost-enterprise/ui/assets/layout-C0BNeH-8.jsbifrost-enterprise/ui/assets/layout-C6oLNaRA2.jsbifrost-enterprise/ui/assets/layout-C7-p0dj_.jsbifrost-enterprise/ui/assets/layout-C8feCr0R.jsbifrost-enterprise/ui/assets/layout-CBCfy5Hz.jsbifrost-enterprise/ui/assets/layout-CCwQ_9cO2.jsbifrost-enterprise/ui/assets/layout-CHx2bjle.jsbifrost-enterprise/ui/assets/layout-CKedcCo7.jsbifrost-enterprise/ui/assets/layout-CMoRW18k.jsbifrost-enterprise/ui/assets/layout-CO3AyFLG2.jsbifrost-enterprise/ui/assets/layout-CRrouGVg.jsbifrost-enterprise/ui/assets/layout-CVxd5J0P2.jsbifrost-enterprise/ui/assets/layout-CXXMF0sE.cssbifrost-enterprise/ui/assets/layout-CZwPmu5-.jsbifrost-enterprise/ui/assets/layout-CaPO0SmO.jsbifrost-enterprise/ui/assets/layout-CdsCOg2B.jsbifrost-enterprise/ui/assets/layout-Ck3NPqcR.jsbifrost-enterprise/ui/assets/layout-CnaHLbWl.jsbifrost-enterprise/ui/assets/layout-Ct3tb7ay.jsbifrost-enterprise/ui/assets/layout-CzRH6kUe.jsbifrost-enterprise/ui/assets/layout-D2cjTywZ.jsbifrost-enterprise/ui/assets/layout-D6EkVqqH2.jsbifrost-enterprise/ui/assets/layout-D6xc-QZK.jsbifrost-enterprise/ui/assets/layout-DIN5uxPy.jsbifrost-enterprise/ui/assets/layout-DL1CRLh6.jsbifrost-enterprise/ui/assets/layout-DONRMEyh.jsbifrost-enterprise/ui/assets/layout-DRIxsgoK.jsbifrost-enterprise/ui/assets/layout-D_qIdqvc.jsbifrost-enterprise/ui/assets/layout-DblEwGzB.jsbifrost-enterprise/ui/assets/layout-DmZ8ICEp.jsbifrost-enterprise/ui/assets/layout-DyhBdbsw.jsbifrost-enterprise/ui/assets/layout-DzY6kfRP.jsbifrost-enterprise/ui/assets/layout-E7RGZ8tQ.jsbifrost-enterprise/ui/assets/layout-HCHnwE5X.jsbifrost-enterprise/ui/assets/layout-JAnfR4ZF.jsbifrost-enterprise/ui/assets/layout-Le8Y1CTp.jsbifrost-enterprise/ui/assets/layout-Nn9tFopH.jsbifrost-enterprise/ui/assets/layout-gDxp35l5.jsbifrost-enterprise/ui/assets/layout-grid-BVJkvuta.jsbifrost-enterprise/ui/assets/layout-jXIos_iz.jsbifrost-enterprise/ui/assets/layout-m9NEbxex.jsbifrost-enterprise/ui/assets/layout-sDvQLajG.jsbifrost-enterprise/ui/assets/layout-tycMn1Pg.jsbifrost-enterprise/ui/assets/layout-vaJIIq9M.jsbifrost-enterprise/ui/assets/layout-wJvigFJY.jsbifrost-enterprise/ui/assets/lib-CrFA-ka7.jsbifrost-enterprise/ui/assets/lib-V_ocUshp.jsbifrost-enterprise/ui/assets/link-C6zsUfDW.jsbifrost-enterprise/ui/assets/loader-circle-l1IbNqY5.jsbifrost-enterprise/ui/assets/lock-Bl_GsYdk.jsbifrost-enterprise/ui/assets/logs-DEYkupCf.jsbifrost-enterprise/ui/assets/logsVolumeChart-BE_dUX9F.jsbifrost-enterprise/ui/assets/markdown-B0UQ9PJy.jsbifrost-enterprise/ui/assets/mcpToolSelector-DFSUhaXO.jsbifrost-enterprise/ui/assets/mcpView-CkMaRTP2.jsbifrost-enterprise/ui/assets/mermaid-3ZIDBTTL-Cv3D3Ysa.jsbifrost-enterprise/ui/assets/minus-BMoloZRq.jsbifrost-enterprise/ui/assets/modelLimitsView-_TNWzVyT.jsbifrost-enterprise/ui/assets/modelMultiselect-CYEl1i5a.jsbifrost-enterprise/ui/assets/modelSettingsView-C7mPFsdJ.jsbifrost-enterprise/ui/assets/multiSelect-DXcqUO4k.jsbifrost-enterprise/ui/assets/noPermissionView-C83kn37O.jsbifrost-enterprise/ui/assets/normalizeDates-Dk_g-4yx.jsbifrost-enterprise/ui/assets/not-found-BoE77sAi.jsbifrost-enterprise/ui/assets/numbers-CLeT9WSu.jsbifrost-enterprise/ui/assets/observabilityView-1XPB4YsF.jsbifrost-enterprise/ui/assets/panel-left-open-Cya1ziV4.jsbifrost-enterprise/ui/assets/pdf-DegnCy_t.jsbifrost-enterprise/ui/assets/pencil-CGlPC7kx.jsbifrost-enterprise/ui/assets/piiRedactorRulesView-CSkAxida.jsbifrost-enterprise/ui/assets/play-BwRLxsVT.jsbifrost-enterprise/ui/assets/plug-B5jGsfv1.jsbifrost-enterprise/ui/assets/plus-f23X6_z9.jsbifrost-enterprise/ui/assets/popover-CAEKYxci.jsbifrost-enterprise/ui/assets/preload-helper-Bf_JiD2A.jsbifrost-enterprise/ui/assets/progress-BhsbUdby.jsbifrost-enterprise/ui/assets/provider-Ck_Osm6t.jsbifrost-enterprise/ui/assets/providersApi-_QRg3Xxr.jsbifrost-enterprise/ui/assets/purify.es-IR_Pjy6Q.jsbifrost-enterprise/ui/assets/puzzle-C9PGpIs-.jsbifrost-enterprise/ui/assets/python-BPW9yQtA.jsbifrost-enterprise/ui/assets/query-builder-BCGezdv3.cssbifrost-enterprise/ui/assets/radio-DDmmE8vm.jsbifrost-enterprise/ui/assets/rateLimitDisplay-BShmrngM.jsbifrost-enterprise/ui/assets/react-CGQiRZuS.jsbifrost-enterprise/ui/assets/react-Dg_Aww62.jsbifrost-enterprise/ui/assets/react-dom-Dzc_ubwQ.jsbifrost-enterprise/ui/assets/react-querybuilder-BQvGBGdO.jsbifrost-enterprise/ui/assets/refresh-ccw-Cl9pzS-9.jsbifrost-enterprise/ui/assets/refresh-cw-Cr2sUecC.jsbifrost-enterprise/ui/assets/regex-OZ964iWQ.jsbifrost-enterprise/ui/assets/rotate-ccw-DhPQqo4-.jsbifrost-enterprise/ui/assets/routingRuleGroupQuery-zZ4XzWAe.jsbifrost-enterprise/ui/assets/routingRulesApi-DvXKY8Dn.jsbifrost-enterprise/ui/assets/routingRulesView-CeZzcLvW.jsbifrost-enterprise/ui/assets/save-DP6-6y8X.jsbifrost-enterprise/ui/assets/schema-D7NPyfnX.jsbifrost-enterprise/ui/assets/schemas-B4ErVIWG.jsbifrost-enterprise/ui/assets/scroll-text-D87ilQyj.jsbifrost-enterprise/ui/assets/scrollArea-DNDY3uLR.jsbifrost-enterprise/ui/assets/search-CckdHOhF.jsbifrost-enterprise/ui/assets/select-DMUddKY5.jsbifrost-enterprise/ui/assets/separator-B2vRNuab.jsbifrost-enterprise/ui/assets/server-DWPDj5WM.jsbifrost-enterprise/ui/assets/settings-DcSggfnQ.jsbifrost-enterprise/ui/assets/sheet-BpXACGfy.jsbifrost-enterprise/ui/assets/shell-E7_23lfO.jsbifrost-enterprise/ui/assets/shellscript-C1c7URw7.jsbifrost-enterprise/ui/assets/shield-check-J6YWhH-S.jsbifrost-enterprise/ui/assets/shuffle-BaDkni3d.jsbifrost-enterprise/ui/assets/skeleton-BpMwPDL2.jsbifrost-enterprise/ui/assets/slicedToArray-BtCKZ5I9.jsbifrost-enterprise/ui/assets/sliders-horizontal-zl8UYjrT.jsbifrost-enterprise/ui/assets/sortable-Dcc1EI_f.jsbifrost-enterprise/ui/assets/sql-BPntKm8D.jsbifrost-enterprise/ui/assets/square-pen-CtQBGDBm.jsbifrost-enterprise/ui/assets/store-ByIGrKMZ.jsbifrost-enterprise/ui/assets/store-CF8nSiF7.jsbifrost-enterprise/ui/assets/strings-D168dBfu.jsbifrost-enterprise/ui/assets/switch-C3w_0bhZ.jsbifrost-enterprise/ui/assets/table-cGyfw8NJ.jsbifrost-enterprise/ui/assets/tabs-BL4OPtW6.jsbifrost-enterprise/ui/assets/teamsApi-WFnYeyOF.jsbifrost-enterprise/ui/assets/textarea-C90yAh5g.jsbifrost-enterprise/ui/assets/themeProvider-gJtnY1Fq.jsbifrost-enterprise/ui/assets/tooltip-BXv4E7qC.jsbifrost-enterprise/ui/assets/trash-2-Di3J1-wg.jsbifrost-enterprise/ui/assets/trash-DP9caARW.jsbifrost-enterprise/ui/assets/trending-up-DdynNEVE.jsbifrost-enterprise/ui/assets/triangle-alert-R39SAL6N.jsbifrost-enterprise/ui/assets/tristateCheckbox-nvyJOBBM.jsbifrost-enterprise/ui/assets/tsx-ZrTwrcW8.jsbifrost-enterprise/ui/assets/typeof-C5hD4QV2.jsbifrost-enterprise/ui/assets/typescript-BtSUrMXe.jsbifrost-enterprise/ui/assets/unlink-D_8S7eUI.jsbifrost-enterprise/ui/assets/use-isomorphic-layout-effect.browser.esm-B_Ew_DwZ.jsbifrost-enterprise/ui/assets/use-toast-Tnbn5Hqk.jsbifrost-enterprise/ui/assets/useDebounce-BWghbdw5.jsbifrost-enterprise/ui/assets/useLocation-DaMSnCE9.jsbifrost-enterprise/ui/assets/useNavigate-ly8KWrNF.jsbifrost-enterprise/ui/assets/useRouter-wgrf0dsd.jsbifrost-enterprise/ui/assets/useStore-1wOHOUw_.jsbifrost-enterprise/ui/assets/useWebSocket-Bvg1CLuL.jsbifrost-enterprise/ui/assets/user-D165FFOe.jsbifrost-enterprise/ui/assets/user-round-DpjjnzD7.jsbifrost-enterprise/ui/assets/users-DjAFlBtj.jsbifrost-enterprise/ui/assets/utils-DX2KD4Km.jsbifrost-enterprise/ui/assets/utils-Dy4kDzIU.jsbifrost-enterprise/ui/assets/v4-C_1xpkij.js
db64a81 to
cfc3830
Compare
Confidence Score: 5/5Safe to merge; all concurrency concerns from earlier review threads are addressed and the migration is correctly structured. The migration, in-memory backfill, iterator implementations, and observer hook are all structurally correct. The prior data-race on No files require special attention beyond the minor doc gap in Important Files Changed
Reviews (5): Last reviewed commit: "fix(governance): backfill rate-limit own..." | Re-trigger Greptile |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@plugins/governance/store.go`:
- Around line 2128-2133: The backfillOwnerScopes call mutates published state
(gs.budgets and gs.rateLimits) after the rebuilt maps are visible, allowing
concurrent BumpBudgetUsage/BumpRateLimitUsage to race and revert owner FK
stamping; fix by performing the owner-scope stamping before publishing the
rebuilt maps (i.e. stamp the local slices/structs in the rebuild routine) or, if
you must mutate after rebuild, replace map entries with fresh cloned structs
containing the stamped owner FK rather than mutating the existing pointers;
update the code around gs.backfillOwnerScopes so it operates on local clones or
applies stamps prior to swapping the maps to ensure race-safe shared state.
In `@plugins/governance/tracker.go`:
- Around line 51-52: The UsageObserver field (usageObserver) is accessed
concurrently by UpdateUsage (background goroutines) and mutated by
SetUsageObserver, causing a data race; fix by protecting access with a
synchronization primitive (e.g., add a sync.RWMutex on the tracker struct or
store the interface in an atomic.Value) and update SetUsageObserver to acquire
the write lock (or use atomic.Store) while UpdateUsage acquires a read lock (or
uses atomic.Load) before invoking the observer; apply the same pattern to other
usages of usageObserver noted in the comment so all reads/writes are race-safe.
In `@transports/bifrost-http/handlers/governance.go`:
- Around line 756-766: When creating a new TableRateLimit for an existing
provider config in updateVirtualKey (the branch where existing.RateLimitID ==
nil), stamp the newly-created rate limit's ProviderConfigID with the parent
providerConfig.ID before saving so ownerScopeFromRateLimit will resolve to
"provider"; locate the TableRateLimit creation in updateVirtualKey and set
rl.ProviderConfigID = &providerConfig.ID (and persist with tx.Save/tx.Create)
just like the create/new-config path does, and apply the same change to the
analogous block around the other rate-limit creation site referenced in the
review.
🪄 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: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 04172cd7-0186-4f2f-9180-5b0ab26e3fe4
⛔ Files ignored due to path filters (13)
core/go.sumis excluded by!**/*.sumframework/go.sumis excluded by!**/*.sumplugins/compat/go.sumis excluded by!**/*.sumplugins/governance/go.sumis excluded by!**/*.sumplugins/jsonparser/go.sumis excluded by!**/*.sumplugins/logging/go.sumis excluded by!**/*.sumplugins/maxim/go.sumis excluded by!**/*.sumplugins/mocker/go.sumis excluded by!**/*.sumplugins/otel/go.sumis excluded by!**/*.sumplugins/prompts/go.sumis excluded by!**/*.sumplugins/semanticcache/go.sumis excluded by!**/*.sumplugins/telemetry/go.sumis excluded by!**/*.sumtransports/go.sumis excluded by!**/*.sum
📒 Files selected for processing (19)
core/go.modframework/configstore/tables/budget.goframework/configstore/tables/ratelimit.goframework/go.modplugins/compat/go.modplugins/governance/go.modplugins/governance/main.goplugins/governance/store.goplugins/governance/tracker.goplugins/jsonparser/go.modplugins/logging/go.modplugins/maxim/go.modplugins/mocker/go.modplugins/otel/go.modplugins/prompts/go.modplugins/semanticcache/go.modplugins/telemetry/go.modtransports/bifrost-http/handlers/governance.gotransports/go.mod
cfc3830 to
7cc2c90
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@framework/configstore/migrations.go`:
- Around line 4595-4667: This migration (migrator.Migration ID
"add_rate_limit_owner_columns") creates indexes inside the transaction via
migrator.CreateIndex on fields from specs before running backfillStatements,
which can cause long write-blocking locks on Postgres; change the flow to first
add the columns and run the backfill using tx (the existing
tx.Exec/backfillStatements), then move index creation out of the transactional
Migrate block: after the migration returns, create the indexes
non-transactionally using raw SQL "CREATE INDEX CONCURRENTLY ..." (or a separate
follow-up migration) targeting the index names in specs
(idx_governance_rate_limits_*), and when checking/creating use the non-tx DB
handle (not tx or migrator.CreateIndex) and ensure existence checks before
issuing CREATE INDEX CONCURRENTLY to avoid errors.
In `@plugins/governance/tracker.go`:
- Around line 185-192: The extraction of owner IDs in tracker.go currently only
reads FK pointers (vk.TeamID, vk.CustomerID) and leaves teamID/customerID empty
when those pointers are nil; update that logic to fall back to the loaded
relation objects (vk.Team.ID and vk.Customer.ID) when the FK pointers are
nil—i.e., set teamID = *vk.TeamID if non-nil, else if vk.Team != nil use
vk.Team.ID (same for customerID using vk.Customer and vk.Customer.ID) so the
observer gets the correct IDs during the migration transition.
🪄 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: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 00b5d843-ed76-4b04-8d89-392774fa1ea9
⛔ Files ignored due to path filters (13)
core/go.sumis excluded by!**/*.sumframework/go.sumis excluded by!**/*.sumplugins/compat/go.sumis excluded by!**/*.sumplugins/governance/go.sumis excluded by!**/*.sumplugins/jsonparser/go.sumis excluded by!**/*.sumplugins/logging/go.sumis excluded by!**/*.sumplugins/maxim/go.sumis excluded by!**/*.sumplugins/mocker/go.sumis excluded by!**/*.sumplugins/otel/go.sumis excluded by!**/*.sumplugins/prompts/go.sumis excluded by!**/*.sumplugins/semanticcache/go.sumis excluded by!**/*.sumplugins/telemetry/go.sumis excluded by!**/*.sumtransports/go.sumis excluded by!**/*.sum
📒 Files selected for processing (21)
core/go.modframework/configstore/migrations.goframework/configstore/migrations_test.goframework/configstore/tables/budget.goframework/configstore/tables/ratelimit.goframework/go.modplugins/compat/go.modplugins/governance/go.modplugins/governance/main.goplugins/governance/store.goplugins/governance/tracker.goplugins/jsonparser/go.modplugins/logging/go.modplugins/maxim/go.modplugins/mocker/go.modplugins/otel/go.modplugins/prompts/go.modplugins/semanticcache/go.modplugins/telemetry/go.modtransports/bifrost-http/handlers/governance.gotransports/go.mod
36554fa to
ab05c04
Compare
7cc2c90 to
7849f0d
Compare
## Summary Adds an "Allow Private Network" toggle to the custom provider creation form, enabling users to configure whether a custom provider can connect to private network IP ranges (e.g., `192.168.x.x`, `10.x.x.x`). Link-local addresses remain blocked regardless of this setting. ## Changes - Added `allow_private_network` as an optional boolean field to the custom provider form schema, defaulting to `false` - Wired the field value into `network_config.allow_private_network` when saving the provider - Added a labeled toggle switch in the form UI with a description clarifying which address ranges are affected and which remain blocked ## 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 1. Navigate to the custom provider creation sheet in the workspace providers UI. 2. Verify the "Allow Private Network" toggle is visible and defaults to off. 3. Enable the toggle and save the provider — confirm `allow_private_network: true` is included in the saved `network_config`. 4. Disable the toggle and save — confirm `allow_private_network: false` is sent. 5. Verify the toggle is disabled when the user lacks provider create access. ```sh cd ui pnpm i || npm i pnpm test || npm test pnpm build || npm run build ``` ## Screenshots/Recordings _Add before/after screenshots of the custom provider form showing the new toggle._ ## Breaking changes - [ ] Yes - [x] No ## Related issues Link related issues and discussions. Example: Closes #123 ## Security considerations This toggle explicitly opts a custom provider into connecting to private network ranges. It defaults to `false` (blocked), preserving the existing secure-by-default behavior. Link-local addresses remain blocked unconditionally to prevent SSRF via metadata 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 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added an "Allow Private Network" toggle option in the custom provider creation form, enabling users to control private network access settings when setting up custom providers. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
7849f0d to
ae6ed4a
Compare
…est provider/model routing (#4175) ## Summary Introduces a new `PreRequestHook` phase to the `LLMPlugin` interface. This hook runs exactly once per top-level request — after `HTTPTransportPreHook` and before `PreLLMHook` — and is the canonical place for plugins to resolve provider, model, and fallback routing decisions. Previously, routing logic had to be shoehorned into `PreLLMHook`, which runs on every fallback attempt and whose mutations have incidental cross-fallback visibility. `PreRequestHook` mutations are committed to the shared `*BifrostRequest` before any fan-out and are observed by every subsequent plugin, every `PreLLMHook` invocation, the provider call, and every fallback. As part of this change, the `filterProvidersByContext` helper (used in `ListAllModels`) is removed, and request validation is moved to after `PreRequestHook` runs so that plugins have the opportunity to populate provider/model before the empty-field check fires. Error messages for missing provider/model are updated to reflect that auto-resolution was attempted. ## Changes - Added `PreRequestHook(ctx *BifrostContext, req *BifrostRequest) error` to the `LLMPlugin` interface with non-blocking error semantics (logged as warning, pipeline continues). - Added `RunPreRequestHooks` to `PluginPipeline`, executing the hook in registration order once per request with tracing and plugin-scope isolation. - Added `RunPreRequestHooks` as a public method on `Bifrost` for callers (e.g., realtime WebSocket handlers) that bypass the normal inference path. - Moved `validateRequest` to after `PreRequestHook` execution in both `handleRequest` and `handleStreamRequest`, renamed to `validateRequestAfterPreRequestHooks` with updated error messages. - Added primary-provider error logging to `handleStreamRequest` to match `handleRequest` behavior. - Removed `filterProvidersByContext` and its tests from `ListAllModels`. - Updated `DynamicPlugin` (shared-object loader) to optionally load `PreRequestHook` from `.so` plugins; legacy plugins without the export get a no-op passthrough, preserving backward compatibility. - Updated `AsLLMPlugin` to recognize `preRequestHook` as sufficient to qualify a `DynamicPlugin` as an `LLMPlugin`. - Added no-op `PreRequestHook` implementations to all existing plugins (`compat`, `governance`, `jsonparser`, `logging`, `maxim`, `mocker`, `prompts`, `semanticcache`, `telemetry`) and all example/test plugins to satisfy the updated interface. - Updated plugin execution-order documentation in `plugin.go` to describe per-request vs. per-attempt semantics. ## Type of change - [ ] Bug fix - [x] Feature - [ ] Refactor - [ ] Documentation - [ ] Chore/CI ## Affected areas - [x] Core (Go) - [x] Transports (HTTP) - [ ] Providers/Integrations - [x] Plugins - [ ] UI (React) - [ ] Docs ## How to test ```sh go test ./... ``` Validate that: - A plugin implementing `PreRequestHook` can mutate `req.Provider` and `req.Model` before the provider call, and those mutations are visible to subsequent plugins and fallback attempts. - A plugin that returns a non-nil error from `PreRequestHook` does not abort the request; the pipeline continues to the next plugin and a warning is logged. - Existing plugins with no-op `PreRequestHook` implementations behave identically to before. - Legacy `.so` plugins that do not export `PreRequestHook` load and function correctly with the no-op passthrough. - Requests with no provider set (and no plugin resolving one) return the updated error message: `"could not auto resolve a provider for the request, please specify a provider explicitly"`. ## Breaking changes - [x] Yes - [ ] No The `LLMPlugin` interface gains a new required method `PreRequestHook`. Any external plugin implementing `LLMPlugin` must add a `PreRequestHook` method. Plugins that do not participate in routing should return `nil`. Shared-object (`.so`) plugins are exempt — the loader treats `PreRequestHook` as optional and provides a no-op default. ## Security considerations `PreRequestHook` runs with `BlockRestrictedWrites` active on the context (same as `RunLLMPreHooks`), preventing plugins from writing to restricted context keys during the hook. Plugins cannot abort or gate requests via error return from this hook; authorization and content-policy enforcement must remain in `HTTPTransportPreHook` or via a short-circuit in `PreLLMHook`. ## Checklist - [ ] 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) - [ ] I verified the CI pipeline passes locally if applicable <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **New Features** * Added `PreRequestHook` plugin phase enabling plugins to perform per-request routing decisions before provider and model validation. * **Refactor** * Request validation now occurs after plugin hooks execute, allowing automatic resolution of routing parameters. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
…terprise fallback (#4196) ## Summary Adds a dedicated **Settings** sub-route (`/workspace/adaptive-routing/settings`) to the Adaptive Routing section, giving it a tabbed sidebar structure similar to other sections like Custom Pricing. On OSS builds, the settings page reuses the existing enterprise upsell fallback from the adaptive routing dashboard rather than introducing a duplicate. ## Changes - Added `/workspace/adaptive-routing/settings` as a child route with its own layout and page component, rendering `LoadBalancerSettingsView` from the enterprise layer. - Updated the adaptive routing layout to use `useChildMatches` and `<Outlet />` so the dashboard renders at the base path while child routes (e.g. `/settings`) render independently. - Added `Dashboard` and `Settings` sub-items to the Adaptive Routing sidebar entry, mirroring the tab pattern used elsewhere. - Extended the `isRouteMatch` exact-match logic in the sidebar to include `/workspace/adaptive-routing`, preventing the Dashboard tab from remaining highlighted when the Settings tab is active. - Added an OSS fallback for `loadBalancerSettingsView` that re-exports the existing `adaptiveRoutingView` upsell component. - Registered `LoadBalancerConfig` as a tag in the base API for cache invalidation. - Updated the sidebar description from "Manage adaptive load balancer" to "Manage adaptive routing". ## 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 1. Navigate to the Adaptive Routing section in the sidebar. 2. Confirm the sidebar now shows **Dashboard** and **Settings** sub-items. 3. Click **Dashboard** — verify it renders the adaptive routing dashboard and the Dashboard tab is highlighted. 4. Click **Settings** — verify it renders the settings view and the Settings tab is highlighted (Dashboard tab should not remain highlighted). 5. On an OSS build, verify the Settings page displays the same enterprise upsell as the Dashboard page. ```sh cd ui pnpm i || npm i pnpm build || npm run build ``` ## Screenshots/Recordings _Add before/after screenshots showing the new sidebar sub-items and the settings page._ ## Breaking changes - [x] No ## Related issues ## Security considerations No new auth surfaces introduced. The existing RBAC check (`RbacResource.AdaptiveRouter`) in the layout guards both the dashboard and the new settings route. ## 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 is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added Adaptive Routing Settings page with dedicated navigation and dashboard access. * Enhanced sidebar navigation with sub-items for Adaptive Routing Dashboard and Settings. * Integrated load balancer settings into the Adaptive Routing interface with enterprise fallback support. * Improved plugin execution order to ensure provider selection occurs after routing components. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
ae6ed4a to
6d0f0f6
Compare
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@framework/configstore/migrations_test.go`:
- Around line 2159-2202: Extend
TestMigrationAddRateLimitOwnerColumns_AddsColumnsAndBackfillsOwners to cover two
edge cases: insert an orphaned rate-limit (e.g., call insertRateLimitRaw with an
id like "rl-orphan" and do not create any owner rows), run
migrationAddRateLimitOwnerColumns(ctx, db), then assert the new owner columns
exist (mig.HasColumn on virtual_key_id/team_id/customer_id/provider_config_id)
and that the owner fields for "rl-orphan" are NULL/zero as appropriate; and add
an idempotency check by calling migrationAddRateLimitOwnerColumns(ctx, db) a
second time and asserting it returns no error and that previously backfilled
owner values (queried from governance_rate_limits for
"rl-vk","rl-team","rl-customer","rl-provider") remain unchanged.
- Around line 2159-2202: The test
TestMigrationAddRateLimitOwnerColumns_AddsColumnsAndBackfillsOwners should be
extended to verify idempotency and behavior for rate limits with no owner: after
calling migrationAddRateLimitOwnerColumns once, call it a second time and assert
no errors and that all columns on tables.TableRateLimit still exist and retain
expected values; also insert a rate limit record with no associated virtual
key/team/customer/provider_config (no owner), run the migration and assert the
new columns remain NULL/zero for that record (e.g.,
virtual_key_id/team_id/customer_id/provider_config_id are unchanged),
referencing migrationAddRateLimitOwnerColumns,
TestMigrationAddRateLimitOwnerColumns_AddsColumnsAndBackfillsOwners, and
tables.TableRateLimit to locate the code to modify.
In `@framework/configstore/migrations.go`:
- Around line 584-586: The migrationAddRateLimitOwnerColumns migration backfills
owner columns before migrationMigrateProviderGovernanceToModelConfigs and
migrationMigrateVirtualKeyGovernanceToModelConfigs reparent rate-limit rows to
governance_model_configs, causing owner columns to become stale; update the
migration sequence to either (A) extend migrationAddRateLimitOwnerColumns to
also detect and set owner scope from governance_model_configs.rate_limit_id
(i.e. backfill model-config ownership into
virtual_key_id/team_id/customer_id/provider_config_id where
governance_model_configs points at the rate limit), or (B) add a new
post-reparent reconciliation step run after
migrationMigrateProviderGovernanceToModelConfigs and
migrationMigrateVirtualKeyGovernanceToModelConfigs that rewrites owner columns
based on governance_model_configs.rate_limit_id so the owner scope always
reflects the final owning table. Ensure this references
migrationAddRateLimitOwnerColumns,
migrationMigrateProviderGovernanceToModelConfigs,
migrationMigrateVirtualKeyGovernanceToModelConfigs and
governance_model_configs.rate_limit_id when implementing the fix.
🪄 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: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 4ff272d0-27dd-432b-886e-8266e004968e
📒 Files selected for processing (7)
framework/configstore/migrations.goframework/configstore/migrations_test.goframework/configstore/tables/ratelimit.goplugins/governance/main.goplugins/governance/store.goplugins/governance/tracker.gotransports/bifrost-http/handlers/governance.go
💤 Files with no reviewable changes (4)
- plugins/governance/tracker.go
- plugins/governance/main.go
- plugins/governance/store.go
- transports/bifrost-http/handlers/governance.go
… field reference, troubleshooting, and updated screenshots (#4206) ## Summary Rewrites the semantic caching documentation to accurately reflect how the feature works, including the two distinct lookup paths (direct hash matching and semantic similarity), the mandatory cache key requirement, and the asynchronous write behavior. Also adds a new log-details screenshot and updates the existing config screenshot. ## Changes - Replaced the overview with a clearer description of the two caching paths (direct and semantic) and a Mermaid flow diagram showing the full lookup sequence. - Added a "How it works" section that calls out the four most common first-time pitfalls: missing cache key, direct-before-semantic ordering, async writes, and persistence across restarts. - Consolidated the configuration reference into a single field table covering all options (`provider`, `embedding_model`, `dimension`, `ttl`, `threshold`, `conversation_history_threshold`, `exclude_system_prompt`, `cache_by_model`, `cache_by_provider`, `vector_store_namespace`, `default_cache_key`). - Restructured configuration tabs to lead with the Web UI, then API, then `config.json`, then Go SDK — matching the most common usage order. - Added an API tab showing `POST /api/plugins` and `PUT /api/plugins/semantic_cache` examples for enabling, updating, and disabling the plugin without a restart. - Replaced the separate "Direct Hash Mode" section with a comparison table and folded the setup instructions into the main configuration section. - Expanded the `cache_debug` metadata table to include all fields (`cache_hit`, `cache_id`, `hit_type`, `threshold`, `similarity`, `provider_used`, `model_used`, `input_tokens`) with accurate presence conditions. - Added documentation for cache visibility in the Logs UI: hit-type badges, the Cache row with copyable `cache_id`, the Caching Details block, and the Local Caching filter. - Replaced the "Cache Lifecycle & Cleanup" prose with a concise bullet list and clarified that entries persist across restarts (previous docs implied a restart would clear the cache). - Added a Troubleshooting accordion section covering the six most common failure modes. - Added a "Next steps" section linking to vector store setup, plugins overview, and providers docs. - Updated `ui-semantic-cache-config.png` and added `ui-semantic-cache-log-details.png`. ## Type of change - [ ] Bug fix - [ ] Feature - [ ] Refactor - [x] Documentation - [ ] Chore/CI ## Affected areas - [ ] Core (Go) - [ ] Transports (HTTP) - [ ] Providers/Integrations - [ ] Plugins - [ ] UI (React) - [x] Docs ## How to test Review the rendered docs to confirm: 1. The Mermaid diagram renders correctly. 2. All tab groups (`config-method`, `direct-hash-setup`, `cache-triggering`, `per-request-overrides`, `cache-clear`) display the correct content per tab. 3. The `ui-semantic-cache-log-details.png` image renders in the Cache Management section. 4. The Troubleshooting accordions expand and collapse correctly. 5. Internal anchor links (`#prerequisites`, `#configuration-reference`, `#cache-lifecycle--cleanup`, `#cache-management`) resolve without 404s. ## Screenshots/Recordings Updated `ui-semantic-cache-config.png` reflects the revised UI layout. New `ui-semantic-cache-log-details.png` shows the log detail sheet with the Semantic Cache badge and Caching Details block. ## Breaking changes - [ ] Yes - [x] No ## Related issues ## Security considerations None. Documentation-only change; no secrets, auth flows, or PII handling modified. ## Checklist - [ ] I read `docs/contributing/README.md` and followed the guidelines - [ ] I added/updated tests where appropriate - [x] I updated documentation where needed - [ ] I verified builds succeed (Go and UI) - [ ] I verified the CI pipeline passes locally if applicable
…heckRequest` context marker (#4207) ## Summary Introduces a new context key, `BifrostContextKeyMCPHealthCheckRequest`, that marks MCP ping and `list_tools` requests as internally generated by Bifrost's health monitor. This allows plugins, hooks, and other middleware to distinguish these internal probes from caller-initiated requests. ## Changes - Added `BifrostContextKeyMCPHealthCheckRequest` context key (`"bifrost-mcp-health-check-request"`) to the set of reserved Bifrost context keys. - In `performHealthCheck`, the timeout context is now wrapped in a `BifrostContext` with the new key set to `true` before being passed to `runPingWithHooks` / `runListToolsWithHooks`, ensuring the marker propagates through the entire health check call chain. - The key is reserved (added to `reservedKeys`) so it cannot be overridden externally — the comment explicitly notes it should not be set manually. ## Type of change - [ ] Bug fix - [x] Feature - [ ] Refactor - [ ] Documentation - [ ] Chore/CI ## Affected areas - [x] Core (Go) - [ ] Transports (HTTP) - [ ] Providers/Integrations - [x] Plugins - [ ] UI (React) - [ ] Docs ## How to test Write a plugin or hook that inspects the incoming context for `BifrostContextKeyMCPHealthCheckRequest`. Trigger a health check cycle and verify the key is present and set to `true` for ping/list_tools probes, while being absent for normal caller-initiated requests. ```sh go test ./... ``` ## Breaking changes - [ ] Yes - [x] No ## Security considerations The new context key is reserved and explicitly documented as not to be set manually, preventing external callers from spoofing health check requests to bypass plugin logic that gates on this marker. ## 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
Signed-off-by: StepSecurity Bot <bot@stepsecurity.io> Co-authored-by: stepsecurity-app[bot] <188008098+stepsecurity-app[bot]@users.noreply.github.com>
## Summary `PluginSpanFilter` and its associated logic (`ShouldExportSpan`, `BuildReparentMap`, `PluginNameFromSpan`) were previously defined and implemented inside the OTEL plugin package. This PR promotes them to `core/schemas` so they can be shared across all observability connectors (OTEL, Datadog, BigQuery) without duplicating the span-name contract or reparenting behavior. The OTEL package re-exports the types and constants as aliases to preserve existing import paths. The `PluginTracingSheet` UI component is also generalized to accept a `pluginName` and `destination` prop, and is relocated from the plugins page to the OTEL observability view where it belongs. ## Changes - Introduced `core/schemas/span_filter.go` with `PluginSpanFilter`, `PluginSpanFilterMode`, `PluginNameFromSpan`, `ShouldExportSpan`, and `BuildReparentMap`, along with full unit test coverage in `span_filter_test.go`. - Removed the duplicate `shouldExportSpan` and `buildReparentMap` methods from `plugins/otel/converter.go`; call sites now delegate to the shared schema methods. - `PluginSpanFilter`, `PluginSpanFilterMode`, and the include/exclude constants in `plugins/otel/main.go` are replaced with type aliases and const aliases pointing to `core/schemas`, keeping the OTEL package's public API unchanged. - Validation in `otel.Init` is replaced with a call to `config.PluginSpanFilter.Validate()`. - `PluginTracingSheet` is moved from `ui/app/workspace/plugins/sheets/` to `ui/app/workspace/observability/sheets/` and now accepts `pluginName` and `destination` props, making it connector-agnostic. - The "Configure Plugin Tracing" button and `PluginTracingSheet` are removed from the plugins page and plugins empty state, and are instead surfaced directly in `OtelView`. - Added a warning notice to the `ent-v1.4.7` changelog about a known `/virtual-key/quota` issue fixed in v1.4.8. - Improved the `v1.5.11` changelog rollback section with a warning callout and collapsible `AccordionGroup` sections for single-node and multi-node rollback SQL. ## Type of change - [ ] Bug fix - [ ] Feature - [x] Refactor - [ ] Documentation - [ ] Chore/CI ## Affected areas - [x] Core (Go) - [ ] Transports (HTTP) - [ ] Providers/Integrations - [x] Plugins - [x] UI (React) - [x] Docs ## How to test ```sh # Core/Transports go test ./core/schemas/... ./plugins/otel/... # UI cd ui pnpm i pnpm build ``` - Open the Observability → OTEL view and confirm the "Configure Plugin Tracing" button appears and opens the sheet correctly. - Verify the sheet reads and writes `plugin_span_filter` only for the `otel` plugin. - Confirm the Plugins page no longer shows a "Configure Plugin Tracing" button or sheet. - Confirm the plugins empty state no longer renders the tracing button. ## Screenshots/Recordings N/A — functional behavior is unchanged; only the location of the tracing button has moved. ## Breaking changes - [ ] Yes - [x] No The OTEL package re-exports all renamed types and constants as aliases, so existing config parsing and external consumers are unaffected. ## Related issues N/A ## Security considerations No new auth, secrets, PII handling, or sandboxing changes introduced. ## 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) - [ ] I verified the CI pipeline passes locally if applicable <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **New Features** * Added configurable span filtering for plugin observability exports with include/exclude modes to control which plugins' spans are exported to observability connectors. * Extended plugin tracing configuration to support any backend plugin destination, not limited to a single connector. * **Refactor** * Consolidated span filtering logic for improved reusability and consistency across observability integrations. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
Extends `plugin_span_filter` support to the Datadog observability connector and introduces a new BigQuery traces connector, while renaming the shared `otelPluginSpanFilter` / `otel_plugin_span_filter` schema definition to the more generic `pluginSpanFilter` / `plugin_span_filter` so it can be reused across all observability connectors.
## Changes
- Added `plugin_span_filter` support to the Datadog plugin in both the Helm chart template (`_helpers.tpl`) and its schema/values definitions, matching the existing pattern used by OTEL connectors.
- Renamed the `otelPluginSpanFilter` / `otel_plugin_span_filter` schema `$defs` entry to `pluginSpanFilter` / `plugin_span_filter` in both `values.schema.json` and `transports/config.schema.json`, and updated all `$ref` usages accordingly. The description was also updated to clarify that the filter applies to any observability connector, not just OTEL.
- Added a full JSON schema definition for a new `bigquery` observability connector in `transports/config.schema.json`, including fields for `project_id`, `dataset_id`, `table_id`, `location`, `service_account_key` (with ADC fallback), `flush_interval_seconds`, `buffer_size`, `custom_labels`, `disable_content_logging`, `request_headers`, and `plugin_span_filter`.
- Added the `bigquery` plugin to the Helm chart (`values.yaml`, `values.schema.json`, and `_helpers.tpl`) with the same `version` validation guard used by other built-in plugins.
- Added commented-out `plugin_span_filter` examples to `values.yaml` for both the Datadog and BigQuery plugins to aid discoverability.
## Type of change
- [ ] Bug fix
- [x] Feature
- [x] Refactor
- [ ] Documentation
- [ ] Chore/CI
## Affected areas
- [ ] Core (Go)
- [x] Transports (HTTP)
- [ ] Providers/Integrations
- [x] Plugins
- [ ] UI (React)
- [ ] Docs
## How to test
1. Deploy the Helm chart with a Datadog plugin config that includes `plugin_span_filter`:
```yaml
bifrost:
plugins:
datadog:
config:
plugin_span_filter:
mode: "exclude"
plugins: ["logging"]
```
Verify the rendered manifest includes `plugin_span_filter` in the Datadog plugin config.
2. Deploy the Helm chart with the BigQuery plugin enabled:
```yaml
bifrost:
plugins:
bigquery:
enabled: true
version: 1
config:
project_id: "my-gcp-project"
dataset_id: "bifrost_traces"
table_id: "traces"
```
Verify the rendered manifest includes the BigQuery plugin config with the expected fields.
3. Validate `transports/config.schema.json` against a BigQuery connector config:
```json
{
"name": "bigquery",
"config": {
"project_id": "my-gcp-project",
"plugin_span_filter": { "mode": "include", "plugins": ["auth"] }
}
}
```
4. Confirm that no dangling `$ref` entries referencing the old `otelPluginSpanFilter` / `otel_plugin_span_filter` names remain in either schema file.
## Breaking changes
- [x] Yes
- [ ] No
The `otelPluginSpanFilter` / `otel_plugin_span_filter` `$defs` keys have been renamed to `pluginSpanFilter` / `plugin_span_filter`. Any external tooling or configs that reference these definition names directly will need to be updated.
## Related issues
## Security considerations
The BigQuery connector schema supports `service_account_key` via an environment variable reference (`env.MY_VAR`) or Application Default Credentials, avoiding the need to embed raw credentials in config files. Care should be taken to ensure service account keys are not logged or exposed through the `custom_labels` or `request_headers` fields.
## 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
Documents the `plugin_span_filter` configuration option for the Datadog connector, and corrects the UI navigation path for the OTEL connector's plugin span filtering instructions.
## Changes
- Added a new **Plugin Span Filtering** section to the Datadog connector docs, covering `exclude`/`include` filter modes, config.json usage, UI configuration, built-in plugin names, child span re-parenting behavior, and a note on per-connector filter independence and config versioning precedence.
- Updated the OTEL connector docs to correct the UI navigation path from the generic "Plugins page" to the specific **Observability** page → **Open Telemetry** connector flow, matching the Datadog connector's updated instructions.
## Type of change
- [ ] Bug fix
- [ ] Feature
- [ ] Refactor
- [x] Documentation
- [ ] Chore/CI
## Affected areas
- [ ] Core (Go)
- [ ] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [x] Docs
## How to test
Review the rendered documentation for:
- The new **Plugin Span Filtering** section appearing correctly in the Datadog connector page, including the config.json example, filter mode table, built-in plugin name list, and the `<Note>` callout.
- The OTEL connector page showing the corrected UI navigation path ("Open the **Observability** page, select the **Open Telemetry** connector...").
## Breaking changes
- [ ] Yes
- [x] No
## Related issues
## Security considerations
None.
## Checklist
- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [ ] I added/updated tests where appropriate
- [x] I updated documentation where needed
- [ ] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit
* **Documentation**
* Added a Plugin Span Filtering guide for Datadog APM explaining how to include/exclude plugin execution spans, supported filter modes (include/exclude), built-in plugin names, example configuration, and how filtering re-parents child spans in traces.
* Clarified OpenTelemetry connector docs to use the Observability page for plugin tracing and that connector config can override UI-saved filters by version.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
Adds support for multimodal function responses (images and files returned by tools) in the Gemini provider. Previously, when a tool returned image or file content alongside text, the media was either dropped or serialized as a raw JSON fallback. This PR preserves those media blocks by routing them through `FunctionResponse.Parts` (a Gemini 3+ feature), with correct provider-specific behavior for the Gemini Developer API vs. Vertex AI.
## Changes
- **`FunctionResponse.Parts` field added** to the `FunctionResponse` type so images/files returned by tools can be attached as sibling parts alongside the structured response object.
- **Forward conversion (`convertResponsesMessagesToGeminiContents`)** now accepts `model` and `provider` arguments. For Gemini 3+ models, image/file content blocks from `ResponsesFunctionToolCallOutputBlocks` are converted to `inlineData`/`fileData` parts and attached to `FunctionResponse.Parts`. For older models (e.g. `gemini-2.5-flash`), media is silently dropped to avoid a hard upstream 400. Vertex AI emits a `{"$ref": "<displayName>"}` entry in the response object (as documented); the Gemini Developer API does not (the `$ref` form triggers an upstream bug).
- **Reverse conversion (`convertGeminiContentsToResponsesMessages`)** reconstructs multimodal function responses back into `ResponsesFunctionToolCallOutputBlocks` (text + image blocks), preserving media on the Bifrost side instead of collapsing everything to a plain string.
- **`Part.UnmarshalJSON`** now handles snake_case fallbacks (`inline_data`, `file_data`) emitted by the google-genai SDK inside `functionResponse.parts`.
- **`Blob.UnmarshalJSON`** now handles snake_case fallbacks (`mime_type`, `display_name`) from `FunctionResponseBlob`.
- **`FileData.UnmarshalJSON`** added with snake_case fallbacks (`mime_type`, `file_uri`, `display_name`) from `FunctionResponseFileData`.
- **Unit tests** added for: image preserved on Gemini 3 (Developer API, no `$ref`), image dropped on older models, Vertex emitting `$ref`, and a full round-trip (`GeminiGenerationRequest` → `BifrostResponsesRequest` → `GeminiGenerationRequest`).
- **Integration tests** added (`test_30`, `test_30b`) covering a fabricated multimodal tool history and a real two-turn workflow, parameterized across `gemini-3-flash-preview` (image understood) and `gemini-2.5-flash` (image dropped, request still succeeds).
## Type of change
- [x] Bug fix
- [x] Feature
## Affected areas
- [x] Core (Go)
- [x] Providers/Integrations
## How to test
```sh
# Unit tests
go test ./core/providers/gemini/... -run TestResponsesAPIParallelFunctionCalling
go test ./core/providers/gemini/... -run TestMultimodalFunctionResponse_RoundTrip
# Integration tests (requires GEMINI_API_KEY)
cd tests/integrations/python
pytest tests/test_google.py::TestGoogleProvider::test_30_multimodal_function_response_image
pytest tests/test_google.py::TestGoogleProvider::test_30b_multimodal_function_response_full_workflow
```
Expected outcomes:
- `gemini-3-flash-preview`: model identifies the tool-returned image color as "red".
- `gemini-2.5-flash`: request succeeds without a 400; model produces a text reply (image was dropped by gating).
## Breaking changes
- [x] No
## Security considerations
No new auth, secrets, or PII surface. Base64 image data passes through in-memory only and is not logged or persisted.
## Checklist
- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit
* **New Features**
* Improved multimodal function-response support: tools can return images alongside text for Gemini 3 models; other model types gracefully fall back to text-only or reference-style handling.
* **Compatibility**
* Better handling of provider/model variations so image-containing tool outputs are preserved where supported and safely downgraded otherwise.
* **Tests**
* Added end-to-end and regression tests validating multimodal function-response round‑trips and field preservation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary Implements the full GCS-backed File API for the Vertex AI provider, replacing the previous stub implementations that returned `UnsupportedOperation` errors. Vertex AI uses Google Cloud Storage rather than a native file store, so all file operations (upload, list, retrieve, delete, content download) are mapped to GCS JSON API calls authenticated via the existing Vertex credential chain. ## Changes - **Vertex `FileUpload`**: Supports two modes — direct upload (multipart/related to GCS when file bytes are provided) and resumable session initiation (returns a GCS `Location` URL when no bytes are provided, allowing the client to PUT bytes directly to GCS). A new `UploadURL` field is added to `BifrostFileUploadResponse` to carry the session URL. - **Vertex `FileList`**: Lists GCS objects under a configurable bucket/prefix. Supports cursor-based pagination via `pageToken`. - **Vertex `FileRetrieve`**: Fetches GCS object metadata by `gs://` URI. - **Vertex `FileDelete`**: Deletes a GCS object by `gs://` URI. Treats 404 as success for idempotency. - **Vertex `FileContent`**: Downloads raw object bytes from GCS by `gs://` URI. - **GCS helpers**: Added `gcsResolveBucket`, `gcsObjectKey`, `gcsEncodeObjectName`, `parseGCSURI`, `gcsMetadataToFileObject`, `gcsGetAuthHeader`, and `parseGCSAPIError` to support the above operations. Bucket and prefix can be supplied via `StorageConfig.GCS` or `extra_params["gcs_bucket"]`/`extra_params["gcs_prefix"]`. - **New GCS types**: `gcsObjectMetadata`, `gcsObjectListResponse`, and `gcsErrorBody` added to `vertex/types.go`. - **`FileStatusPendingUpload`**: New `FileStatus` constant representing a resumable session that has been minted but whose bytes have not yet been received. - **`bifrost.go` validation**: The empty-file guard is skipped for Vertex, since resumable uploads intentionally omit file bytes. - **HTTP transport `fileUpload`**: The `file` multipart field is now optional. When absent, a `filename` form field is accepted instead. `content_type` and arbitrary extra form fields (e.g. `gcs_bucket`, `gcs_prefix`) are forwarded to the provider. - **HTTP transport `fileList`**: Unknown query args are collected and forwarded as `ExtraParams` so storage-backed providers can receive `gcs_bucket` etc. - **HTTP transport file ID decoding**: `fileRetrieve`, `fileDelete`, and `fileContent` now percent-decode the file ID path segment, allowing `gs://` and `s3://` URIs to be passed safely in URL paths. - **`DisablePathNormalizing`**: Enabled on the Vertex fasthttp client to prevent path normalization from mangling percent-encoded GCS object names. - **UI**: Vertex is added to `BATCH_SUPPORTED_PROVIDERS` and the missing `BatchAPIFormField` is rendered for providers that support batch. ## Type of change - [ ] Bug fix - [x] Feature - [ ] Refactor - [ ] Documentation - [ ] Chore/CI ## Affected areas - [x] Core (Go) - [x] Transports (HTTP) - [x] Providers/Integrations - [ ] Plugins - [x] UI (React) - [ ] Docs ## How to test ```sh # Core/Transports go test ./... # Direct upload (file bytes provided) curl -X POST http://localhost:8080/v1/files \ -F "provider=vertex" \ -F "purpose=batch" \ -F "gcs_bucket=my-bucket" \ -F "file=@/path/to/file.jsonl" # Expected: 200 with status=processed and storage_uri=gs://my-bucket/... # Resumable upload session (no file bytes) curl -X POST http://localhost:8080/v1/files \ -F "provider=vertex" \ -F "purpose=batch" \ -F "gcs_bucket=my-bucket" \ -F "filename=input.jsonl" \ -F "content_type=application/jsonl" # Expected: 200 with status=pending_upload and upload_url set # List files curl "http://localhost:8080/v1/files?provider=vertex&gcs_bucket=my-bucket" # Retrieve metadata (gs:// URI must be percent-encoded in path) curl "http://localhost:8080/v1/files/gs%3A%2F%2Fmy-bucket%2Fvertex-files%2F...?provider=vertex" # Delete curl -X DELETE "http://localhost:8080/v1/files/gs%3A%2F%2Fmy-bucket%2Fvertex-files%2F...?provider=vertex" # Download content curl "http://localhost:8080/v1/files/gs%3A%2F%2Fmy-bucket%2Fvertex-files%2F.../content?provider=vertex" ``` GCS bucket must be provided either in `StorageConfig.GCS.Bucket` or via the `gcs_bucket` extra param. An optional `gcs_prefix` scopes object keys within the bucket. ## Screenshots/Recordings N/A ## Breaking changes - [ ] Yes - [x] No ## Related issues N/A ## Security considerations - GCS requests are authenticated using the existing Vertex credential chain (`getAuthTokenSource`). Tokens are short-lived and refreshed automatically; stale token sources are evicted on refresh failure. - File IDs for Vertex are `gs://` URIs. Callers supplying arbitrary file IDs to retrieve/delete/content endpoints should validate that URIs reference expected buckets before forwarding to the API. ## 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 is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Vertex provider: full file support — upload (multipart & resumable with returned upload URL), list, retrieve, delete, and download. * UI: Vertex keys can be marked for batch API usage. * **Improvements** * File uploads may omit bytes; filename, content_type, and unknown form/query fields are preserved as extra params. * File IDs with special characters are percent-decoded. * Deletes are idempotent (missing objects treated as success). <!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary Implements full Vertex AI batch prediction support, replacing the previous stub implementations that returned `UnsupportedOperationError` for all batch operations. The Vertex AI Batch Prediction API uses GCS for input/output and `BatchPredictionJob` resources rather than OpenAI-style file IDs, requiring a dedicated mapping layer. ## Changes - **`BatchCreate`**: Accepts either a `gs://` input file URI or inline request items. Inline items are serialized to Vertex-format JSONL (with `custom_id` embedded in request labels via `bifrost_custom_id`) and uploaded to GCS before the job is submitted. The GCS output prefix is resolved from `extra_params["output_uri"]` or derived from `extra_params["gcs_bucket"]`/`["gcs_prefix"]`. Vertex-native fields (`modelParameters`, `labels`, `encryptionSpec`, etc.) are passed through via `extra_params`. - **`BatchList`**: Paginates across all configured keys using `SerialListHelper`, since batch jobs are scoped to a project/region. Each key's native Vertex `pageToken` is re-encoded into the Bifrost cursor. - **`BatchRetrieve`**: Tries each key in turn until the job is found, since a job ID is only resolvable within the project/region that created it. - **`BatchCancel`** / **`BatchDelete`**: Same multi-key fan-out pattern as retrieve. - **`BatchResults`**: Fetches the job to locate its GCS output directory, lists all `predictions-*.jsonl` files, downloads and parses each line, and recovers `custom_id` from the echoed request labels. - **`vertexJobStateToBatchStatus`**: Maps Vertex `JOB_STATE_*` values to Bifrost `BatchStatus` constants. - **`ToVertexBatchCreateRequest`**: Maps a Bifrost batch create request to a `VertexBatchCreateRequest`, stripping Bifrost control keys (`output_uri`, `gcs_bucket`, `gcs_prefix`, `job_name`) before forwarding `extra_params` to Vertex. - **`vertexConvertRequestsToJSONL`**: Converts inline `BatchRequestItem` entries to Vertex JSONL, injecting `custom_id` into each request's `labels` map without mutating the caller's data. - **GCS helpers** (`gcsListAllObjects`, `gcsDownloadObject`): Added to support paginated object listing and raw object download needed by `BatchResults`. - **Batch prediction types**: Added `vertexBatchPredictionJob`, `VertexBatchCreateRequest`, `vertexBatchJobListResponse`, `vertexBatchOutputLine`, and supporting config/stats structs. A regional Vertex key (e.g. `us-central1`) is required; `global` is explicitly rejected since the Batch Prediction API does not support it. ## Type of change - [ ] Bug fix - [x] Feature - [ ] Refactor - [ ] Documentation - [ ] Chore/CI ## Affected areas - [ ] Core (Go) - [ ] Transports (HTTP) - [x] Providers/Integrations - [ ] Plugins - [ ] UI (React) - [ ] Docs ## How to test ```sh go test ./core/providers/vertex/... ``` To exercise end-to-end: 1. Configure a Vertex key with a regional `VertexKeyConfig` (e.g. `us-central1`) and a GCS bucket accessible by the service account. 2. Call `BatchCreate` with either `input_file_id` (a `gs://` JSONL URI) or inline `requests`, and set `extra_params["gcs_bucket"]` or `extra_params["output_uri"]`. 3. Poll `BatchRetrieve` until the job reaches `completed`. 4. Call `BatchResults` to retrieve per-request responses with `custom_id` round-tripped correctly. 5. Verify `BatchList`, `BatchCancel`, and `BatchDelete` against jobs in the target project/region. ## Breaking changes - [ ] Yes - [x] No ## Related issues ## Security considerations - GCS and Vertex API calls are authenticated via the existing `gcsGetAuthHeader` helper (service account credentials from the key config). No credentials are logged or returned in responses. - The `bifrost_custom_id` label is user-supplied and echoed back from Vertex; callers should treat it as untrusted input if used downstream. ## 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 is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Full Vertex AI Batch Prediction: create, list, retrieve, cancel, delete, and fetch results with status/timestamp mapping, structured errors, display_name support, and GCS upload/download helpers. * Inline JSONL support: converts inline requests, injects/preserves custom IDs, stages to GCS, and parses prediction JSONL outputs. * Vertex-native HTTP routes for batch operations and response passthrough. * **Tests** * End-to-end Vertex batch integration tests, GCS staging helpers, and test config mappings. * **Chores** * Schema/config updates to enable Vertex batch workflows and request handling. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
… `hashicorp-vault`) as alternative to AES encryption for sensitive config fields (#4157) ## Summary Adds a vault backend integration layer to the configstore tables, enabling sensitive fields (API keys, credentials, tokens) to be stored in an external secret manager instead of AES-encrypted in the database. This introduces a new `vault` encryption status alongside the existing `plain_text` and `encrypted` statuses, with the vault implementation itself deferred to the enterprise layer via function pointer hooks. ## Changes - Introduced `EncryptionStatusVault = "vault"` as a third encryption status constant. - Added a `VaultHooks` struct in `encryption.go` with pluggable function pointers (`IsEnabled`, `Prefix`, `StoreString`, `ResolveString`, `Remove`) that the enterprise layer populates at startup. - Added helper functions (`vaultEnvVar`, `resolveVaultEnvVar`, `vaultString`, `resolveVaultString`) that guard against nil/empty values and missing hooks before delegating to vault operations. - Updated `BeforeSave` hooks across all configstore tables (`key`, `mcp`, `oauth`, `plugin`, `provider`, `sessions`, `temp_token`, `vectorstore`, `virtualkey`) to check vault first, fall through to AES encryption, and set the appropriate `EncryptionStatus`. - Updated `AfterFind` hooks to switch on `EncryptionStatus`, resolving vault references when `vault` and decrypting when `encrypted`, preserving backward compatibility with existing AES-encrypted rows. - Added `AfterDelete` hooks to all affected tables for best-effort vault secret cleanup when a row is deleted. - Added `VaultStoreConfig` struct to `config.go` and a `vault_store` key to `ConfigData`, allowing the config file to carry vault backend settings that the enterprise layer consumes. - Added an `initVault` no-op stub in `config.go` that logs when vault config is present but defers actual initialization to the enterprise layer. - Extended `config.schema.json` with a `vault_config` definition covering `aws-secrets-manager`, `gcp-secret-manager`, and `hashicorp-vault` backends, including all relevant credential and configuration fields. ## Type of change - [ ] Bug fix - [x] Feature - [ ] Refactor - [ ] Documentation - [ ] Chore/CI ## Affected areas - [x] Core (Go) - [x] Transports (HTTP) - [ ] Providers/Integrations - [ ] Plugins - [ ] UI (React) - [ ] Docs ## How to test ```sh go test ./framework/configstore/... go test ./transports/bifrost-http/... go build ./... ``` To validate vault path construction and hook dispatch, wire up stub implementations of `VaultHooks` in a test and perform create/read/delete operations on any configstore table. Confirm that: - With `VaultHooks.IsEnabled` returning `true`, `EncryptionStatus` is set to `vault` on save and secrets are passed to `StoreString`. - On `AfterFind`, `ResolveString` is called for rows with `EncryptionStatus = vault`. - On `AfterDelete`, `Remove` is called for each vaulted field path. - Rows previously saved with `EncryptionStatus = encrypted` continue to decrypt correctly via the AES path. **New config key:** ```json { "vault_store": { "enabled": true, "type": "aws-secrets-manager", "prefix": "bifrost", "aws": { "region": "us-east-1", "role_arn": "arn:aws:iam::123456789012:role/bifrost-vault" } } } ``` Supported types: `aws-secrets-manager`, `gcp-secret-manager`, `hashicorp-vault`. ## Breaking changes - [ ] Yes - [x] No Existing AES-encrypted rows are unaffected. Vault is only activated when `VaultHooks.IsEnabled` returns `true`, which requires the enterprise layer to populate the hooks. ## Security considerations - Sensitive fields (API keys, OAuth secrets, session tokens, credentials) are no longer written to the database in AES-encrypted form when vault is active; only vault reference strings are persisted. - Vault hook function pointers are package-level globals populated at startup; callers must ensure they are set before any database operations occur when vault is enabled. - `AfterDelete` cleanup is best-effort — vault removal errors are intentionally swallowed to avoid blocking row deletion. Operators should audit vault paths independently if hard deletion guarantees are required. - The `vault_store` config block may contain credential fields; these should be supplied via `env.VAR_NAME` references rather than inline values. ## 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 is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Optional external vault support for storing sensitive config with AWS/GCP/HashiCorp backends; runtime gate with graceful fallback to existing encryption. * Per-row vault-backed storage and retrieval for keys, providers, plugins, MCP, OAuth, sessions, tokens, vector stores, virtual keys, and per-user headers. * Best-effort automatic cleanup of vault secrets on row deletion and batch deletes. * **Documentation** * Config schema and loader updated with a top-level vault_store section and backend-specific options (optional prefix). * **Chores** * Bumped several indirect Go dependency versions. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
…publishing VK provider allowlist (#4221) ## Summary The routing allowlist published to `BifrostContextKeyRoutingAllowedProviders` previously included all providers on a virtual key regardless of their `allowed_models` / `blocked_models` configuration. This meant a downstream routing layer (load balancing, model-catalog resolution) could select a provider that the VK explicitly forbids for the requested model. This PR fixes that by filtering the allowlist against the model being routed before publishing it. ## Changes - Extracted the allowlist-publishing logic into a dedicated `publishRoutingAllowlist` method that filters each provider config against the resolved model using `AllowedModels.IsAllowed` / `BlacklistedModels.IsBlocked` before adding it to the allowed set. - Removed the previous inline allowlist construction in `PreRequestHook`, which unconditionally included every provider on the VK without any model-level filtering. - `publishRoutingAllowlist` is now called after routing rules have been applied (so the model is in its final post-routing state) for both the large-payload path (using `ParseModelString` on `LargePayloadMetadata.Model`) and the standard path (using `req.GetRequestFields()`). - An empty allowed slice continues to mean "no provider is permitted," preserving the existing fail-closed behaviour enforced by the empty-provider validation in `handleRequest`. - A `nil` virtual key is a no-op, so unauthenticated or VK-less requests are unaffected. ## Type of change - [x] Bug fix ## Affected areas - [x] Plugins ## How to test ```sh go test ./... ``` 1. Configure a virtual key with two providers where one provider has `allowed_models` that excludes the requested model. 2. Send a request for that model and confirm the excluded provider is never selected by the load balancer or model-catalog resolver. 3. Confirm that a request for a model permitted by both providers can still be routed to either. 4. Confirm that a request where no provider permits the model fails closed rather than falling through to a forbidden provider. ## Breaking changes - [ ] Yes - [x] No ## Related issues ## Security considerations This tightens provider selection enforcement on virtual keys. Previously, a model-level restriction on a VK provider config could be bypassed by a downstream routing layer picking that provider after governance failed to select one. That bypass path is now closed. ## 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 is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved consistency between routing rule application and provider allowlist enforcement for virtual keys with model restrictions, ensuring correct provider filtering is applied after routing decisions are made. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
…` method on the schema type (#4222) ## Summary Moves the `isModelBlockedByList` logic from a standalone function in the governance plugin's `utils.go` into a method (`IsBlocked`) on the `BlackList` type in the core schemas package, consolidating the blocked-model check closer to the type it operates on. ## Changes - Removed `isModelBlockedByList` and `blockedModelCandidates` helper functions from `plugins/governance/utils.go` - Replaced all call sites (`loadBalanceProvider`, `isModelAllowed`, `filterModelsForVirtualKey`) with `pc.BlacklistedModels.IsBlocked(model)`, delegating to the new method on `schemas.BlackList` - Removed the corresponding unit tests from `plugins/governance/blocklist_test.go`, as the logic and its tests now live with the `BlackList` type ## Type of change - [ ] Bug fix - [ ] Feature - [x] Refactor - [ ] Documentation - [ ] Chore/CI ## Affected areas - [ ] Core (Go) - [ ] Transports (HTTP) - [ ] Providers/Integrations - [x] Plugins - [ ] UI (React) - [ ] Docs ## How to test ```sh go test ./... ``` ## Breaking changes - [ ] Yes - [x] No ## Related issues ## Security considerations No security implications. This is a pure refactor with no behavioral changes to the blocklist matching logic. ## 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 is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Tests** * Removed blacklist model verification test suite. * **Refactor** * Streamlined blacklist validation across governance plugin components. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
…ectors (#4236) ## Summary The plugin tracing configuration sheet previously built its plugin list by merging a hardcoded built-in plugin list with custom plugins fetched from the config store. This meant enterprise plugins, auto-loaded plugins, and any plugin registered under a name different from its config key (e.g. `enterprise-prompts` instead of `prompts`) were silently missing from the list. As a result, users could not configure span filtering for those plugins, and any manually entered filter names could silently no-op. This PR replaces that approach with a single `/api/plugins/loaded` endpoint that returns the sanitized names of every plugin actually loaded at runtime — the exact names embedded in their trace spans — and uses that list throughout the tracing sheet and filter logic. ## Changes - Extracted `SanitizePluginSpanName` from `core/utils.go` into `core/schemas/span_filter.go` as an exported function so the same normalization logic is shared between span construction and span filtering. - Added `GetLoadedPluginNames()` to `Config`, `BifrostHTTPServer`, and the `PluginsLoader`/`ServerCallbacks` interfaces, returning deduplicated, sorted, sanitized plugin names for all currently loaded plugins. - Added a `GET /api/plugins/loaded` route backed by `getLoadedPlugins`, which returns the runtime plugin list. - Added a `getLoadedPlugins` RTK Query endpoint (`useGetLoadedPluginsQuery`) in the UI. - Replaced the built-in/custom split in `pluginTracingSheet.tsx` with a single flat list sourced from `useGetLoadedPluginsQuery`, removing the separate "Built-in Plugins" and "Custom Plugins" sections. - Added `TestSanitizePluginSpanName` and `TestSanitizedNameMatchesSpanExtraction` to lock the invariant that names used to build spans round-trip correctly through `PluginNameFromSpan`. - Updated the OTel and Datadog connector docs to clarify that plugin names in span filters must match the name shown in the tracing sheet, and that enterprise plugins like `enterprise-prompts` and `enterprise-governance` differ from their config keys. ## Type of change - [ ] Bug fix - [x] Feature - [ ] Refactor - [x] Documentation - [ ] Chore/CI ## Affected areas - [x] Core (Go) - [x] Transports (HTTP) - [ ] Providers/Integrations - [x] Plugins - [x] UI (React) - [x] Docs ## How to test ```sh # Core/Transports go test ./core/schemas/... ./transports/bifrost-http/... # UI cd ui pnpm i pnpm test pnpm build ``` 1. Start the gateway with a mix of built-in, enterprise, and custom plugins loaded. 2. Open the **Configure Plugin Tracing** sheet for an observability connector. 3. Verify the plugin list includes enterprise plugins (e.g. `enterprise-prompts`, `enterprise-governance`) and any auto-loaded plugins, not just the hardcoded built-in set. 4. Call `GET /api/plugins/loaded` directly and confirm the returned names match what appears in the sheet and in actual trace span names (`plugin.<name>.<stage>`). 5. Configure an `include` or `exclude` filter using a name from the sheet and verify spans are correctly filtered in the connected APM backend. ## Breaking changes - [x] Yes - [ ] No `PluginsLoader` and `ServerCallbacks` interfaces gain a `GetLoadedPluginNames() []string` method. Any external implementations of these interfaces must add this method. ## Related issues ## Security considerations The `/api/plugins/loaded` endpoint exposes the names of all loaded plugins. It should be protected by the same middleware chain as other `/api/plugins` routes, which it is via `ChainMiddlewares`. ## 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) - [ ] I verified the CI pipeline passes locally if applicable <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * API endpoint exposing currently loaded plugin names * Observability UI: unified "Plugins" list with improved initialization and select-all behavior * Consistent plugin name normalization so filter matching aligns with displayed plugin names * **Documentation** * Clarified plugin name guidance for span filtering; instructs copying exact names from the UI * **Tests** * Added tests for plugin name normalization and loaded-plugins endpoint <!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
Adds first-class support for the Claude Fable 5 / Mythos family (Fable 5, Mythos 5, Mythos Preview) in the Anthropic provider. These models share Opus 4.7+'s adaptive-only thinking surface and rejected sampling parameters, but have two additional constraints: `thinking:{type:"disabled"}` is rejected with a 400 (adaptive thinking is always on and cannot be disabled), and `speed:"fast"` is not supported. Without this change, requests to Fable/Mythos models would either 400 or silently send unsupported parameters.
## Changes
- Introduced `IsFableFamily(model string) bool` to detect `fable` and `mythos` model strings.
- Introduced `IsAdaptiveOnlyThinkingModel(model string) bool` as the union of `IsOpus47Plus` and `IsFableFamily`, used to gate adaptive-only thinking and sampling-parameter stripping (`temperature`, `top_p`, `top_k`). All prior `IsOpus47Plus` call sites for these gates are replaced with `IsAdaptiveOnlyThinkingModel`.
- When `Reasoning.MaxTokens` is nil (caller requests disabled thinking) and the model is Fable/Mythos, the `thinking` parameter is omitted entirely rather than sent as `{type:"disabled"}`, which would cause a 400.
- `speed:"fast"` is now only forwarded when `SupportsFastMode` returns true, preventing Fable/Mythos from receiving the unsupported parameter.
- `IsFableFamily` is wired into `SupportsEffortParameter`, `SupportsAdaptiveThinking`, `SupportsMidConversationSystem`, `ComputerUseGeneration`, `TextEditorGeneration`, and the dynamic web-search tool type selector so Fable/Mythos inherit the correct feature surface.
- Unit tests added for `IsFableFamily`, `IsAdaptiveOnlyThinkingModel`, and updated for `SupportsAdaptiveThinking`, `SupportsMidConversationSystem`, `SupportsFastMode`, `SupportsEffortParameter`, and `ComputerUseGeneration` to cover the new family.
- E2E Postman collection extended with a "Cross-Cut Round 30: Fable 5 / Mythos Feature Gating" group covering adaptive thinking, effort levels, structured outputs, task budgets, computer-use tools, web search, mid-conversation system messages, sampling-param stripping, disabled-thinking omission, and fast-mode stripping.
## Type of change
- [x] Bug fix
- [x] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI
## Affected areas
- [x] Core (Go)
- [ ] Transports (HTTP)
- [x] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs
## How to test
```sh
go test ./core/providers/anthropic/...
```
Key test functions to verify:
- `TestIsFableFamily` — confirms `fable`/`mythos` strings are detected and Opus/Sonnet/Haiku are not.
- `TestIsAdaptiveOnlyThinkingModel` — confirms the union gate covers Opus 4.7+ and Fable/Mythos but not Opus 4.6/Sonnet 4.6.
- `TestSupportsAdaptiveThinking`, `TestSupportsMidConversationSystem`, `TestSupportsFastMode`, `TestSupportsEffortParameter`, `TestComputerUseGeneration` — all updated with Fable/Mythos cases.
For live validation, run the "Cross-Cut Round 30: Fable 5 / Mythos Feature Gating" collection in the E2E Postman harness against an account with Fable access. Tests are guarded on `code < 400` so they pass gracefully without access.
## Breaking changes
- [x] No
## Related issues
## Security considerations
None. No new auth surfaces, secrets, or PII handling introduced.
## Checklist
- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit
* **New Features**
* Added support for Claude Fable/Mythos family and explicit adaptive-only thinking handling across providers; tool selection updated for Fable variants.
* **Behavior Changes**
* Adaptive-only models now omit sampling params (temperature/top_p/top_k) and use adaptive thinking paths; disabled-reasoning for Fable/Mythos omits the thinking field. Fast-mode/speed and top_k passthrough are applied only when supported. Default thinking.display fallback moved to adaptive-only gating.
* **Tests**
* Expanded unit and e2e tests covering Fable/Mythos gating, reasoning, sampling, tools, and mid-conversation system placement.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
) * changelogs * fix(bedrock): fill Cohere embed/rerank usage from response header Bedrock's Cohere embed and rerank models don't return token counts in the response body, so Usage came back nil and logs/cost showed "-" for tokens. Bedrock does send the count in the X-Amzn-Bedrock-Input-Token-Count response header, and we already capture provider response headers in completeRequest and completeAgentRuntimeRequest. Read that header (case-insensitive) and backfill Usage with the input token count when the body didn't provide it. Titan embeddings are unaffected since they already populate Usage from the body. Added a unit test for the header parsing helper. Closes #3917 --------- Signed-off-by: Akshay Deo <akshay@akshaydeo.com> Co-authored-by: akshaydeo <akshay@akshaydeo.com>
The Embedding method unconditionally calls getAuthTokenSource(key), which attempts google.FindDefaultCredentials(). This fails in environments where GCP auth is provided externally via context headers (e.g. Workload Identity Federation) rather than Application Default Credentials. Other methods (ChatCompletion, Responses, ResponsesStream) already check key.Value and use it as an API key query parameter when set, allowing external auth via SetExtraHeaders to take effect. This commit applies the same pattern to Embedding for consistency. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
## Summary Fixes a minor whitespace/formatting inconsistency in the Bedrock embedding response struct definition. ## Changes - Aligned the inline comment on the `Uint8` field to match the formatting style of the surrounding `Ubinary` field comment, removing an extra trailing space. ## Type of change - [ ] Bug fix - [ ] Feature - [ ] Refactor - [ ] Documentation - [x] Chore/CI ## Affected areas - [ ] Core (Go) - [ ] Transports (HTTP) - [x] Providers/Integrations - [ ] Plugins - [ ] UI (React) - [ ] Docs ## How to test ```sh go test ./... ``` ## Screenshots/Recordings N/A ## Breaking changes - [ ] Yes - [x] No ## Related issues N/A ## 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 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Resolved a compilation error in the embedding service to ensure proper functionality. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
6d0f0f6 to
6d708e1
Compare
|
Superseded by the consolidated alerting stack — see #4435. Closing in favor of the rebuilt stack on latest |

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 guidelinesSummary by CodeRabbit
Release Notes
New Features
Database