@oh-my-pi/pi-catalog 15.13.1 → 15.13.2

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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,25 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [15.13.2] - 2026-06-15
6
+
7
+ ### Added
8
+
9
+ - Added the `ToolCallSyntax` union and `FALLBACK_TOOL_SYNTAX` constant to `@oh-my-pi/pi-catalog/identity` (re-exported from `@oh-my-pi/pi-ai/grammar`).
10
+ - Added `preferredToolSyntax(modelId)` to `@oh-my-pi/pi-catalog/identity`, resolving a model's native tool-call syntax affinity from its family token (Claude→`anthropic`, GLM→`glm`, Kimi→`kimi`, Qwen→`qwen3`, DeepSeek→`deepseek`, OpenAI/gpt-oss→`harmony`, else the `xml` fallback).
11
+ - Added `flux-1-schnell-fp8` to the Fireworks serverless model catalog
12
+ - Added `gpt-oss-20b` to the Fireworks model catalog
13
+ - Added `qwen3-embedding-8b` to the Fireworks model catalog
14
+ - Added `qwen3-reranker-8b` to the Fireworks model catalog
15
+ - Added `Gemma 4 E2B IT` and `Gemma 4 E4B IT` to the Google model catalog
16
+ - Added `qwen/qwen3-asr-flash` to the Zenmux model catalog
17
+ - Added sparse `supportsTools` model metadata so providers can mark models that require in-band tool-call formatting.
18
+
19
+ ### Changed
20
+
21
+ - Kept non-tool-capable Fireworks serverless models in discovery results and marked them with `supportsTools: false` for fallback-aware handling
22
+ - Extended `modelFamilyToken(modelId)` to classify Claude/OpenAI ids the structured parser misses (older dated forms such as `claude-3-5-sonnet-20241022` and `gpt-4o`), returning `anthropic`/`openai` instead of an empty token.
23
+
5
24
  ## [15.13.1] - 2026-06-15
6
25
 
7
26
  ### Added
@@ -37,6 +37,8 @@ export declare function isMinimaxM2FamilyModelId(modelId: string): boolean;
37
37
  * and `none`.
38
38
  */
39
39
  export declare function isOpenAIGptOssModelId(modelId: string): boolean;
40
+ /** OpenAI model ids (gpt-*, o1-*, o3-*, o4-*, or prefixed with openai/). */
41
+ export declare function isOpenAIModelId(modelId: string): boolean;
40
42
  /**
41
43
  * Reasoning-capable GLM coding SKUs: glm-4.5 and up on the base / `-air` /
42
44
  * `-turbo` lines. Excludes the vision (`…v`) shape, the non-reasoning
@@ -7,3 +7,4 @@ export * from "./markers";
7
7
  export * from "./priority";
8
8
  export * from "./reference";
9
9
  export * from "./selection";
10
+ export * from "./tool-syntax";
@@ -0,0 +1,3 @@
1
+ export type ToolCallSyntax = "glm" | "hermes" | "kimi" | "xml" | "anthropic" | "deepseek" | "harmony" | "pi" | "qwen3";
2
+ export declare const FALLBACK_TOOL_SYNTAX: ToolCallSyntax;
3
+ export declare function preferredToolSyntax(modelId: string): ToolCallSyntax;
@@ -371,6 +371,13 @@ export interface Model<TApi extends Api = Api> {
371
371
  baseUrl: string;
372
372
  reasoning: boolean;
373
373
  input: ("text" | "image")[];
374
+ /**
375
+ * Native provider tool-call support. `false` is the only unsupported signal:
376
+ * `true` and `undefined` both mean callers may use native tools. Catalog and
377
+ * discovery sources should set this sparsely when an upstream explicitly
378
+ * reports that native tool calling is unsupported.
379
+ */
380
+ supportsTools?: boolean;
374
381
  cost: {
375
382
  input: number;
376
383
  output: number;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-catalog",
4
- "version": "15.13.1",
4
+ "version": "15.13.2",
5
5
  "description": "Model catalog for omp: bundled model database, provider discovery descriptors, model identity, classification, and equivalence",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": "Can Boluk",
@@ -34,11 +34,11 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "@bufbuild/protobuf": "^2.12.0",
37
- "@oh-my-pi/pi-utils": "15.13.1",
37
+ "@oh-my-pi/pi-utils": "15.13.2",
38
38
  "zod": "^4"
39
39
  },
40
40
  "devDependencies": {
41
- "@oh-my-pi/pi-ai": "15.13.1",
41
+ "@oh-my-pi/pi-ai": "15.13.2",
42
42
  "@types/bun": "^1.3.14"
43
43
  },
44
44
  "engines": {
@@ -78,6 +78,11 @@ export function isOpenAIGptOssModelId(modelId: string): boolean {
78
78
  return /(^|\/)gpt-oss[-:]/i.test(modelId);
79
79
  }
80
80
 
81
+ /** OpenAI model ids (gpt-*, o1-*, o3-*, o4-*, or prefixed with openai/). */
82
+ export function isOpenAIModelId(modelId: string): boolean {
83
+ return /(^|\/)(gpt|o1|o3|o4)[-.]/i.test(modelId) || modelId.toLowerCase().includes("openai/");
84
+ }
85
+
81
86
  /**
82
87
  * Reasoning-capable GLM coding SKUs: glm-4.5 and up on the base / `-air` /
83
88
  * `-turbo` lines. Excludes the vision (`…v`) shape, the non-reasoning
@@ -114,6 +119,8 @@ export function isGlmVisionModelId(modelId: string): boolean {
114
119
  export function modelFamilyToken(modelId: string): string {
115
120
  const parsed = parseKnownModel(modelId);
116
121
  if (parsed.family !== "unknown") return parsed.family;
122
+ if (isClaudeModelId(modelId) || isAnthropicNamespacedModelId(modelId)) return "anthropic";
123
+ if (isOpenAIModelId(modelId)) return "openai";
117
124
  if (isKimiModelId(modelId)) return "kimi";
118
125
  if (isQwenModelId(modelId)) return "qwen";
119
126
  if (isMinimaxM2FamilyModelId(modelId)) return "minimax";
@@ -7,3 +7,4 @@ export * from "./markers";
7
7
  export * from "./priority";
8
8
  export * from "./reference";
9
9
  export * from "./selection";
10
+ export * from "./tool-syntax";
@@ -0,0 +1,25 @@
1
+ import { modelFamilyToken } from "./family";
2
+
3
+ export type ToolCallSyntax = "glm" | "hermes" | "kimi" | "xml" | "anthropic" | "deepseek" | "harmony" | "pi" | "qwen3";
4
+
5
+ export const FALLBACK_TOOL_SYNTAX: ToolCallSyntax = "xml";
6
+
7
+ export function preferredToolSyntax(modelId: string): ToolCallSyntax {
8
+ switch (modelFamilyToken(modelId)) {
9
+ case "anthropic":
10
+ return "anthropic";
11
+ case "glm":
12
+ return "glm";
13
+ case "kimi":
14
+ return "kimi";
15
+ case "qwen":
16
+ return "qwen3";
17
+ case "deepseek":
18
+ return "deepseek";
19
+ case "openai":
20
+ case "gpt-oss":
21
+ return "harmony";
22
+ default:
23
+ return FALLBACK_TOOL_SYNTAX;
24
+ }
25
+ }
package/src/models.json CHANGED
@@ -13692,6 +13692,39 @@
13692
13692
  "requiresAssistantContentForToolCalls": true
13693
13693
  }
13694
13694
  },
13695
+ "flux-1-schnell-fp8": {
13696
+ "id": "flux-1-schnell-fp8",
13697
+ "name": "FLUX.1 [schnell] FP8",
13698
+ "api": "openai-completions",
13699
+ "provider": "fireworks",
13700
+ "baseUrl": "https://api.fireworks.ai/inference/v1",
13701
+ "reasoning": true,
13702
+ "input": [
13703
+ "text"
13704
+ ],
13705
+ "cost": {
13706
+ "input": 0,
13707
+ "output": 0,
13708
+ "cacheRead": 0,
13709
+ "cacheWrite": 0
13710
+ },
13711
+ "contextWindow": null,
13712
+ "maxTokens": null,
13713
+ "supportsTools": false,
13714
+ "thinking": {
13715
+ "mode": "effort",
13716
+ "efforts": [
13717
+ "minimal",
13718
+ "low",
13719
+ "medium",
13720
+ "high",
13721
+ "xhigh"
13722
+ ],
13723
+ "effortMap": {
13724
+ "minimal": "none"
13725
+ }
13726
+ }
13727
+ },
13695
13728
  "glm-5": {
13696
13729
  "id": "glm-5",
13697
13730
  "name": "GLM-5",
@@ -13783,6 +13816,26 @@
13783
13816
  ]
13784
13817
  }
13785
13818
  },
13819
+ "gpt-oss-20b": {
13820
+ "id": "gpt-oss-20b",
13821
+ "name": "OpenAI gpt-oss-20b",
13822
+ "api": "openai-completions",
13823
+ "provider": "fireworks",
13824
+ "baseUrl": "https://api.fireworks.ai/inference/v1",
13825
+ "reasoning": false,
13826
+ "input": [
13827
+ "text"
13828
+ ],
13829
+ "cost": {
13830
+ "input": 0,
13831
+ "output": 0,
13832
+ "cacheRead": 0,
13833
+ "cacheWrite": 0
13834
+ },
13835
+ "contextWindow": 131072,
13836
+ "maxTokens": 65536,
13837
+ "supportsTools": false
13838
+ },
13786
13839
  "kimi-k2.5": {
13787
13840
  "id": "kimi-k2.5",
13788
13841
  "name": "Kimi K2.5",
@@ -14003,6 +14056,70 @@
14003
14056
  }
14004
14057
  }
14005
14058
  },
14059
+ "qwen3-embedding-8b": {
14060
+ "id": "qwen3-embedding-8b",
14061
+ "name": "Qwen3 Embedding 8B",
14062
+ "api": "openai-completions",
14063
+ "provider": "fireworks",
14064
+ "baseUrl": "https://api.fireworks.ai/inference/v1",
14065
+ "reasoning": true,
14066
+ "input": [
14067
+ "text"
14068
+ ],
14069
+ "cost": {
14070
+ "input": 0,
14071
+ "output": 0,
14072
+ "cacheRead": 0,
14073
+ "cacheWrite": 0
14074
+ },
14075
+ "contextWindow": 40960,
14076
+ "maxTokens": null,
14077
+ "supportsTools": false,
14078
+ "thinking": {
14079
+ "mode": "effort",
14080
+ "efforts": [
14081
+ "minimal",
14082
+ "low",
14083
+ "medium",
14084
+ "high"
14085
+ ],
14086
+ "effortMap": {
14087
+ "minimal": "none"
14088
+ }
14089
+ }
14090
+ },
14091
+ "qwen3-reranker-8b": {
14092
+ "id": "qwen3-reranker-8b",
14093
+ "name": "Qwen3 Reranker 8B",
14094
+ "api": "openai-completions",
14095
+ "provider": "fireworks",
14096
+ "baseUrl": "https://api.fireworks.ai/inference/v1",
14097
+ "reasoning": true,
14098
+ "input": [
14099
+ "text"
14100
+ ],
14101
+ "cost": {
14102
+ "input": 0,
14103
+ "output": 0,
14104
+ "cacheRead": 0,
14105
+ "cacheWrite": 0
14106
+ },
14107
+ "contextWindow": 40960,
14108
+ "maxTokens": null,
14109
+ "supportsTools": false,
14110
+ "thinking": {
14111
+ "mode": "effort",
14112
+ "efforts": [
14113
+ "minimal",
14114
+ "low",
14115
+ "medium",
14116
+ "high"
14117
+ ],
14118
+ "effortMap": {
14119
+ "minimal": "none"
14120
+ }
14121
+ }
14122
+ },
14006
14123
  "qwen3.6-plus": {
14007
14124
  "id": "qwen3.6-plus",
14008
14125
  "name": "Qwen3.6 Plus",
@@ -16509,6 +16626,64 @@
16509
16626
  "high"
16510
16627
  ]
16511
16628
  }
16629
+ },
16630
+ "gemma-4-E2B-it": {
16631
+ "id": "gemma-4-E2B-it",
16632
+ "name": "Gemma 4 E2B IT",
16633
+ "api": "google-generative-ai",
16634
+ "provider": "google",
16635
+ "baseUrl": "https://generativelanguage.googleapis.com/v1beta",
16636
+ "reasoning": true,
16637
+ "input": [
16638
+ "text",
16639
+ "image"
16640
+ ],
16641
+ "cost": {
16642
+ "input": 0,
16643
+ "output": 0,
16644
+ "cacheRead": 0,
16645
+ "cacheWrite": 0
16646
+ },
16647
+ "contextWindow": 131072,
16648
+ "maxTokens": 8192,
16649
+ "thinking": {
16650
+ "mode": "budget",
16651
+ "efforts": [
16652
+ "minimal",
16653
+ "low",
16654
+ "medium",
16655
+ "high"
16656
+ ]
16657
+ }
16658
+ },
16659
+ "gemma-4-E4B-it": {
16660
+ "id": "gemma-4-E4B-it",
16661
+ "name": "Gemma 4 E4B IT",
16662
+ "api": "google-generative-ai",
16663
+ "provider": "google",
16664
+ "baseUrl": "https://generativelanguage.googleapis.com/v1beta",
16665
+ "reasoning": true,
16666
+ "input": [
16667
+ "text",
16668
+ "image"
16669
+ ],
16670
+ "cost": {
16671
+ "input": 0,
16672
+ "output": 0,
16673
+ "cacheRead": 0,
16674
+ "cacheWrite": 0
16675
+ },
16676
+ "contextWindow": 131072,
16677
+ "maxTokens": 8192,
16678
+ "thinking": {
16679
+ "mode": "budget",
16680
+ "efforts": [
16681
+ "minimal",
16682
+ "low",
16683
+ "medium",
16684
+ "high"
16685
+ ]
16686
+ }
16512
16687
  }
16513
16688
  },
16514
16689
  "google-antigravity": {
@@ -75419,6 +75594,34 @@
75419
75594
  "requiresEffort": true
75420
75595
  }
75421
75596
  },
75597
+ "qwen/qwen3-asr-flash": {
75598
+ "id": "qwen/qwen3-asr-flash",
75599
+ "name": "Qwen3-ASR-Flash",
75600
+ "api": "openai-completions",
75601
+ "provider": "zenmux",
75602
+ "baseUrl": "https://zenmux.ai/api/v1",
75603
+ "reasoning": true,
75604
+ "input": [
75605
+ "text"
75606
+ ],
75607
+ "cost": {
75608
+ "input": 0,
75609
+ "output": 0,
75610
+ "cacheRead": 0,
75611
+ "cacheWrite": 0
75612
+ },
75613
+ "contextWindow": 1000000,
75614
+ "maxTokens": null,
75615
+ "thinking": {
75616
+ "mode": "effort",
75617
+ "efforts": [
75618
+ "minimal",
75619
+ "low",
75620
+ "medium",
75621
+ "high"
75622
+ ]
75623
+ }
75624
+ },
75422
75625
  "qwen/qwen3-coder": {
75423
75626
  "id": "qwen/qwen3-coder",
75424
75627
  "name": "Qwen3-Coder",
@@ -1160,6 +1160,7 @@ function mapFireworksControlPlaneModel(
1160
1160
  ): ModelSpec<"openai-completions"> {
1161
1161
  const name = toModelName(record.displayName, reference?.name ?? publicModelId);
1162
1162
  const supportsImage = toBoolean(record.supportsImageInput) === true;
1163
+ const supportsTools = toBoolean(record.supportsTools);
1163
1164
  const contextWindow = toPositiveNumber(record.contextLength, reference?.contextWindow ?? null);
1164
1165
  // The control plane reports no max-output budget; default the Kimi family to
1165
1166
  // its published cap, everyone else to the discovery fallback, then clamp.
@@ -1192,6 +1193,7 @@ function mapFireworksControlPlaneModel(
1192
1193
  input: supportsImage ? ["text", "image"] : (reference?.input ?? ["text"]),
1193
1194
  contextWindow,
1194
1195
  maxTokens,
1196
+ ...(supportsTools === false ? { supportsTools: false } : {}),
1195
1197
  };
1196
1198
  return stripFireworksDeepSeekThinkingToggle(model, publicModelId);
1197
1199
  }
@@ -1240,7 +1242,6 @@ async function fetchFireworksServerlessModels(options: {
1240
1242
  if (!isRecord(entry)) continue;
1241
1243
  const record = entry as FireworksControlPlaneModel;
1242
1244
  if (toBoolean(record.supportsServerless) !== true) continue;
1243
- if (toBoolean(record.supportsTools) !== true) continue;
1244
1245
  if (typeof record.state === "string" && record.state !== "READY") continue;
1245
1246
  const wireName = typeof record.name === "string" ? record.name : "";
1246
1247
  if (!wireName) continue;
@@ -1396,6 +1397,7 @@ function mapWaferModel(
1396
1397
  const capabilities = wafer?.capabilities ?? {};
1397
1398
  const reasoning = capabilities.reasoning === true;
1398
1399
  const vision = capabilities.vision === true;
1400
+ const supportsTools = toBoolean(capabilities.tools) === false ? false : undefined;
1399
1401
  const contextWindow = toPositiveNumber(
1400
1402
  wafer?.context_length,
1401
1403
  toPositiveNumber((entry as { max_model_len?: unknown }).max_model_len, defaults.contextWindow),
@@ -1434,6 +1436,7 @@ function mapWaferModel(
1434
1436
  cost,
1435
1437
  contextWindow,
1436
1438
  maxTokens,
1439
+ ...(supportsTools === false ? { supportsTools } : {}),
1437
1440
  };
1438
1441
  if (reasoning) {
1439
1442
  // Wafer's `wafer.provider` envelope tells us which upstream backend serves
@@ -2928,6 +2931,7 @@ export function mapModelsDevToModels(
2928
2931
  },
2929
2932
  contextWindow: toPositiveNumber(m.limit?.context, desc.defaultContextWindow ?? null),
2930
2933
  maxTokens: toPositiveNumber(m.limit?.output, desc.defaultMaxTokens ?? null),
2934
+ ...(m.tool_call === false ? { supportsTools: false } : {}),
2931
2935
  ...(desc.compat && { compat: desc.compat }),
2932
2936
  ...(desc.headers && { headers: { ...desc.headers } }),
2933
2937
  };
package/src/types.ts CHANGED
@@ -436,6 +436,13 @@ export interface Model<TApi extends Api = Api> {
436
436
  baseUrl: string;
437
437
  reasoning: boolean;
438
438
  input: ("text" | "image")[];
439
+ /**
440
+ * Native provider tool-call support. `false` is the only unsupported signal:
441
+ * `true` and `undefined` both mean callers may use native tools. Catalog and
442
+ * discovery sources should set this sparsely when an upstream explicitly
443
+ * reports that native tool calling is unsupported.
444
+ */
445
+ supportsTools?: boolean;
439
446
  cost: {
440
447
  input: number; // $/million tokens
441
448
  output: number; // $/million tokens