@ryanfw/prompt-orchestration-pipeline 1.2.6 → 1.2.8

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.
Files changed (34) hide show
  1. package/package.json +1 -1
  2. package/src/config/__tests__/models.test.ts +51 -11
  3. package/src/config/models.ts +195 -100
  4. package/src/core/__tests__/config.test.ts +79 -0
  5. package/src/core/__tests__/lifecycle-policy.test.ts +78 -0
  6. package/src/core/__tests__/pipeline-runner.test.ts +628 -0
  7. package/src/core/__tests__/task-runner.test.ts +2 -3
  8. package/src/core/config.ts +17 -0
  9. package/src/core/pipeline-runner.ts +67 -9
  10. package/src/core/status-writer.ts +4 -0
  11. package/src/core/task-runner.ts +2 -5
  12. package/src/providers/__tests__/base.test.ts +2 -2
  13. package/src/providers/__tests__/openai.test.ts +1 -1
  14. package/src/ui/client/__tests__/job-adapter.test.ts +12 -0
  15. package/src/ui/client/adapters/job-adapter.ts +1 -0
  16. package/src/ui/client/types.ts +1 -0
  17. package/src/ui/components/DAGGrid.tsx +11 -1
  18. package/src/ui/components/JobDetail.tsx +2 -1
  19. package/src/ui/components/__tests__/DAGGrid.test.tsx +92 -0
  20. package/src/ui/components/__tests__/JobDetail.test.tsx +62 -0
  21. package/src/ui/components/types.ts +2 -0
  22. package/src/ui/dist/assets/{index-CeAgP91B.js → index-CNlnQmK4.js} +144 -64
  23. package/src/ui/dist/assets/{index-CeAgP91B.js.map → index-CNlnQmK4.js.map} +1 -1
  24. package/src/ui/dist/assets/style-DNbNL3Yg.css +2 -0
  25. package/src/ui/dist/index.html +2 -2
  26. package/src/ui/embedded-assets.js +6 -6
  27. package/src/ui/server/__tests__/job-control-endpoints.test.ts +474 -2
  28. package/src/ui/server/endpoints/job-control-endpoints.ts +136 -22
  29. package/src/ui/state/transformers/__tests__/status-transformer.test.ts +15 -0
  30. package/src/ui/state/transformers/status-transformer.ts +1 -0
  31. package/src/ui/state/types.ts +1 -0
  32. package/src/utils/__tests__/dag.test.ts +35 -0
  33. package/src/utils/dag.ts +1 -0
  34. package/src/ui/dist/assets/style-DA1Ma4YS.css +0 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ryanfw/prompt-orchestration-pipeline",
3
- "version": "1.2.6",
3
+ "version": "1.2.8",
4
4
  "description": "A Prompt-orchestration pipeline (POP) is a framework for building, running, and experimenting with complex chains of LLM tasks.",
5
5
  "type": "module",
6
6
  "main": "src/ui/server/index.ts",
@@ -15,7 +15,7 @@ import {
15
15
  } from "../models";
16
16
  import type { ModelConfigEntry } from "../models";
17
17
 
18
- const MODEL_COUNT = 41;
18
+ const MODEL_COUNT = 50;
19
19
  const PROVIDER_COUNT = 8;
20
20
 
21
21
  describe("ModelAlias", () => {
@@ -110,8 +110,8 @@ describe("DEFAULT_MODEL_BY_PROVIDER", () => {
110
110
  });
111
111
 
112
112
  describe("aliasToFunctionName", () => {
113
- it('converts "openai:gpt-5.2" to "gpt52"', () => {
114
- expect(aliasToFunctionName("openai:gpt-5.2")).toBe("gpt52");
113
+ it('converts "openai:gpt-5.4" to "gpt54"', () => {
114
+ expect(aliasToFunctionName("openai:gpt-5.4")).toBe("gpt54");
115
115
  });
116
116
 
117
117
  it('converts "gemini:flash-2.5-lite" to "flash25Lite"', () => {
@@ -137,8 +137,8 @@ describe("aliasToFunctionName", () => {
137
137
  });
138
138
 
139
139
  describe("getProviderFromAlias", () => {
140
- it('returns "openai" for "openai:gpt-5.2"', () => {
141
- expect(getProviderFromAlias("openai:gpt-5.2")).toBe("openai");
140
+ it('returns "openai" for "openai:gpt-5.4"', () => {
141
+ expect(getProviderFromAlias("openai:gpt-5.4")).toBe("openai");
142
142
  });
143
143
 
144
144
  it("throws for non-string input", () => {
@@ -152,8 +152,8 @@ describe("getProviderFromAlias", () => {
152
152
  });
153
153
 
154
154
  describe("getModelFromAlias", () => {
155
- it('returns "gpt-5.2" for "openai:gpt-5.2"', () => {
156
- expect(getModelFromAlias("openai:gpt-5.2")).toBe("gpt-5.2");
155
+ it('returns "gpt-5.4" for "openai:gpt-5.4"', () => {
156
+ expect(getModelFromAlias("openai:gpt-5.4")).toBe("gpt-5.4");
157
157
  });
158
158
 
159
159
  it("handles multiple colons by rejoining segments after first", () => {
@@ -171,12 +171,27 @@ describe("getModelFromAlias", () => {
171
171
  });
172
172
 
173
173
  describe("getModelConfig", () => {
174
- it('returns config for "openai:gpt-5.2" with provider "openai"', () => {
175
- const config = getModelConfig("openai:gpt-5.2");
174
+ it('returns config for "openai:gpt-5.4" with provider "openai"', () => {
175
+ const config = getModelConfig("openai:gpt-5.4");
176
176
  expect(config).not.toBeNull();
177
177
  expect(config!.provider).toBe("openai");
178
178
  });
179
179
 
180
+ it("returns configs for Alibaba Qwen 3.6 models", () => {
181
+ expect(getModelConfig("alibaba:qwen3.6-flash")).toMatchObject({
182
+ provider: "alibaba",
183
+ model: "qwen3.6-flash",
184
+ });
185
+ expect(getModelConfig("alibaba:qwen3.6-plus")).toMatchObject({
186
+ provider: "alibaba",
187
+ model: "qwen3.6-plus",
188
+ });
189
+ expect(getModelConfig("alibaba:qwen3.6-max-preview")).toMatchObject({
190
+ provider: "alibaba",
191
+ model: "qwen3.6-max-preview",
192
+ });
193
+ });
194
+
180
195
  it("returns null for unknown aliases", () => {
181
196
  expect(getModelConfig("nonexistent:model")).toBeNull();
182
197
  expect(getModelConfig("invalid")).toBeNull();
@@ -184,10 +199,20 @@ describe("getModelConfig", () => {
184
199
 
185
200
  it("returns null for removed aliases", () => {
186
201
  expect(getModelConfig("openai:gpt-4")).toBeNull();
202
+ expect(getModelConfig("openai:gpt-4o")).toBeNull();
203
+ expect(getModelConfig("openai:gpt-4o-mini")).toBeNull();
204
+ expect(getModelConfig("openai:gpt-5.2")).toBeNull();
205
+ expect(getModelConfig("openai:gpt-5.2-pro")).toBeNull();
206
+ expect(getModelConfig("openai:o3-mini")).toBeNull();
207
+ expect(getModelConfig("anthropic:haiku-4-6")).toBeNull();
208
+ expect(getModelConfig("deepseek:deepseek-chat")).toBeNull();
209
+ expect(getModelConfig("deepseek:deepseek-reasoner")).toBeNull();
187
210
  expect(getModelConfig("deepseek:r1")).toBeNull();
211
+ expect(getModelConfig("moonshot:kimi-k1.5")).toBeNull();
188
212
  expect(getModelConfig("moonshot:kimi-moonshot-v1-128k")).toBeNull();
189
213
  expect(getModelConfig("gemini:flash-3")).toBeNull();
190
214
  expect(getModelConfig("gemini:pro-3")).toBeNull();
215
+ expect(getModelConfig("zai:glm-5-code")).toBeNull();
191
216
  expect(getModelConfig("zai:glm-4-plus")).toBeNull();
192
217
  });
193
218
  });
@@ -201,13 +226,21 @@ describe("FUNCTION_NAME_BY_ALIAS", () => {
201
226
  expect(Object.isFrozen(FUNCTION_NAME_BY_ALIAS)).toBe(true);
202
227
  });
203
228
 
204
- it("has correct value for openai:gpt-5.2", () => {
205
- expect(FUNCTION_NAME_BY_ALIAS["openai:gpt-5.2"]).toBe("gpt52");
229
+ it("has correct value for openai:gpt-5.4", () => {
230
+ expect(FUNCTION_NAME_BY_ALIAS["openai:gpt-5.4"]).toBe("gpt54");
206
231
  });
207
232
 
208
233
  it("has correct value for gemini:flash-2.5-lite", () => {
209
234
  expect(FUNCTION_NAME_BY_ALIAS["gemini:flash-2.5-lite"]).toBe("flash25Lite");
210
235
  });
236
+
237
+ it("has correct values for Alibaba Qwen 3.6 models", () => {
238
+ expect(FUNCTION_NAME_BY_ALIAS["alibaba:qwen3.6-flash"]).toBe("qwen36Flash");
239
+ expect(FUNCTION_NAME_BY_ALIAS["alibaba:qwen3.6-plus"]).toBe("qwen36Plus");
240
+ expect(FUNCTION_NAME_BY_ALIAS["alibaba:qwen3.6-max-preview"]).toBe(
241
+ "qwen36MaxPreview",
242
+ );
243
+ });
211
244
  });
212
245
 
213
246
  describe("PROVIDER_FUNCTIONS", () => {
@@ -238,6 +271,13 @@ describe("PROVIDER_FUNCTIONS", () => {
238
271
  }
239
272
  });
240
273
 
274
+ it("includes callable paths for Alibaba Qwen 3.6 models", () => {
275
+ const alibabaPaths = PROVIDER_FUNCTIONS.alibaba.map((entry) => entry.fullPath);
276
+ expect(alibabaPaths).toContain("llm.alibaba.qwen36Flash");
277
+ expect(alibabaPaths).toContain("llm.alibaba.qwen36Plus");
278
+ expect(alibabaPaths).toContain("llm.alibaba.qwen36MaxPreview");
279
+ });
280
+
241
281
  it("is frozen (top-level)", () => {
242
282
  expect(Object.isFrozen(PROVIDER_FUNCTIONS)).toBe(true);
243
283
  });
@@ -27,7 +27,11 @@ function deepFreeze<T>(obj: T): T {
27
27
  if (obj === null || typeof obj !== "object") return obj;
28
28
  Object.freeze(obj);
29
29
  for (const value of Object.values(obj as object)) {
30
- if (value !== null && typeof value === "object" && !Object.isFrozen(value)) {
30
+ if (
31
+ value !== null &&
32
+ typeof value === "object" &&
33
+ !Object.isFrozen(value)
34
+ ) {
31
35
  deepFreeze(value);
32
36
  }
33
37
  }
@@ -41,12 +45,12 @@ export const ModelAlias = Object.freeze({
41
45
  OPENAI_GPT_4_1: "openai:gpt-4-1",
42
46
  OPENAI_GPT_4_1_MINI: "openai:gpt-4-1-mini",
43
47
  OPENAI_GPT_4_1_NANO: "openai:gpt-4-1-nano",
44
- OPENAI_GPT_4O: "openai:gpt-4o",
45
- OPENAI_GPT_4O_MINI: "openai:gpt-4o-mini",
46
- OPENAI_GPT_5_2: "openai:gpt-5.2",
47
- OPENAI_GPT_5_2_PRO: "openai:gpt-5.2-pro",
48
+ OPENAI_GPT_5_4: "openai:gpt-5.4",
49
+ OPENAI_GPT_5_4_MINI: "openai:gpt-5.4-mini",
50
+ OPENAI_GPT_5_4_NANO: "openai:gpt-5.4-nano",
51
+ OPENAI_GPT_5_5: "openai:gpt-5.5",
52
+ OPENAI_GPT_5_5_PRO: "openai:gpt-5.5-pro",
48
53
  OPENAI_O3: "openai:o3",
49
- OPENAI_O3_MINI: "openai:o3-mini",
50
54
  OPENAI_O4_MINI: "openai:o4-mini",
51
55
  // Anthropic
52
56
  ANTHROPIC_OPUS_4_5: "anthropic:opus-4-5",
@@ -54,33 +58,42 @@ export const ModelAlias = Object.freeze({
54
58
  ANTHROPIC_HAIKU_4_5: "anthropic:haiku-4-5",
55
59
  ANTHROPIC_OPUS_4_6: "anthropic:opus-4-6",
56
60
  ANTHROPIC_SONNET_4_6: "anthropic:sonnet-4-6",
57
- ANTHROPIC_HAIKU_4_6: "anthropic:haiku-4-6",
61
+ ANTHROPIC_OPUS_4_7: "anthropic:opus-4-7",
58
62
  // Gemini
59
63
  GEMINI_FLASH_2_5: "gemini:flash-2.5",
60
64
  GEMINI_FLASH_2_5_LITE: "gemini:flash-2.5-lite",
61
65
  GEMINI_PRO_2_5: "gemini:pro-2.5",
62
- // DeepSeek — cache miss prices
63
- DEEPSEEK_CHAT: "deepseek:deepseek-chat",
64
- DEEPSEEK_REASONER: "deepseek:deepseek-reasoner",
66
+ GEMINI_PRO_3_1_PREVIEW: "gemini:pro-3.1-preview",
67
+ GEMINI_FLASH_3_PREVIEW: "gemini:flash-3-preview",
68
+ GEMINI_FLASH_3_1_LITE_PREVIEW: "gemini:flash-3.1-lite-preview",
69
+ // DeepSeek
70
+ DEEPSEEK_V4_FLASH: "deepseek:v4-flash",
71
+ DEEPSEEK_V4_PRO: "deepseek:v4-pro",
65
72
  // Moonshot / Kimi
66
73
  MOONSHOT_KIMI_K2_5: "moonshot:kimi-k2.5",
67
- MOONSHOT_KIMI_K1_5: "moonshot:kimi-k1.5",
74
+ MOONSHOT_KIMI_K2_6: "moonshot:kimi-k2.6",
68
75
  // Claude Code — subscription-based, zero token cost
69
76
  CLAUDE_CODE_SONNET: "claude-code:sonnet",
70
77
  CLAUDE_CODE_OPUS: "claude-code:opus",
71
78
  CLAUDE_CODE_HAIKU: "claude-code:haiku",
72
79
  // Z.ai
80
+ ZAI_GLM_5_1: "zai:glm-5-1",
73
81
  ZAI_GLM_5: "zai:glm-5",
74
- ZAI_GLM_5_CODE: "zai:glm-5-code",
82
+ ZAI_GLM_5_TURBO: "zai:glm-5-turbo",
75
83
  ZAI_GLM_4_7: "zai:glm-4-7",
76
84
  ZAI_GLM_4_7_FLASH_X: "zai:glm-4-7-flash-x",
77
85
  ZAI_GLM_4_6: "zai:glm-4-6",
78
86
  ZAI_GLM_4_5: "zai:glm-4-5",
87
+ ZAI_GLM_4_5_X: "zai:glm-4-5-x",
79
88
  ZAI_GLM_4_5_AIR: "zai:glm-4-5-air",
80
89
  ZAI_GLM_4_5_AIR_X: "zai:glm-4-5-air-x",
81
- // Alibaba (Qwen via DashScope)
90
+ // Alibaba (Qwen via DashScope, international/Singapore deployment)
91
+ ALIBABA_QWEN3_6_MAX_PREVIEW: "alibaba:qwen3.6-max-preview",
92
+ ALIBABA_QWEN3_6_PLUS: "alibaba:qwen3.6-plus",
93
+ ALIBABA_QWEN3_6_FLASH: "alibaba:qwen3.6-flash",
82
94
  ALIBABA_QWEN3_MAX: "alibaba:qwen3-max",
83
95
  ALIBABA_QWEN3_5_PLUS: "alibaba:qwen3.5-plus",
96
+ ALIBABA_QWEN3_5_FLASH: "alibaba:qwen3.5-flash",
84
97
  ALIBABA_QWEN_PLUS: "alibaba:qwen-plus",
85
98
  ALIBABA_QWEN_FLASH: "alibaba:qwen-flash",
86
99
  ALIBABA_QWQ_PLUS: "alibaba:qwq-plus",
@@ -112,41 +125,41 @@ const MODEL_CONFIG_RAW: Record<ModelAliasKey, ModelConfigEntry> = {
112
125
  tokenCostInPerMillion: 0.1,
113
126
  tokenCostOutPerMillion: 0.4,
114
127
  },
115
- "openai:gpt-4o": {
128
+ "openai:gpt-5.4": {
116
129
  provider: "openai",
117
- model: "gpt-4o",
130
+ model: "gpt-5.4",
118
131
  tokenCostInPerMillion: 2.5,
119
- tokenCostOutPerMillion: 10,
132
+ tokenCostOutPerMillion: 15,
120
133
  },
121
- "openai:gpt-4o-mini": {
134
+ "openai:gpt-5.4-mini": {
122
135
  provider: "openai",
123
- model: "gpt-4o-mini",
124
- tokenCostInPerMillion: 0.15,
125
- tokenCostOutPerMillion: 0.6,
136
+ model: "gpt-5.4-mini",
137
+ tokenCostInPerMillion: 0.75,
138
+ tokenCostOutPerMillion: 4.5,
126
139
  },
127
- "openai:gpt-5.2": {
140
+ "openai:gpt-5.4-nano": {
128
141
  provider: "openai",
129
- model: "gpt-5.2",
130
- tokenCostInPerMillion: 2,
131
- tokenCostOutPerMillion: 10,
142
+ model: "gpt-5.4-nano",
143
+ tokenCostInPerMillion: 0.2,
144
+ tokenCostOutPerMillion: 1.25,
132
145
  },
133
- "openai:gpt-5.2-pro": {
146
+ "openai:gpt-5.5": {
134
147
  provider: "openai",
135
- model: "gpt-5.2-pro",
136
- tokenCostInPerMillion: 15,
137
- tokenCostOutPerMillion: 60,
148
+ model: "gpt-5.5",
149
+ tokenCostInPerMillion: 5,
150
+ tokenCostOutPerMillion: 30,
138
151
  },
139
- "openai:o3": {
152
+ "openai:gpt-5.5-pro": {
140
153
  provider: "openai",
141
- model: "o3",
142
- tokenCostInPerMillion: 10,
143
- tokenCostOutPerMillion: 40,
154
+ model: "gpt-5.5-pro",
155
+ tokenCostInPerMillion: 30,
156
+ tokenCostOutPerMillion: 180,
144
157
  },
145
- "openai:o3-mini": {
158
+ "openai:o3": {
146
159
  provider: "openai",
147
- model: "o3-mini",
148
- tokenCostInPerMillion: 1.1,
149
- tokenCostOutPerMillion: 4.4,
160
+ model: "o3",
161
+ tokenCostInPerMillion: 2,
162
+ tokenCostOutPerMillion: 8,
150
163
  },
151
164
  "openai:o4-mini": {
152
165
  provider: "openai",
@@ -185,13 +198,13 @@ const MODEL_CONFIG_RAW: Record<ModelAliasKey, ModelConfigEntry> = {
185
198
  tokenCostInPerMillion: 3,
186
199
  tokenCostOutPerMillion: 15,
187
200
  },
188
- "anthropic:haiku-4-6": {
201
+ "anthropic:opus-4-7": {
189
202
  provider: "anthropic",
190
- model: "claude-haiku-4-6",
191
- tokenCostInPerMillion: 1,
192
- tokenCostOutPerMillion: 5,
203
+ model: "claude-opus-4-7",
204
+ tokenCostInPerMillion: 5,
205
+ tokenCostOutPerMillion: 25,
193
206
  },
194
- // Gemini
207
+ // Gemini — base (≤200k context) pricing for tiered models
195
208
  "gemini:flash-2.5": {
196
209
  provider: "gemini",
197
210
  model: "gemini-2.5-flash",
@@ -210,31 +223,49 @@ const MODEL_CONFIG_RAW: Record<ModelAliasKey, ModelConfigEntry> = {
210
223
  tokenCostInPerMillion: 1.25,
211
224
  tokenCostOutPerMillion: 10,
212
225
  },
226
+ "gemini:pro-3.1-preview": {
227
+ provider: "gemini",
228
+ model: "gemini-3.1-pro-preview",
229
+ tokenCostInPerMillion: 2,
230
+ tokenCostOutPerMillion: 12,
231
+ },
232
+ "gemini:flash-3-preview": {
233
+ provider: "gemini",
234
+ model: "gemini-3-flash-preview",
235
+ tokenCostInPerMillion: 0.5,
236
+ tokenCostOutPerMillion: 3,
237
+ },
238
+ "gemini:flash-3.1-lite-preview": {
239
+ provider: "gemini",
240
+ model: "gemini-3.1-flash-lite-preview",
241
+ tokenCostInPerMillion: 0.25,
242
+ tokenCostOutPerMillion: 1.5,
243
+ },
213
244
  // DeepSeek — cache miss prices
214
- "deepseek:deepseek-chat": {
245
+ "deepseek:v4-flash": {
215
246
  provider: "deepseek",
216
- model: "deepseek-chat",
217
- tokenCostInPerMillion: 0.28,
218
- tokenCostOutPerMillion: 0.42,
247
+ model: "deepseek-v4-flash",
248
+ tokenCostInPerMillion: 0.14,
249
+ tokenCostOutPerMillion: 0.28,
219
250
  },
220
- "deepseek:deepseek-reasoner": {
251
+ "deepseek:v4-pro": {
221
252
  provider: "deepseek",
222
- model: "deepseek-reasoner",
223
- tokenCostInPerMillion: 0.28,
224
- tokenCostOutPerMillion: 0.42,
253
+ model: "deepseek-v4-pro",
254
+ tokenCostInPerMillion: 1.74,
255
+ tokenCostOutPerMillion: 3.48,
225
256
  },
226
- // Moonshot / Kimi
257
+ // Moonshot / Kimi — cache miss prices
227
258
  "moonshot:kimi-k2.5": {
228
259
  provider: "moonshot",
229
260
  model: "kimi-k2.5",
230
- tokenCostInPerMillion: 0.5,
261
+ tokenCostInPerMillion: 0.6,
231
262
  tokenCostOutPerMillion: 2.5,
232
263
  },
233
- "moonshot:kimi-k1.5": {
264
+ "moonshot:kimi-k2.6": {
234
265
  provider: "moonshot",
235
- model: "kimi-k1.5",
236
- tokenCostInPerMillion: 0.5,
237
- tokenCostOutPerMillion: 2.5,
266
+ model: "kimi-k2.6",
267
+ tokenCostInPerMillion: 0.95,
268
+ tokenCostOutPerMillion: 4,
238
269
  },
239
270
  // Claude Code — subscription-based, zero token cost
240
271
  "claude-code:sonnet": {
@@ -256,17 +287,23 @@ const MODEL_CONFIG_RAW: Record<ModelAliasKey, ModelConfigEntry> = {
256
287
  tokenCostOutPerMillion: 0,
257
288
  },
258
289
  // Z.ai
290
+ "zai:glm-5-1": {
291
+ provider: "zai",
292
+ model: "glm-5.1",
293
+ tokenCostInPerMillion: 1.4,
294
+ tokenCostOutPerMillion: 4.4,
295
+ },
259
296
  "zai:glm-5": {
260
297
  provider: "zai",
261
298
  model: "glm-5",
262
299
  tokenCostInPerMillion: 1,
263
300
  tokenCostOutPerMillion: 3.2,
264
301
  },
265
- "zai:glm-5-code": {
302
+ "zai:glm-5-turbo": {
266
303
  provider: "zai",
267
- model: "glm-5-code",
304
+ model: "glm-5-turbo",
268
305
  tokenCostInPerMillion: 1.2,
269
- tokenCostOutPerMillion: 5,
306
+ tokenCostOutPerMillion: 4,
270
307
  },
271
308
  "zai:glm-4-7": {
272
309
  provider: "zai",
@@ -292,6 +329,12 @@ const MODEL_CONFIG_RAW: Record<ModelAliasKey, ModelConfigEntry> = {
292
329
  tokenCostInPerMillion: 0.6,
293
330
  tokenCostOutPerMillion: 2.2,
294
331
  },
332
+ "zai:glm-4-5-x": {
333
+ provider: "zai",
334
+ model: "glm-4.5-x",
335
+ tokenCostInPerMillion: 2.2,
336
+ tokenCostOutPerMillion: 8.9,
337
+ },
295
338
  "zai:glm-4-5-air": {
296
339
  provider: "zai",
297
340
  model: "glm-4.5-air",
@@ -304,30 +347,54 @@ const MODEL_CONFIG_RAW: Record<ModelAliasKey, ModelConfigEntry> = {
304
347
  tokenCostInPerMillion: 1.1,
305
348
  tokenCostOutPerMillion: 4.5,
306
349
  },
307
- // Alibaba (Qwen via DashScope)
350
+ // Alibaba (Qwen via DashScope, international/Singapore deployment, base tier)
351
+ "alibaba:qwen3.6-max-preview": {
352
+ provider: "alibaba",
353
+ model: "qwen3.6-max-preview",
354
+ tokenCostInPerMillion: 1.3,
355
+ tokenCostOutPerMillion: 7.8,
356
+ },
357
+ "alibaba:qwen3.6-plus": {
358
+ provider: "alibaba",
359
+ model: "qwen3.6-plus",
360
+ tokenCostInPerMillion: 0.276,
361
+ tokenCostOutPerMillion: 1.651,
362
+ },
363
+ "alibaba:qwen3.6-flash": {
364
+ provider: "alibaba",
365
+ model: "qwen3.6-flash",
366
+ tokenCostInPerMillion: 0.025,
367
+ tokenCostOutPerMillion: 1.5,
368
+ },
308
369
  "alibaba:qwen3-max": {
309
370
  provider: "alibaba",
310
371
  model: "qwen3-max",
311
- tokenCostInPerMillion: 0.359,
312
- tokenCostOutPerMillion: 1.434,
372
+ tokenCostInPerMillion: 1.2,
373
+ tokenCostOutPerMillion: 6,
313
374
  },
314
375
  "alibaba:qwen3.5-plus": {
315
376
  provider: "alibaba",
316
377
  model: "qwen3.5-plus",
317
- tokenCostInPerMillion: 0.115,
318
- tokenCostOutPerMillion: 0.688,
378
+ tokenCostInPerMillion: 0.4,
379
+ tokenCostOutPerMillion: 2.4,
380
+ },
381
+ "alibaba:qwen3.5-flash": {
382
+ provider: "alibaba",
383
+ model: "qwen3.5-flash",
384
+ tokenCostInPerMillion: 0.1,
385
+ tokenCostOutPerMillion: 0.4,
319
386
  },
320
387
  "alibaba:qwen-plus": {
321
388
  provider: "alibaba",
322
389
  model: "qwen-plus",
323
- tokenCostInPerMillion: 0.115,
324
- tokenCostOutPerMillion: 0.287,
390
+ tokenCostInPerMillion: 0.4,
391
+ tokenCostOutPerMillion: 1.2,
325
392
  },
326
393
  "alibaba:qwen-flash": {
327
394
  provider: "alibaba",
328
395
  model: "qwen-flash",
329
- tokenCostInPerMillion: 0.022,
330
- tokenCostOutPerMillion: 0.216,
396
+ tokenCostInPerMillion: 0.05,
397
+ tokenCostOutPerMillion: 0.4,
331
398
  },
332
399
  "alibaba:qwq-plus": {
333
400
  provider: "alibaba",
@@ -338,19 +405,21 @@ const MODEL_CONFIG_RAW: Record<ModelAliasKey, ModelConfigEntry> = {
338
405
  "alibaba:qwen3-coder-plus": {
339
406
  provider: "alibaba",
340
407
  model: "qwen3-coder-plus",
341
- tokenCostInPerMillion: 0.574,
342
- tokenCostOutPerMillion: 2.294,
408
+ tokenCostInPerMillion: 1,
409
+ tokenCostOutPerMillion: 5,
343
410
  },
344
411
  "alibaba:qwen3-coder-flash": {
345
412
  provider: "alibaba",
346
413
  model: "qwen3-coder-flash",
347
- tokenCostInPerMillion: 0.144,
348
- tokenCostOutPerMillion: 0.574,
414
+ tokenCostInPerMillion: 0.3,
415
+ tokenCostOutPerMillion: 1.5,
349
416
  },
350
417
  };
351
418
 
352
419
  export const MODEL_CONFIG: Readonly<Record<ModelAliasKey, ModelConfigEntry>> =
353
- deepFreeze(MODEL_CONFIG_RAW) as Readonly<Record<ModelAliasKey, ModelConfigEntry>>;
420
+ deepFreeze(MODEL_CONFIG_RAW) as Readonly<
421
+ Record<ModelAliasKey, ModelConfigEntry>
422
+ >;
354
423
 
355
424
  export const VALID_MODEL_ALIASES: ReadonlySet<ModelAliasKey> = new Set(
356
425
  Object.keys(MODEL_CONFIG) as ModelAliasKey[],
@@ -360,18 +429,24 @@ export const VALID_MODEL_ALIASES: ReadonlySet<ModelAliasKey> = new Set(
360
429
 
361
430
  export function aliasToFunctionName(alias: string): string {
362
431
  if (typeof alias !== "string") {
363
- throw new Error(`Invalid model alias: expected string, got ${typeof alias}`);
432
+ throw new Error(
433
+ `Invalid model alias: expected string, got ${typeof alias}`,
434
+ );
364
435
  }
365
436
  if (!alias.includes(":")) {
366
437
  throw new Error(`Invalid model alias: "${alias}" does not contain a colon`);
367
438
  }
368
439
  const model = alias.split(":").slice(1).join(":");
369
- return model.replace(/[-.]([a-z0-9])/gi, (_, char: string) => char.toUpperCase());
440
+ return model.replace(/[-.]([a-z0-9])/gi, (_, char: string) =>
441
+ char.toUpperCase(),
442
+ );
370
443
  }
371
444
 
372
445
  export function getProviderFromAlias(alias: string): ProviderName {
373
446
  if (typeof alias !== "string") {
374
- throw new Error(`Invalid model alias: expected string, got ${typeof alias}`);
447
+ throw new Error(
448
+ `Invalid model alias: expected string, got ${typeof alias}`,
449
+ );
375
450
  }
376
451
  if (!alias.includes(":")) {
377
452
  throw new Error(`Invalid model alias: "${alias}" does not contain a colon`);
@@ -381,7 +456,9 @@ export function getProviderFromAlias(alias: string): ProviderName {
381
456
 
382
457
  export function getModelFromAlias(alias: string): string {
383
458
  if (typeof alias !== "string") {
384
- throw new Error(`Invalid model alias: expected string, got ${typeof alias}`);
459
+ throw new Error(
460
+ `Invalid model alias: expected string, got ${typeof alias}`,
461
+ );
385
462
  }
386
463
  if (!alias.includes(":")) {
387
464
  throw new Error(`Invalid model alias: "${alias}" does not contain a colon`);
@@ -390,33 +467,38 @@ export function getModelFromAlias(alias: string): string {
390
467
  }
391
468
 
392
469
  export function getModelConfig(alias: string): ModelConfigEntry | null {
393
- return (MODEL_CONFIG as Record<string, ModelConfigEntry | undefined>)[alias] ?? null;
470
+ return (
471
+ (MODEL_CONFIG as Record<string, ModelConfigEntry | undefined>)[alias] ??
472
+ null
473
+ );
394
474
  }
395
475
 
396
476
  // ─── Default Model By Provider ───────────────────────────────────────────────
397
477
 
398
- export const DEFAULT_MODEL_BY_PROVIDER: Readonly<Record<ProviderName, ModelAliasKey>> =
399
- Object.freeze({
400
- openai: "openai:gpt-5.2",
401
- anthropic: "anthropic:sonnet-4-6",
402
- gemini: "gemini:flash-2.5",
403
- deepseek: "deepseek:deepseek-chat",
404
- moonshot: "moonshot:kimi-k2.5",
405
- "claude-code": "claude-code:sonnet",
406
- zai: "zai:glm-5",
407
- alibaba: "alibaba:qwen3-max",
408
- } as const);
478
+ export const DEFAULT_MODEL_BY_PROVIDER: Readonly<
479
+ Record<ProviderName, ModelAliasKey>
480
+ > = Object.freeze({
481
+ openai: "openai:gpt-5.4",
482
+ anthropic: "anthropic:sonnet-4-6",
483
+ gemini: "gemini:flash-2.5",
484
+ deepseek: "deepseek:v4-flash",
485
+ moonshot: "moonshot:kimi-k2.6",
486
+ "claude-code": "claude-code:sonnet",
487
+ zai: "zai:glm-5-1",
488
+ alibaba: "alibaba:qwen3-max",
489
+ } as const);
409
490
 
410
491
  // ─── Function Name Derived Index ─────────────────────────────────────────────
411
492
 
412
- export const FUNCTION_NAME_BY_ALIAS: Readonly<Record<ModelAliasKey, string>> = Object.freeze(
413
- Object.fromEntries(
414
- (Object.keys(MODEL_CONFIG) as ModelAliasKey[]).map((alias) => [
415
- alias,
416
- aliasToFunctionName(alias),
417
- ]),
418
- ) as Record<ModelAliasKey, string>,
419
- );
493
+ export const FUNCTION_NAME_BY_ALIAS: Readonly<Record<ModelAliasKey, string>> =
494
+ Object.freeze(
495
+ Object.fromEntries(
496
+ (Object.keys(MODEL_CONFIG) as ModelAliasKey[]).map((alias) => [
497
+ alias,
498
+ aliasToFunctionName(alias),
499
+ ]),
500
+ ) as Record<ModelAliasKey, string>,
501
+ );
420
502
 
421
503
  // ─── Provider Functions Index ────────────────────────────────────────────────
422
504
 
@@ -438,7 +520,13 @@ export function buildProviderFunctionsIndex(): ProviderFunctionsIndex {
438
520
  index[provider] = [];
439
521
  }
440
522
  index[provider]!.push(
441
- Object.freeze({ alias, provider, model, functionName, fullPath }) as ProviderFunctionEntry,
523
+ Object.freeze({
524
+ alias,
525
+ provider,
526
+ model,
527
+ functionName,
528
+ fullPath,
529
+ }) as ProviderFunctionEntry,
442
530
  );
443
531
  }
444
532
 
@@ -450,7 +538,8 @@ export function buildProviderFunctionsIndex(): ProviderFunctionsIndex {
450
538
  return Object.freeze(index) as ProviderFunctionsIndex;
451
539
  }
452
540
 
453
- export const PROVIDER_FUNCTIONS: ProviderFunctionsIndex = buildProviderFunctionsIndex();
541
+ export const PROVIDER_FUNCTIONS: ProviderFunctionsIndex =
542
+ buildProviderFunctionsIndex();
454
543
 
455
544
  // ─── Module-Load Invariant Validation ────────────────────────────────────────
456
545
 
@@ -497,13 +586,19 @@ export function validateModelRegistry(
497
586
  }
498
587
 
499
588
  // Check non-negative token costs
500
- if (typeof entry.tokenCostInPerMillion !== "number" || entry.tokenCostInPerMillion < 0) {
589
+ if (
590
+ typeof entry.tokenCostInPerMillion !== "number" ||
591
+ entry.tokenCostInPerMillion < 0
592
+ ) {
501
593
  throw new Error(
502
594
  `Model config invariant violation: alias "${alias}" has invalid tokenCostInPerMillion: ` +
503
595
  `${entry.tokenCostInPerMillion}`,
504
596
  );
505
597
  }
506
- if (typeof entry.tokenCostOutPerMillion !== "number" || entry.tokenCostOutPerMillion < 0) {
598
+ if (
599
+ typeof entry.tokenCostOutPerMillion !== "number" ||
600
+ entry.tokenCostOutPerMillion < 0
601
+ ) {
507
602
  throw new Error(
508
603
  `Model config invariant violation: alias "${alias}" has invalid tokenCostOutPerMillion: ` +
509
604
  `${entry.tokenCostOutPerMillion}`,