refactor: replace aggregated model discovery shims with per-key live cache fanout via OnKeyAdded/Updated/Deleted - #4038
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:
📝 WalkthroughWalkthroughRefactors model-catalog refresh to be key-driven: adds UpsertLiveFromResponse, key lifecycle callbacks, routes handler key events to callbacks, introduces per-key parallel refresh primitives, updates bootstrap and provider reload flows, and decouples pricing reload from model listing. ChangesKey-Driven Model Catalog Refresh
Sequence Diagram(s)sequenceDiagram
participant ProviderHTTPHandler
participant ModelsManager
participant BifrostHTTPServer
participant ModelCatalog
ProviderHTTPHandler->>ModelsManager: OnKeyAdded(ctx, provider, key)
ModelsManager->>BifrostHTTPServer: Trigger RefreshLiveModelsForProvider(provider, keys)
BifrostHTTPServer->>ModelCatalog: FetchAndStoreLiveForKey(provider, keyID) (filtered + unfiltered)
ModelCatalog->>ModelCatalog: UpsertLiveFromResponse(provider, keyID, unfiltered, resp)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
live model cache store and port keyconfig regression tests for alias/model isolation
#4034
|
|
4ece5ee to
e0da1a4
Compare
0872473 to
e78233f
Compare
Confidence Score: 5/5Safe to merge; the refactor is well-structured with no behavioral regressions in the changed code paths. All changed production paths are logically correct: keyconfig seeding, per-key live cache fanout, lifecycle callbacks, and the keyless/non-keyless guards are consistent. The only concern is two governance resolver tests that were deleted instead of updated, leaving wildcard + opaque-provider blacklist ordering without direct coverage — but the underlying logic is unchanged. plugins/governance/resolver_test.go — two behavioral tests were removed rather than updated to the new catalog API. Important Files Changed
Reviews (13): Last reviewed commit: "feat: wire up modelcatalog composer" | Re-trigger Greptile |
e78233f to
8056692
Compare
e0da1a4 to
489baca
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 `@transports/bifrost-http/server/server.go`:
- Around line 908-922: RefreshLiveModelsForProvider launches an unbounded
goroutine per key causing spikes; limit concurrency by using a bounded worker
pool/semaphore (e.g., a buffered channel or worker goroutines) when iterating
keys, acquire a token before calling FetchAndStoreLiveForKey and release it
after the call, still using the existing sync.WaitGroup to wait for completion;
update RefreshLiveModelsForProvider to use this semaphore/worker pattern
(referencing RefreshLiveModelsForProvider and FetchAndStoreLiveForKey) so at
most N concurrent key refreshes run (choose a sensible default like 10 or make
it configurable).
- Around line 1573-1578: The loop unconditionally calls
RefreshLiveModelsForProvider for providers with zero keys, causing an unintended
keyless fallback; update the provider loop to skip calling
s.RefreshLiveModelsForProvider when providerConfig.Keys is empty and the
provider is not declared keyless (use the provider config flag corresponding to
custom_provider_config.is_key_less, e.g., providerConfig.IsKeyLess or similar).
In practice, before wg.Add/starting the goroutine, check if
len(providerConfig.Keys) == 0 && !providerConfig.IsKeyLess then continue;
otherwise proceed to spawn the goroutine calling
s.RefreshLiveModelsForProvider(ctx, provider, providerConfig.Keys).
- Around line 930-970: FetchAndStoreLiveForKey always calls ListModelsRequest
even when a provider has custom_provider_config.allowed_requests.list_models ==
false; before starting the goroutines in FetchAndStoreLiveForKey, check the
provider's custom config (custom_provider_config.allowed_requests.list_models)
from the server config and short-circuit/return (or skip spawning both
ListModelsRequest goroutines) when list_models is disabled for that provider so
no ListModelsRequest() calls are issued; update code paths around
FetchAndStoreLiveForKey, the two goroutines that call
s.Client.ListModelsRequest, and any helper used to read provider configs (e.g.,
the server config accessors) to perform this check.
🪄 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: b2534ba7-40a5-41ae-a47c-5e1ffc97402d
📒 Files selected for processing (8)
framework/modelcatalog/models.goframework/modelcatalog/pool.goframework/modelcatalog/shims.goplugins/governance/httptransportprehook_test.gotransports/bifrost-http/handlers/provider_keys.gotransports/bifrost-http/handlers/providers.gotransports/bifrost-http/handlers/providers_test.gotransports/bifrost-http/server/server.go
💤 Files with no reviewable changes (1)
- framework/modelcatalog/shims.go
489baca to
d298565
Compare
99af5b8 to
8934e2e
Compare
3e436ba to
c6a47c6
Compare
400b52e to
e77b1f7
Compare
c6a47c6 to
40bebde
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/modelcatalog/pool.go`:
- Around line 19-21: The UpsertLiveFromResponse method can clear the cache when
resp is nil because extractModelIDs returns nil; add an early nil guard in
ModelCatalog.UpsertLiveFromResponse to return immediately if resp is nil (and
optionally if resp.Models == nil) before calling mc.live.Upsert so you don't
pass a nil/empty slice into mc.live.Upsert; reference the function
ModelCatalog.UpsertLiveFromResponse, the helper extractModelIDs, and the call
mc.live.Upsert to locate where to add the check.
In `@transports/bifrost-http/server/server.go`:
- Around line 950-999: The single shared bfCtx is mutated by SetValue and then
reused concurrently by both goroutines—create a fresh BifrostContext for each
goroutine instead: inside each goroutine call schemas.NewBifrostContext(ctx,
time.Now().Add(15*time.Second)), set the same flags with
SetValue(schemas.BifrostContextKeySkipPluginPipeline, true) and
SetValue(schemas.BifrostContextKeyValidateKeys, true), defer Cancel() on that
per-goroutine context, and pass that new context to s.Client.ListModelsRequest;
keep the rest of the logic (keyIDPtr, UpsertLiveFromResponse, updateKeyStatus)
unchanged and ensure both goroutines use independent contexts to avoid
shared-state races involving NewBifrostContext and SetValue.
- Around line 630-644: The code is silently ignoring errors from
GetProviderKeysRaw which causes SetKeyConfigForProvider(provider, nil) and
subsequent InvalidateLiveProvider/skip discovery; update ReloadProvider and the
three OnKey* callbacks to capture the error returned by
s.Config.GetProviderKeysRaw(provider) (e.g. inMemoryKeys, err := ...), and if
err != nil return or propagate that error (or handle it explicitly) instead of
treating keys as empty—do not call SetKeyConfigForProvider(provider, nil) or
call InvalidateLiveProvider when the lookup failed; only call
SetKeyConfigForProvider/InvalidateLiveProvider/RefreshLiveModelsForProvider when
GetProviderKeysRaw succeeds so the catalog stays in sync.
🪄 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: 6d79d08d-1908-4082-9c68-b2d8878ca592
📒 Files selected for processing (8)
framework/modelcatalog/models.goframework/modelcatalog/pool.goplugins/governance/httptransportprehook_test.goplugins/governance/resolver_test.gotransports/bifrost-http/handlers/provider_keys.gotransports/bifrost-http/handlers/providers.gotransports/bifrost-http/handlers/providers_test.gotransports/bifrost-http/server/server.go
💤 Files with no reviewable changes (1)
- plugins/governance/resolver_test.go
40bebde to
b2fc2f9
Compare
e77b1f7 to
1f7578f
Compare
b2fc2f9 to
ab12e97
Compare
1f7578f to
6300061
Compare
ab12e97 to
c9598f0
Compare
4ec2730 to
b6de129
Compare
c9598f0 to
ee7e985
Compare
b6de129 to
b66eb67
Compare
ee7e985 to
08d23fc
Compare
08d23fc to
827a049
Compare
b66eb67 to
215242f
Compare
Merge activity
|

Summary
Replaces the single aggregated per-provider live model cache with a per-(provider, keyID) cache. Previously, all keys for a provider were merged into a single live entry keyed by
"". Now each key gets its own entry, and key lifecycle events (OnKeyAdded,OnKeyUpdated,OnKeyDeleted) trigger targeted fetches or invalidations for only the affected key rather than re-fetching all keys for the provider.Changes
shims.goand its deprecatedUpsertModelDataForProvider,UpsertUnfilteredModelDataForProvider, andDeleteModelDataForProvidermethods. TheextractModelIDshelper was moved inline.UpsertLiveFromResponsetoModelCatalogas the canonical way to push aBifrostListModelsResponseinto the live cache for a specific key.OnKeyAdded,OnKeyUpdated, andOnKeyDeletedto theServerCallbacksinterface andModelsManagerinterface, with implementations inBifrostHTTPServer. Key create/update/delete handlers inprovider_keys.gonow call these instead of the oldattemptModelDiscovery.populateModelPoolWithListModelswithRefreshLiveModelsForProvider(fans out per-key in parallel) andFetchAndStoreLiveForKey(issues filtered + unfiltered list-models for a single key concurrently).ReplaceKeyConfigto seed the full keyconfig snapshot before fanning out per-provider live fetches.ReloadProvidernow callsSetKeyConfigForProvider+InvalidateLiveProvider+RefreshLiveModelsForProviderinstead of re-running the full aggregated list-models flow.RemoveProvidernow callsInvalidateLiveProvider+RemoveKeyConfigForProvider.ForceReloadPricingandReloadPricingFromDBAndPopulateModelPoolno longer trigger a list-models refresh — pricing reload is now pricing-only.""sentinel for their live cache entry. AnisKeylessProviderhelper centralizes that check.modelcatalog.NewTestCatalog(nil)instead of&modelcatalog.ModelCatalog{}, andmockModelsManagerupdated to implement the newOnKey*methods.Type of change
Affected areas
How to test
go version go test ./...Verify that adding, updating, and deleting a provider key triggers only the expected number of list-models calls (2 per key rather than 2×N). Confirm that
ReloadProvidercorrectly invalidates stale entries and re-fetches only for the current key set.Screenshots/Recordings
N/A
Breaking changes
ServerCallbacksandModelsManagerinterfaces have three new required methods:OnKeyAdded,OnKeyUpdated, andOnKeyDeleted. Any external implementations of these interfaces must add these methods. The deprecated shim methods (UpsertModelDataForProvider,UpsertUnfilteredModelDataForProvider,DeleteModelDataForProvider) have been removed.Related issues
N/A
Security considerations
No new auth, secrets, or PII handling introduced. Key validation (
BifrostContextKeyValidateKeys) is preserved inFetchAndStoreLiveForKey.Checklist
docs/contributing/README.mdand followed the guidelinesSummary by CodeRabbit
New Features
Refactor
Behavior Changes
Tests