feat(coding-agent): add model catalog refresh flag

This commit is contained in:
Armin Ronacher
2026-07-16 12:46:49 +02:00
parent 2be9efa19c
commit 97f9978fa6
13 changed files with 148 additions and 19 deletions
+1
View File
@@ -17,6 +17,7 @@
- Added neutral auth-flow information/link events and provider-owned Amazon Bedrock and Google Vertex AI credential selection flows.
- Added `ModelsStore` with an in-memory default for restoring and persisting dynamic provider catalogs.
- Added the dynamic Radius `pi-messages` gateway provider with OAuth and credential-specific catalog refresh.
- Added `Models.refresh({ force: true })` to let providers bypass freshness checks for explicit refreshes.
### Changed
+1 -1
View File
@@ -1047,7 +1047,7 @@ if (result.aborted) console.log('refresh cancelled');
for (const [provider, error] of result.errors) console.error(provider, error);
```
Use `models.refresh({ allowNetwork: false })` to restore persisted catalogs without network access. Model reads stay synchronous and return the last restored or refreshed list.
Use `models.refresh({ allowNetwork: false })` to restore persisted catalogs without network access, or `models.refresh({ force: true })` to bypass provider freshness checks. Model reads stay synchronous and return the last restored or refreshed list.
Custom models can carry `headers` (e.g. proxies behind bot detection) and `compat` flags. `Models.getAuth(model)` includes those model headers, and stream methods merge them before explicit request headers and `transformHeaders`. See [OpenAI Compatibility Settings](#openai-compatibility-settings).
+11 -1
View File
@@ -38,11 +38,15 @@ export interface RefreshModelsContext {
store: ProviderModelsStore;
/** False during offline/cache-only initialization. */
allowNetwork: boolean;
/** Bypass provider freshness checks and fetch immediately when network access is allowed. */
force?: boolean;
signal?: AbortSignal;
}
export interface ModelsRefreshOptions {
allowNetwork?: boolean;
/** Bypass provider freshness checks and fetch immediately when network access is allowed. */
force?: boolean;
signal?: AbortSignal;
}
@@ -290,7 +294,13 @@ class ModelsImpl implements MutableModels {
stored = await this.readCredential(provider.id);
const credential = await this.resolveRefreshCredential(provider, stored, allowNetwork, options.signal);
if (!credential) return;
await provider.refreshModels({ credential, store, allowNetwork, signal: options.signal });
await provider.refreshModels({
credential,
store,
allowNetwork,
force: options.force,
signal: options.signal,
});
} catch (error) {
if (!options.signal?.aborted) {
errors.set(
+5 -2
View File
@@ -283,8 +283,9 @@ describe("Models runtime", () => {
expect(offline.getModel("dynamic", "fetched")).toBeDefined();
});
it("passes effective API-key credentials and skips unconfigured providers", async () => {
it("passes effective API-key credentials and refresh options while skipping unconfigured providers", async () => {
let effectiveCredential: unknown;
let forceRefresh: boolean | undefined;
let unconfiguredRefreshes = 0;
const models = createModels();
models.setProvider(
@@ -293,6 +294,7 @@ describe("Models runtime", () => {
auth: { apiKey: envKeyAuth("ambient-key") },
refreshModels: async (context) => {
effectiveCredential = context.credential;
forceRefresh = context.force;
},
}),
);
@@ -306,8 +308,9 @@ describe("Models runtime", () => {
}),
);
await models.refresh();
await models.refresh({ force: true });
expect(effectiveCredential).toEqual({ type: "api_key", key: "ambient-key", env: undefined });
expect(forceRefresh).toBe(true);
expect(unconfiguredRefreshes).toBe(0);
});