From e1274a6dc3ed44feadb9bfd4b9d9b8a4c1104f11 Mon Sep 17 00:00:00 2001 From: Ari Webb Date: Thu, 12 Feb 2026 12:21:27 -0800 Subject: [PATCH] fix: refresh does refresh on all byok (#931) --- src/agent/available-models.ts | 60 +++++++++++++++++++++++++ src/cli/App.tsx | 2 +- src/tests/cli/statusline-config.test.ts | 1 - 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/src/agent/available-models.ts b/src/agent/available-models.ts index f456a55..efad0f5 100644 --- a/src/agent/available-models.ts +++ b/src/agent/available-models.ts @@ -1,3 +1,4 @@ +import { debugWarn } from "../utils/debug"; import { getClient } from "./client"; const CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes @@ -42,6 +43,59 @@ export function getAvailableModelsCacheInfo(): { }; } +/** + * Provider response from /v1/providers/ endpoint + */ +type Provider = { + id: string; + name: string; + provider_type: string; + provider_category?: "base" | "byok" | null; +}; + +/** + * Refresh BYOK providers to get the latest models from their APIs. + * This calls PATCH /v1/providers/{provider_id}/refresh for each BYOK provider. + * Errors are logged but don't fail the overall refresh (best-effort). + */ +async function refreshByokProviders(): Promise { + const client = await getClient(); + + try { + // List all providers + const providers = await client.get("/v1/providers/"); + + // Filter to BYOK providers only + const byokProviders = providers.filter( + (p) => p.provider_category === "byok", + ); + + // Refresh each BYOK provider in parallel (best-effort, don't fail on errors) + await Promise.allSettled( + byokProviders.map(async (provider) => { + try { + await client.patch(`/v1/providers/${provider.id}/refresh`); + } catch (error) { + // Log but don't throw - refresh is best-effort + debugWarn( + "available-models", + `Failed to refresh provider ${provider.name} (${provider.id}):`, + error, + ); + } + }), + ); + } catch (error) { + // If we can't list providers, just log and continue + // This might happen on self-hosted servers without the providers endpoint + debugWarn( + "available-models", + "Failed to list providers for refresh:", + error, + ); + } +} + async function fetchFromNetwork(): Promise { const client = await getClient(); const modelsList = await client.models.list(); @@ -81,6 +135,12 @@ export async function getAvailableModelHandles(options?: { }; } + // When forceRefresh is true, first refresh BYOK providers to get latest models + // This matches the behavior in ADE (letta-cloud) where refresh is called before listing models + if (forceRefresh) { + await refreshByokProviders(); + } + inflight = fetchFromNetwork() .then((entry) => { cache = entry; diff --git a/src/cli/App.tsx b/src/cli/App.tsx index bf0593b..d1ebe5e 100644 --- a/src/cli/App.tsx +++ b/src/cli/App.tsx @@ -1265,7 +1265,7 @@ export default function App({ }, [llmConfig]); const [currentModelId, setCurrentModelId] = useState(null); // Full model handle for API calls (e.g., "anthropic/claude-sonnet-4-5-20251101") - const [currentModelHandle, setCurrentModelHandle] = useState( + const [_currentModelHandle, setCurrentModelHandle] = useState( null, ); // Derive agentName from agentState (single source of truth) diff --git a/src/tests/cli/statusline-config.test.ts b/src/tests/cli/statusline-config.test.ts index 6a2757f..96384ed 100644 --- a/src/tests/cli/statusline-config.test.ts +++ b/src/tests/cli/statusline-config.test.ts @@ -8,7 +8,6 @@ import { isStatusLineDisabled, MAX_STATUS_LINE_TIMEOUT_MS, MIN_STATUS_LINE_DEBOUNCE_MS, - MIN_STATUS_LINE_INTERVAL_MS, normalizeStatusLineConfig, resolveStatusLineConfig, } from "../../cli/helpers/statusLineConfig";