feat: Only show available models in selector (#238)

This commit is contained in:
Kevin Lin
2025-12-16 11:31:54 -08:00
committed by GitHub
parent 90a66ee94b
commit ea175e1dbb
3 changed files with 260 additions and 26 deletions

View File

@@ -0,0 +1,101 @@
import { getClient } from "./client";
const CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
type CacheEntry = {
handles: Set<string>;
fetchedAt: number;
};
let cache: CacheEntry | null = null;
let inflight: Promise<CacheEntry> | null = null;
function isFresh(now = Date.now()) {
return cache !== null && now - cache.fetchedAt < CACHE_TTL_MS;
}
export type AvailableModelHandlesResult = {
handles: Set<string>;
source: "cache" | "network";
fetchedAt: number;
};
export function clearAvailableModelsCache() {
cache = null;
}
export function getAvailableModelsCacheInfo(): {
hasCache: boolean;
isFresh: boolean;
fetchedAt: number | null;
ageMs: number | null;
ttlMs: number;
} {
const now = Date.now();
return {
hasCache: cache !== null,
isFresh: isFresh(now),
fetchedAt: cache?.fetchedAt ?? null,
ageMs: cache ? now - cache.fetchedAt : null,
ttlMs: CACHE_TTL_MS,
};
}
async function fetchFromNetwork(): Promise<CacheEntry> {
const client = await getClient();
const modelsList = await client.models.list();
const handles = new Set(
modelsList.map((m) => m.handle).filter((h): h is string => !!h),
);
return { handles, fetchedAt: Date.now() };
}
export async function getAvailableModelHandles(options?: {
forceRefresh?: boolean;
}): Promise<AvailableModelHandlesResult> {
const forceRefresh = options?.forceRefresh === true;
const now = Date.now();
if (!forceRefresh && isFresh(now) && cache) {
return {
handles: cache.handles,
source: "cache",
fetchedAt: cache.fetchedAt,
};
}
if (!forceRefresh && inflight) {
const entry = await inflight;
return {
handles: entry.handles,
source: "network",
fetchedAt: entry.fetchedAt,
};
}
inflight = fetchFromNetwork()
.then((entry) => {
cache = entry;
return entry;
})
.finally(() => {
inflight = null;
});
const entry = await inflight;
return {
handles: entry.handles,
source: "network",
fetchedAt: entry.fetchedAt,
};
}
/**
* Best-effort prefetch to warm the cache (no throw).
* This is intentionally fire-and-forget.
*/
export function prefetchAvailableModelHandles(): void {
void getAvailableModelHandles().catch(() => {
// Ignore failures; UI will handle errors on-demand.
});
}