@oh-my-pi/pi-catalog 16.2.8 → 16.2.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -80,7 +80,9 @@ export const CATALOG_PROVIDERS = [
80
80
  {
81
81
  id: "anthropic",
82
82
  defaultModel: "claude-opus-4-8",
83
+ envVars: ["ANTHROPIC_API_KEY"],
83
84
  createModelManagerOptions: (config: ModelManagerConfig) => anthropicModelManagerOptions(config),
85
+ catalogDiscovery: { label: "Anthropic" },
84
86
  },
85
87
  {
86
88
  id: "azure",
@@ -20,6 +20,25 @@ import {
20
20
  import { createBundledReferenceMap, createReferenceResolver, toModelSpec } from "./bundled-references";
21
21
 
22
22
  const MODELS_DEV_URL = "https://models.dev/api.json";
23
+
24
+ /**
25
+ * Uses a cancellable timer rather than the native abort-timeout helper so
26
+ * successful fast discovery requests do not leave armed timeout signals for
27
+ * concurrent GC to trip over later.
28
+ */
29
+ async function withCatalogDiscoveryTimeout<T>(timeoutMs: number, run: (signal: AbortSignal) => Promise<T>): Promise<T> {
30
+ const controller = new AbortController();
31
+ const timer = setTimeout(
32
+ () => controller.abort(new DOMException("The operation timed out.", "TimeoutError")),
33
+ timeoutMs,
34
+ );
35
+ try {
36
+ return await run(controller.signal);
37
+ } finally {
38
+ clearTimeout(timer);
39
+ }
40
+ }
41
+
23
42
  const ANTHROPIC_BASE_URL = "https://api.anthropic.com/v1";
24
43
  const ANTHROPIC_OAUTH_BETA =
25
44
  "claude-code-20250219,oauth-2025-04-20,interleaved-thinking-2025-05-14,redact-thinking-2026-02-12,context-management-2025-06-27,prompt-caching-scope-2026-01-05,mid-conversation-system-2026-04-07,advanced-tool-use-2025-11-20,effort-2025-11-24,extended-cache-ttl-2025-04-11";
@@ -156,11 +175,23 @@ function buildAnthropicReferenceMap(
156
175
  * first-party `/v1/models` endpoint but that models.dev has not catalogued yet.
157
176
  * Seeded into model generation so the bundled catalog is never gated on
158
177
  * models.dev's update cadence; deduped behind upstream catalog / models.dev
159
- * entries once those appear. Token limits and pricing are pinned
160
- * authoritatively in `applyAnthropicCatalogPolicy`, and `thinking` is re-baked
178
+ * entries once those appear. Token limits and pricing are pinned either directly or
179
+ * in `applyAnthropicCatalogPolicy`, and `thinking` is re-baked
161
180
  * by the generator's policy pass (scripts/generated-policies.ts).
162
181
  */
163
182
  export const ANTHROPIC_CURATED_FALLBACK_MODELS: readonly ModelSpec<"anthropic-messages">[] = [
183
+ {
184
+ id: "claude-sonnet-5",
185
+ name: "Claude Sonnet 5",
186
+ api: "anthropic-messages",
187
+ provider: "anthropic",
188
+ baseUrl: "https://api.anthropic.com",
189
+ reasoning: true,
190
+ input: ["text", "image"],
191
+ cost: { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
192
+ contextWindow: 1_000_000,
193
+ maxTokens: 128_000,
194
+ },
164
195
  {
165
196
  id: "claude-fable-5",
166
197
  name: "Claude Fable 5",
@@ -2339,34 +2370,40 @@ export async function fetchLmStudioNativeModelMetadata(
2339
2370
  options?: LmStudioNativeModelMetadataOptions,
2340
2371
  ): Promise<Map<string, LmStudioNativeModelMetadata> | null> {
2341
2372
  const nativeBaseUrl = toLmStudioNativeBaseUrl(baseUrl);
2342
- try {
2343
- const response = await fetchImpl(`${nativeBaseUrl}/api/v0/models`, {
2344
- method: "GET",
2345
- headers: { Accept: "application/json", ...(options?.headers ?? {}) },
2346
- signal: options?.signal ?? AbortSignal.timeout(LM_STUDIO_NATIVE_METADATA_TIMEOUT_MS),
2347
- });
2348
- if (!response.ok) {
2349
- return null;
2350
- }
2351
- const payload = await response.json();
2352
- if (!isRecord(payload) || !Array.isArray(payload.data)) {
2353
- return null;
2354
- }
2355
- const metadata = new Map<string, LmStudioNativeModelMetadata>();
2356
- for (const entry of payload.data) {
2357
- if (!isRecord(entry) || typeof entry.id !== "string" || entry.id.length === 0) {
2358
- continue;
2359
- }
2360
- const contextWindow = getLmStudioNativeContextWindow(entry);
2361
- metadata.set(entry.id, {
2362
- input: getLmStudioNativeInput(entry),
2363
- ...(contextWindow === undefined ? {} : { contextWindow }),
2373
+ const fetchMetadata = async (signal?: AbortSignal): Promise<Map<string, LmStudioNativeModelMetadata> | null> => {
2374
+ try {
2375
+ const response = await fetchImpl(`${nativeBaseUrl}/api/v0/models`, {
2376
+ method: "GET",
2377
+ headers: { Accept: "application/json", ...(options?.headers ?? {}) },
2378
+ signal,
2364
2379
  });
2380
+ if (!response.ok) {
2381
+ return null;
2382
+ }
2383
+ const payload = await response.json();
2384
+ if (!isRecord(payload) || !Array.isArray(payload.data)) {
2385
+ return null;
2386
+ }
2387
+ const metadata = new Map<string, LmStudioNativeModelMetadata>();
2388
+ for (const entry of payload.data) {
2389
+ if (!isRecord(entry) || typeof entry.id !== "string" || entry.id.length === 0) {
2390
+ continue;
2391
+ }
2392
+ const contextWindow = getLmStudioNativeContextWindow(entry);
2393
+ metadata.set(entry.id, {
2394
+ input: getLmStudioNativeInput(entry),
2395
+ ...(contextWindow === undefined ? {} : { contextWindow }),
2396
+ });
2397
+ }
2398
+ return metadata;
2399
+ } catch {
2400
+ return null;
2365
2401
  }
2366
- return metadata;
2367
- } catch {
2368
- return null;
2402
+ };
2403
+ if (options?.signal !== undefined) {
2404
+ return fetchMetadata(options.signal);
2369
2405
  }
2406
+ return withCatalogDiscoveryTimeout(LM_STUDIO_NATIVE_METADATA_TIMEOUT_MS, fetchMetadata);
2370
2407
  }
2371
2408
 
2372
2409
  export interface LmStudioModelManagerConfig {
@@ -2857,6 +2894,7 @@ export interface FetchLiteLLMRichModelsOptions<TApi extends Api> {
2857
2894
  headers?: Record<string, string>;
2858
2895
  fetch?: FetchImpl;
2859
2896
  signal?: AbortSignal;
2897
+ timeoutMs?: number;
2860
2898
  referenceResolver?: (modelId: string) => ModelSpec<TApi> | undefined;
2861
2899
  }
2862
2900
 
@@ -3012,6 +3050,7 @@ async function fetchLiteLLMRichEndpoint<TApi extends Api>(
3012
3050
  options: FetchLiteLLMRichModelsOptions<TApi>,
3013
3051
  managementBaseUrl: string,
3014
3052
  runtimeBaseUrl: string,
3053
+ signal?: AbortSignal,
3015
3054
  ): Promise<ModelSpec<TApi>[] | null> {
3016
3055
  const fetchImpl = options.fetch ?? globalThis.fetch;
3017
3056
  const requestHeaders: Record<string, string> = {
@@ -3026,7 +3065,7 @@ async function fetchLiteLLMRichEndpoint<TApi extends Api>(
3026
3065
  response = await fetchImpl(`${managementBaseUrl}${endpoint}`, {
3027
3066
  method: "GET",
3028
3067
  headers: requestHeaders,
3029
- signal: options.signal,
3068
+ signal,
3030
3069
  });
3031
3070
  } catch {
3032
3071
  return null;
@@ -3065,13 +3104,19 @@ export async function fetchLiteLLMRichModels<TApi extends Api>(
3065
3104
  if (!managementBaseUrl || !runtimeBaseUrl) {
3066
3105
  return null;
3067
3106
  }
3068
- for (const endpoint of LITELLM_RICH_ENDPOINTS) {
3069
- const models = await fetchLiteLLMRichEndpoint(endpoint, options, managementBaseUrl, runtimeBaseUrl);
3070
- if (models) {
3071
- return models;
3107
+ const fetchModels = async (signal?: AbortSignal): Promise<ModelSpec<TApi>[] | null> => {
3108
+ for (const endpoint of LITELLM_RICH_ENDPOINTS) {
3109
+ const models = await fetchLiteLLMRichEndpoint(endpoint, options, managementBaseUrl, runtimeBaseUrl, signal);
3110
+ if (models) {
3111
+ return models;
3112
+ }
3072
3113
  }
3114
+ return null;
3115
+ };
3116
+ if (options.signal !== undefined) {
3117
+ return fetchModels(options.signal);
3073
3118
  }
3074
- return null;
3119
+ return options.timeoutMs !== undefined ? withCatalogDiscoveryTimeout(options.timeoutMs, fetchModels) : fetchModels();
3075
3120
  }
3076
3121
 
3077
3122
  export function litellmModelManagerOptions(
@@ -3096,7 +3141,7 @@ export function litellmModelManagerOptions(
3096
3141
  apiKey,
3097
3142
  fetch: config?.fetch,
3098
3143
  referenceResolver: resolveReference,
3099
- signal: AbortSignal.timeout(10_000),
3144
+ timeoutMs: 10_000,
3100
3145
  });
3101
3146
  if (richModels && richModels.length > 0) {
3102
3147
  return richModels;
@@ -3146,7 +3191,7 @@ export function vllmModelManagerOptions(config?: VllmModelManagerConfig): ModelM
3146
3191
  };
3147
3192
  },
3148
3193
  fetch: config?.fetch,
3149
- signal: AbortSignal.timeout(VLLM_DISCOVERY_TIMEOUT_MS),
3194
+ timeoutMs: VLLM_DISCOVERY_TIMEOUT_MS,
3150
3195
  }),
3151
3196
  };
3152
3197
  }
@@ -953,8 +953,8 @@ export function collapseBuiltModelVariants<TApi extends Api>(models: readonly Mo
953
953
  const collapsed = collapseEffortVariantsAcrossProviders(models);
954
954
  const inputRefs = new Set<Model<TApi>>(models);
955
955
  return collapsed.map(model =>
956
- // Resolved compat re-fed as override config resolves to itself.
957
- inputRefs.has(model) ? model : buildModel(model as unknown as ModelSpec<TApi>),
956
+ // Rebuild from a projected spec (sparse compatConfig) instead of resolved compat.
957
+ inputRefs.has(model) ? model : buildModel({ ...model, compat: model.compatConfig } as unknown as ModelSpec<TApi>),
958
958
  );
959
959
  }
960
960