@linxiraos/pi-catalog 1.1.3 → 1.1.5

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.
@@ -19,6 +19,7 @@ import {
19
19
  cerebrasModelManagerOptions,
20
20
  cloudflareAiGatewayModelManagerOptions,
21
21
  coreWeaveModelManagerOptions,
22
+ deepinfraModelManagerOptions,
22
23
  deepseekModelManagerOptions,
23
24
  firepassModelManagerOptions,
24
25
  fireworksModelManagerOptions,
@@ -56,6 +57,7 @@ import {
56
57
  xaiModelManagerOptions,
57
58
  xaiOAuthModelManagerOptions,
58
59
  xiaomiModelManagerOptions,
60
+ yoloAutoModelManagerOptions,
59
61
  zenmuxModelManagerOptions,
60
62
  zhipuCodingPlanModelManagerOptions,
61
63
  } from "./openai-compat";
@@ -150,6 +152,14 @@ export const CATALOG_PROVIDERS = [
150
152
  createModelManagerOptions: (config: ModelManagerConfig) => cursorModelManagerOptions(config),
151
153
  catalogDiscovery: { label: "Cursor", envVars: ["CURSOR_API_KEY"], oauthProvider: "cursor" },
152
154
  },
155
+ {
156
+ id: "deepinfra",
157
+ defaultModel: "deepseek-ai/DeepSeek-V4-Flash-0731",
158
+ envVars: ["DEEPINFRA_API_KEY"],
159
+ createModelManagerOptions: (config: ModelManagerConfig) => deepinfraModelManagerOptions(config),
160
+ dynamicModelsAuthoritative: true,
161
+ catalogDiscovery: { label: "DeepInfra", allowUnauthenticated: true },
162
+ },
153
163
  {
154
164
  id: "deepseek",
155
165
  defaultModel: "deepseek-v4-pro",
@@ -413,7 +423,7 @@ export const CATALOG_PROVIDERS = [
413
423
  },
414
424
  {
415
425
  id: "synthetic",
416
- defaultModel: "hf:zai-org/GLM-5.1",
426
+ defaultModel: "hf:zai-org/GLM-5.2",
417
427
  envVars: ["SYNTHETIC_API_KEY"],
418
428
  createModelManagerOptions: (config: ModelManagerConfig) => syntheticModelManagerOptions(config),
419
429
  dynamicModelsAuthoritative: true,
@@ -521,6 +531,14 @@ export const CATALOG_PROVIDERS = [
521
531
  createModelManagerOptions: (config: ModelManagerConfig) =>
522
532
  xiaomiModelManagerOptions({ ...config, providerId: "xiaomi-token-plan-sgp", tokenPlanRegion: "sgp" }),
523
533
  },
534
+ {
535
+ id: "yolo-auto",
536
+ defaultModel: "deepseek-flash-v4",
537
+ envVars: ["YOLO_AUTO_API_KEY"],
538
+ createModelManagerOptions: (config: ModelManagerConfig) => yoloAutoModelManagerOptions(config),
539
+ dynamicModelsAuthoritative: true,
540
+ catalogDiscovery: { label: "Yolo-Auto" },
541
+ },
524
542
  {
525
543
  id: "zai",
526
544
  defaultModel: "glm-5.3",
@@ -1305,6 +1305,181 @@ export function novitaModelManagerOptions(
1305
1305
  });
1306
1306
  }
1307
1307
 
1308
+ // ---------------------------------------------------------------------------
1309
+ // 5.6 DeepInfra
1310
+ // ---------------------------------------------------------------------------
1311
+
1312
+ export const DEEPINFRA_BASE_URL = "https://api.deepinfra.com/v1/openai";
1313
+ /**
1314
+ * `filter=with_meta` attaches per-model `metadata` (limits, pricing, tags);
1315
+ * `sort_by=omp` asks DeepInfra to return models in omp-priority order
1316
+ * (earlier = better). The mapper does not stamp `priority` yet — see
1317
+ * `mapDeepinfraModel` — but the params are sent so discovery picks the
1318
+ * ordering up as soon as the server honors it.
1319
+ */
1320
+ const DEEPINFRA_MODELS_QUERY = "?filter=with_meta&sort_by=omp";
1321
+ const DEEPINFRA_EFFORTS = [Effort.Low, Effort.Medium, Effort.High] as const;
1322
+
1323
+ /** DeepInfra OpenAI-compatible discovery configuration. */
1324
+ export interface DeepinfraModelManagerConfig {
1325
+ apiKey?: string;
1326
+ baseUrl?: string;
1327
+ fetch?: FetchImpl;
1328
+ }
1329
+
1330
+ interface DeepinfraModelEntry {
1331
+ id?: unknown;
1332
+ metadata?: unknown;
1333
+ }
1334
+
1335
+ function deepinfraTags(metadata: Record<string, unknown>): readonly string[] {
1336
+ const tags = metadata.tags;
1337
+ return Array.isArray(tags) ? tags.filter((tag): tag is string => typeof tag === "string") : [];
1338
+ }
1339
+
1340
+ /**
1341
+ * Map one DeepInfra catalog entry to a chat model spec. Non-`chat` entries
1342
+ * (`tts`, `stt`, `embed`, `image-gen`, `video-gen`) are dropped — those
1343
+ * surfaces are served by dedicated tool backends, not the chat catalog.
1344
+ * DeepInfra reports token prices in USD per 1M tokens — omp's `ModelCost`
1345
+ * unit, used verbatim. A bundled reference (when the generated catalog has
1346
+ * one) is spread first so compat/tooling metadata can contribute, but the
1347
+ * live metadata always wins for limits, pricing, and modalities.
1348
+ */
1349
+ function mapDeepinfraModel(
1350
+ entry: DeepinfraModelEntry,
1351
+ baseUrl: string,
1352
+ reference: ModelSpec<"openai-completions"> | undefined,
1353
+ ): ModelSpec<"openai-completions"> | null {
1354
+ const id = typeof entry.id === "string" ? entry.id.trim() : "";
1355
+ if (!id) {
1356
+ return null;
1357
+ }
1358
+ const metadata = isRecord(entry.metadata) ? entry.metadata : {};
1359
+ const tags = deepinfraTags(metadata);
1360
+ if (!tags.includes("chat")) {
1361
+ return null;
1362
+ }
1363
+ const pricing = isRecord(metadata.pricing) ? metadata.pricing : {};
1364
+ // `reasoning_effort` marks models whose effort dial is advertised. The
1365
+ // parameter itself is validated and accepted platform-wide on DeepInfra
1366
+ // (verified: 200 on effort-tagged, reasoning-only, and plain-chat models;
1367
+ // 422 only for out-of-enum values), so a bundled reference's effort ladder
1368
+ // surviving the `...reference` spread after a tag disappears is harmless —
1369
+ // the host ignores the dial rather than rejecting the request.
1370
+ const thinking: ThinkingConfig | undefined = tags.includes("reasoning_effort")
1371
+ ? { mode: "effort", efforts: [...DEEPINFRA_EFFORTS] }
1372
+ : undefined;
1373
+ const contextWindow = toPositiveNumber(metadata.context_length, reference?.contextWindow ?? null);
1374
+ // `metadata.max_tokens` mirrors `context_length` on every live row today —
1375
+ // it is the total token ceiling, not an output cap (the catalog exposes no
1376
+ // output-specific field). Stamping it into `maxTokens` would advertise
1377
+ // `output === context` for every model and default oversized `max_tokens`
1378
+ // onto models with smaller real output limits. Trust it only when it is
1379
+ // strictly below the context window (i.e. the API starts publishing a real
1380
+ // output cap); otherwise keep the bundled reference's cap (stencil.so fill
1381
+ // during generation) or leave the limit unknown so requests defer to the
1382
+ // server-side cap.
1383
+ const liveMaxTokens = toPositiveNumber(metadata.max_tokens, 0);
1384
+ const hasLiveOutputCap = contextWindow !== null && liveMaxTokens > 0 && liveMaxTokens < contextWindow;
1385
+ // A same-id reference carries the canonical deployment's output cap, which
1386
+ // can exceed the (smaller) window DeepInfra serves — e.g. a 500K cap against
1387
+ // a 256K context. An output cap above the context ceiling is meaningless, so
1388
+ // keep the fallback inside the window.
1389
+ const referenceMaxTokens = reference?.maxTokens ?? null;
1390
+ const maxTokens = hasLiveOutputCap
1391
+ ? liveMaxTokens
1392
+ : referenceMaxTokens !== null && contextWindow !== null
1393
+ ? Math.min(referenceMaxTokens, contextWindow)
1394
+ : referenceMaxTokens;
1395
+ return {
1396
+ ...reference,
1397
+ id,
1398
+ name: reference?.name ?? id,
1399
+ api: "openai-completions",
1400
+ provider: "deepinfra",
1401
+ baseUrl,
1402
+ reasoning: tags.includes("reasoning") || tags.includes("reasoning_effort"),
1403
+ ...(thinking ? { thinking } : {}),
1404
+ input: tags.includes("vision") || tags.includes("vlm") ? ["text", "image"] : ["text"],
1405
+ cost: {
1406
+ input: toPositiveNumber(pricing.input_tokens, 0),
1407
+ output: toPositiveNumber(pricing.output_tokens, 0),
1408
+ cacheRead: toPositiveNumber(pricing.cache_read_tokens, 0),
1409
+ cacheWrite: 0,
1410
+ },
1411
+ contextWindow,
1412
+ maxTokens,
1413
+ };
1414
+ }
1415
+
1416
+ /**
1417
+ * Bespoke fetch instead of `fetchOpenAICompatibleModels`: the shared helper
1418
+ * cannot carry the `filter`/`sort_by` query params and re-sorts results by id,
1419
+ * which would destroy DeepInfra's priority ordering once the server honors
1420
+ * `sort_by=omp`. Response order is preserved (dedupe keeps the first, i.e.
1421
+ * highest-priority, occurrence).
1422
+ */
1423
+ async function fetchDeepinfraModels(options: {
1424
+ baseUrl: string;
1425
+ apiKey?: string;
1426
+ fetch?: FetchImpl;
1427
+ references: Map<string, ModelSpec<"openai-completions">>;
1428
+ }): Promise<ModelSpec<"openai-completions">[] | null> {
1429
+ const headers: Record<string, string> = { Accept: "application/json" };
1430
+ if (options.apiKey) {
1431
+ headers.Authorization = `Bearer ${options.apiKey}`;
1432
+ }
1433
+ const fetchImpl = discoveryFetch(options.fetch);
1434
+ let payload: unknown;
1435
+ try {
1436
+ const response = await withCatalogDiscoveryTimeout(DEFAULT_OPENAI_COMPATIBLE_DISCOVERY_TIMEOUT_MS, signal =>
1437
+ fetchImpl(`${options.baseUrl}/models${DEEPINFRA_MODELS_QUERY}`, { method: "GET", headers, signal }),
1438
+ );
1439
+ if (!response.ok) {
1440
+ return null;
1441
+ }
1442
+ payload = await response.json();
1443
+ } catch {
1444
+ return null;
1445
+ }
1446
+ if (!isRecord(payload) || !Array.isArray(payload.data)) {
1447
+ return null;
1448
+ }
1449
+ const models: ModelSpec<"openai-completions">[] = [];
1450
+ const seen = new Set<string>();
1451
+ for (const entry of payload.data) {
1452
+ if (!isRecord(entry)) {
1453
+ continue;
1454
+ }
1455
+ const reference = typeof entry.id === "string" ? options.references.get(entry.id) : undefined;
1456
+ const mapped = mapDeepinfraModel(entry as DeepinfraModelEntry, options.baseUrl, reference);
1457
+ if (mapped && !seen.has(mapped.id)) {
1458
+ seen.add(mapped.id);
1459
+ models.push(mapped);
1460
+ }
1461
+ }
1462
+ return models;
1463
+ }
1464
+
1465
+ /**
1466
+ * Builds DeepInfra's model-discovery manager. The catalog endpoint is public,
1467
+ * so discovery (and keyless `gen:models` generation) works without an API key;
1468
+ * model availability at runtime is still gated on credentials by the registry.
1469
+ */
1470
+ export function deepinfraModelManagerOptions(
1471
+ config?: DeepinfraModelManagerConfig,
1472
+ ): ModelManagerOptions<"openai-completions"> {
1473
+ const apiKey = config?.apiKey;
1474
+ const baseUrl = (config?.baseUrl ?? DEEPINFRA_BASE_URL).replace(/\/$/, "");
1475
+ const references = createBundledReferenceMap<"openai-completions">("deepinfra");
1476
+ return {
1477
+ providerId: "deepinfra",
1478
+ dynamicModelsAuthoritative: true,
1479
+ fetchDynamicModels: () => fetchDeepinfraModels({ baseUrl, apiKey, fetch: config?.fetch, references }),
1480
+ };
1481
+ }
1482
+
1308
1483
  // ---------------------------------------------------------------------------
1309
1484
  // 6. xAI
1310
1485
  // ---------------------------------------------------------------------------
@@ -4585,6 +4760,133 @@ export function aiandModelManagerOptions(config?: AiandModelManagerConfig): Mode
4585
4760
  };
4586
4761
  }
4587
4762
 
4763
+ // ---------------------------------------------------------------------------
4764
+ // 16.7 Yolo-Auto (yolo-auto.com)
4765
+ // ---------------------------------------------------------------------------
4766
+
4767
+ const YOLO_AUTO_BASE_URL = "https://yolo-auto.com/v1";
4768
+
4769
+ /**
4770
+ * Documented Yolo-Auto catalog (yolo-auto.com/docs, 2026-08) bundled so the
4771
+ * provider is usable when generation and first boot have no live key. The
4772
+ * flat-rate `/v1/models` response is authoritative once discovery runs.
4773
+ * The compat block mirrors the provider's documented wire surface: the API
4774
+ * speaks the generic chat template with `reasoning_effort` support and rejects
4775
+ * the `developer` role and `store` param.
4776
+ */
4777
+ export const YOLO_AUTO_STATIC_MODELS: readonly ModelSpec<"openai-completions">[] = [
4778
+ {
4779
+ id: "deepseek-flash-v4",
4780
+ name: "DeepSeek Flash V4",
4781
+ api: "openai-completions",
4782
+ provider: "yolo-auto",
4783
+ baseUrl: YOLO_AUTO_BASE_URL,
4784
+ reasoning: true,
4785
+ input: ["text", "image"],
4786
+ thinking: {
4787
+ mode: "effort",
4788
+ efforts: [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High, Effort.XHigh, Effort.Max],
4789
+ effortMap: {
4790
+ [Effort.Minimal]: "low",
4791
+ [Effort.Low]: "low",
4792
+ [Effort.Medium]: "high",
4793
+ [Effort.High]: "high",
4794
+ [Effort.XHigh]: "max",
4795
+ [Effort.Max]: "max",
4796
+ },
4797
+ },
4798
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
4799
+ contextWindow: 131_072,
4800
+ maxTokens: null,
4801
+ compat: {
4802
+ supportsDeveloperRole: false,
4803
+ supportsStore: false,
4804
+ supportsReasoningEffort: true,
4805
+ thinkingFormat: "chat-template",
4806
+ },
4807
+ },
4808
+ ];
4809
+
4810
+ export interface YoloAutoModelManagerConfig {
4811
+ apiKey?: string;
4812
+ baseUrl?: string;
4813
+ fetch?: FetchImpl;
4814
+ }
4815
+
4816
+ /**
4817
+ * Map a discovered entry onto its reference, then re-apply Yolo-Auto's
4818
+ * provider-wide wire constraints. A model that only exists in another
4819
+ * provider's bundle (e.g. `gpt-4o` served via the global reference index)
4820
+ * would otherwise inherit that provider's token pricing and `store`-capable
4821
+ * surface, which the flat-rate Yolo endpoint neither charges nor accepts.
4822
+ */
4823
+ function mapYoloAutoModel(
4824
+ entry: OpenAICompatibleModelRecord,
4825
+ defaults: ModelSpec<"openai-completions">,
4826
+ reference: ModelSpec<"openai-completions"> | undefined,
4827
+ ): ModelSpec<"openai-completions"> {
4828
+ const model = mapWithBundledReference(entry, defaults, reference);
4829
+ return {
4830
+ ...model,
4831
+ // Flat-rate and no-store are provider-wide: they must win whether the
4832
+ // reference came from the yolo bundle, the global index, or nowhere.
4833
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
4834
+ compat: {
4835
+ ...model.compat,
4836
+ supportsStore: false,
4837
+ supportsDeveloperRole: false,
4838
+ },
4839
+ };
4840
+ }
4841
+ /**
4842
+ * Yolo-Auto model manager: OpenAI-compatible chat completions at the
4843
+ * flat-rate `deepseek-flash-v4` endpoint. Live `/v1/models` discovery replaces the
4844
+ * bundled seed once a key is stored or present in `YOLO_AUTO_API_KEY`. Models
4845
+ * the provider adds later inherit metadata (reasoning, thinking, context)
4846
+ * from the global bundled reference index, so new ids work without a per-id
4847
+ * code change.
4848
+ */
4849
+ export function yoloAutoModelManagerOptions(
4850
+ config?: YoloAutoModelManagerConfig,
4851
+ ): ModelManagerOptions<"openai-completions"> {
4852
+ // Curated static models take precedence over the previously generated
4853
+ // bundle so later corrections to the curated metadata are honored; bundle
4854
+ // rows keep ids that earlier credentialed runs discovered, and the global
4855
+ // index covers everything else.
4856
+ const references = new Map<string, ModelSpec<"openai-completions">>();
4857
+ const bundled = createBundledReferenceMap<"openai-completions">("yolo-auto");
4858
+ for (const model of bundled.values()) {
4859
+ references.set(model.id, model);
4860
+ }
4861
+ for (const model of YOLO_AUTO_STATIC_MODELS) {
4862
+ const previous = references.get(model.id);
4863
+ references.set(model.id, {
4864
+ ...previous,
4865
+ ...model,
4866
+ // Generation-filled fields the curated seed leaves null: keep the
4867
+ // canonical values baked by the previous regen (max-output cap,
4868
+ // derived thinking) instead of dropping them.
4869
+ maxTokens: model.maxTokens ?? previous?.maxTokens ?? null,
4870
+ thinking: model.thinking ?? previous?.thinking,
4871
+ });
4872
+ }
4873
+ const resolveReference = createReferenceResolver(() => references);
4874
+ return {
4875
+ ...createOpenAICompatibleModelManagerOptions({
4876
+ api: "openai-completions",
4877
+ providerId: "yolo-auto",
4878
+ defaultBaseUrl: YOLO_AUTO_BASE_URL,
4879
+ config,
4880
+ requireApiKey: true,
4881
+ mapModel: (entry, defaults, reference) =>
4882
+ mapYoloAutoModel(entry, defaults, resolveReference(defaults.id) ?? reference),
4883
+ dynamicModelsAuthoritative: true,
4884
+ }),
4885
+ };
4886
+ }
4887
+
4888
+ // ---------------------------------------------------------------------------
4889
+
4588
4890
  // ---------------------------------------------------------------------------
4589
4891
  // 17. Qwen Portal
4590
4892
  // ---------------------------------------------------------------------------
package/src/types.ts CHANGED
@@ -150,7 +150,14 @@ export interface Usage {
150
150
  };
151
151
  }
152
152
 
153
- export type OpenAIReasoningFormat = "openai" | "openrouter" | "zai" | "kimi" | "qwen" | "qwen-chat-template";
153
+ export type OpenAIReasoningFormat =
154
+ | "openai"
155
+ | "openrouter"
156
+ | "zai"
157
+ | "kimi"
158
+ | "qwen"
159
+ | "qwen-chat-template"
160
+ | "chat-template";
154
161
 
155
162
  export type OpenAIReasoningDisableMode =
156
163
  | "omit"
@@ -160,7 +167,8 @@ export type OpenAIReasoningDisableMode =
160
167
  | "venice-disable-thinking"
161
168
  | "zai-thinking-disabled"
162
169
  | "qwen-enable-thinking-false"
163
- | "qwen-template-false";
170
+ | "qwen-template-false"
171
+ | "chat-template-thinking-false";
164
172
 
165
173
  export type OpenAIStreamMarkupHealingPattern = "kimi" | "dsml" | "qwen" | "thinking";
166
174