modelmix 5.1.10 → 5.1.15

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 (45) hide show
  1. package/README.md +42 -10
  2. package/demo/free.js +1 -3
  3. package/demo/short.js +15 -13
  4. package/effort.js +13 -7
  5. package/http-client.js +6 -6
  6. package/index.d.ts +13 -6
  7. package/index.js +204 -144
  8. package/lib/abort-signal.js +57 -0
  9. package/lib/model-chain.js +2 -2
  10. package/lib/provider-api-key.js +9 -0
  11. package/lib/providers/anthropic.js +24 -22
  12. package/lib/providers/base.js +21 -28
  13. package/lib/providers/google.js +12 -8
  14. package/lib/providers/openai-compatible.js +19 -48
  15. package/lib/providers/openai-options.js +16 -0
  16. package/lib/providers/openai.js +48 -47
  17. package/lib/providers.js +5 -2
  18. package/lib/token-usage.js +19 -4
  19. package/mcp-tools.js +5 -2
  20. package/package.json +3 -2
  21. package/plugins/rlm/index.d.ts +1 -0
  22. package/plugins/rlm/lib/budget.js +2 -7
  23. package/plugins/rlm/lib/isolated-vm-sandbox.js +8 -1
  24. package/plugins/rlm/lib/planner-prompt.js +1 -7
  25. package/plugins/rlm/lib/plugin.js +24 -11
  26. package/plugins/rlm/lib/validation.js +14 -0
  27. package/plugins/rlm/lib/variable-descriptors.js +1 -5
  28. package/plugins/rlm/lib/worker-catalog.js +1 -5
  29. package/plugins/rlm/test/isolated-vm-sandbox.test.js +23 -0
  30. package/pnpm-workspace.yaml +10 -5
  31. package/schema.js +0 -28
  32. package/skills/modelmix/SKILL.md +34 -12
  33. package/test/abort.test.js +517 -0
  34. package/test/anthropic.test.js +77 -1
  35. package/test/bottleneck.test.js +2 -2
  36. package/test/effort.test.js +24 -1
  37. package/test/fallback.test.js +101 -1
  38. package/test/glm.test.js +2 -6
  39. package/test/kimi.test.js +2 -6
  40. package/test/live.test.js +10 -5
  41. package/test/muse.test.js +24 -7
  42. package/test/provider-expansion.test.js +17 -0
  43. package/test/public-api.test.js +2 -2
  44. package/test/qwen.test.js +2 -3
  45. package/test/tokens.test.js +23 -8
package/README.md CHANGED
@@ -95,6 +95,26 @@ const model = await ModelMix.new(setup)
95
95
  console.log(await model.message());
96
96
  ```
97
97
 
98
+ **Cancel one execution with AbortSignal**
99
+
100
+ Pass the signal directly to the terminal method. ModelMix propagates it through
101
+ queued work, retries, fallbacks, provider transports, plugins, child executions,
102
+ and tool calls:
103
+
104
+ ```javascript
105
+ const controller = new AbortController();
106
+ const response = model.message(controller.signal);
107
+
108
+ controller.abort(new Error('Request cancelled'));
109
+ await response;
110
+ ```
111
+
112
+ Use `raw(signal)`, `stream(callback, signal)`, `block(options, signal)`, or
113
+ `json(example, description, jsonOptions, signal)` for the other output modes.
114
+ `execute()` accepts it as the top-level `signal` field. Never put a signal in
115
+ ModelMix or provider `config`/`options`; those locations are rejected because
116
+ they are reusable and may become provider payload fields.
117
+
98
118
  The same ordered chain can be attached by passing model shortcuts directly to
99
119
  `chain()`. Add `@effort` to override unified effort for one model; entries
100
120
  without it inherit the chain effort, or use the provider default when the chain
@@ -119,10 +139,9 @@ console.log(ETH.price);
119
139
 
120
140
  **This example uses providers with free quotas (Groq, Cerebras, and Together). OpenRouter is disabled because its GPT-OSS 120B route is no longer free. If one model runs out of quota, ModelMix automatically falls back to the next model in the chain.**
121
141
  ```javascript
122
- ModelMix.new({ mix: { openrouter: false } })
142
+ ModelMix.new()
123
143
  .gptOss()
124
144
  .kimiK25()
125
- .hermes3()
126
145
  .addText('What is the capital of France?');
127
146
  ```
128
147
 
@@ -143,15 +162,20 @@ ModelMix provides convenient shorthand methods for quickly accessing different A
143
162
  | `gpt56terra()` | OpenAI | gpt-5.6-terra | [\$2.00][1] | [\$12.00][1] |
144
163
  | `gpt56luna()` | OpenAI | gpt-5.6-luna | [\$0.20][1] | [\$1.20][1] |
145
164
  | `gpt55()` | OpenAI | gpt-5.5 | [\$5.00][1] | [\$30.00][1] |
165
+ | `gpt55pro()` | OpenAI | gpt-5.5-pro | [\$30.00][1] | [\$180.00][1] |
146
166
  | `gpt54()` | OpenAI | gpt-5.4 | [\$2.50][1] | [\$15.00][1] |
147
167
  | `gpt54mini()` | OpenAI | gpt-5.4-mini | [\$0.75][1] | [\$4.50][1] |
148
168
  | `gpt54nano()` | OpenAI | gpt-5.4-nano | [\$0.20][1] | [\$1.25][1] |
149
- | `gpt53codex()` | OpenAI | gpt-5.3-codex | [\$1.25][1] | [\$14.00][1] |
169
+ | `gpt54pro()` | OpenAI | gpt-5.4-pro | [\$30.00][1] | [\$180.00][1] |
170
+ | `gpt53codex()` | OpenAI | gpt-5.3-codex | [\$1.75][1] | [\$14.00][1] |
171
+ | `gpt53chat()` | OpenAI | gpt-5.3-chat-latest | [\$1.75][1] | [\$14.00][1] |
150
172
  | `gpt52()` | OpenAI | gpt-5.2 | [\$1.75][1] | [\$14.00][1] |
151
173
  | `gpt51()` | OpenAI | gpt-5.1 | [\$1.25][1] | [\$10.00][1] |
174
+ | `gpt5()` | OpenAI | gpt-5 | [\$1.25][1] | [\$10.00][1] |
152
175
  | `gpt5mini()` | OpenAI | gpt-5-mini | [\$0.25][1] | [\$2.00][1] |
153
176
  | `gpt5nano()` | OpenAI | gpt-5-nano | [\$0.05][1] | [\$0.40][1] |
154
177
  | `gptOss()` | Multi-provider | gpt-oss-120B | [\$0.15][7] | [\$0.60][7] |
178
+ | `fable51()` | Anthropic | claude-fable-5-1 | [\$10.00][2] | [\$50.00][2] |
155
179
  | `fable5()` | Anthropic | claude-fable-5 | [\$10.00][2] | [\$50.00][2] |
156
180
  | `opus5()` | Anthropic | claude-opus-5 | [\$5.00][2] | [\$25.00][2] |
157
181
  | `opus48()` | Anthropic | claude-opus-4-8 | [\$5.00][2] | [\$25.00][2] |
@@ -172,6 +196,7 @@ ModelMix provides convenient shorthand methods for quickly accessing different A
172
196
  | `grok420multiAgent()` | Grok | grok-4.20-multi-agent-0309 | [\$1.25][6] | [\$2.50][6] |
173
197
  | `grok420()` | Grok | grok-4.20-0309 (†) | [\$1.25][6] | [\$2.50][6] |
174
198
  | `museGlimmer30b()` | Fireworks | models/muse-glimmer-30b | [\$0.35][17] | [\$1.50][17] |
199
+ | `museSpark12Contributor()` | OpenRouter | meta/muse-spark-1.2-contributor | [\$0.10][22] | [\$0.20][22] |
175
200
  | `qwen35397b()` | OpenRouter | qwen/qwen3.5-397b-a17b | [\$0.385][14] | [\$2.45][14] |
176
201
  | `qwen36plus()` | OpenRouter | qwen/qwen3.6-plus | [\$0.325][18] | [\$1.95][18] |
177
202
  | `qwen37plus()` | Fireworks | models/qwen3p7-plus | [\$0.40][10] | [\$1.60][10] |
@@ -197,9 +222,13 @@ ModelMix provides convenient shorthand methods for quickly accessing different A
197
222
 
198
223
  Gemini 3.7 Flash and 3.6 Flash use Google's introductory standard pricing through December 31, 2026; standard rates double on January 1, 2027.
199
224
 
200
- `museGlimmer30b()` uses Fireworks first and OpenRouter as its default fallback. NVIDIA NIM and Together are also available through `mix.nvidia` and `mix.together`; disable either default provider with `mix.fireworks: false` or `mix.openrouter: false`.
225
+ `museGlimmer30b()` uses Fireworks by default. OpenRouter, NVIDIA NIM, and Together are available through `mix.openrouter`, `mix.nvidia`, and `mix.together`.
226
+
227
+ `fable51()` uses the official Anthropic API by default (`claude-fable-5-1`). Pass `mix: { openrouter: true }` to append [`anthropic/claude-fable-5.1`][21] as its fallback.
228
+
229
+ Every textual GPT-5 shortcut in the table uses the official OpenAI API by default. Pass `mix: { openrouter: true }` to `ModelMix.new()` or to an individual shortcut to append the matching [`openai/*` OpenRouter route][23] as its fallback. `gpt53chat()` maps the official `gpt-5.3-chat-latest` alias to `openai/gpt-5.3-chat`. Realtime shortcuts remain official-only because they use OpenAI's WebSocket transport.
201
230
 
202
- The multi-provider shortcuts also expose the current catalog alternatives: `gptOss()` supports NVIDIA and Fireworks; `qwen37plus()` supports Together; `kimiK27Code()` supports Fireworks and OpenRouter; `kimiK3()` supports Fireworks, OpenRouter, and Together; `GLM52()` supports Fireworks and OpenRouter; and both MiniMax shortcuts support Fireworks. `minimaxM27()` keeps every enabled provider in its fallback chain. `qwen36plus()` now defaults to OpenRouter because Fireworks retired its serverless deployment; Fireworks remains available explicitly for private or on-demand deployments.
231
+ OpenRouter fallbacks are disabled globally by default and are appended only with `mix.openrouter: true`. Shortcuts whose primary provider is OpenRouter, such as `qwen36plus()`, are unaffected. The multi-provider shortcuts also expose the current catalog alternatives: `gptOss()` supports NVIDIA and Fireworks; `qwen37plus()` supports Together; `kimiK27Code()` supports Fireworks and OpenRouter; `kimiK3()` supports Fireworks, OpenRouter, and Together; `GLM52()` supports Fireworks and OpenRouter; and both MiniMax shortcuts support Fireworks. `minimaxM27()` keeps every explicitly enabled provider in its fallback chain.
203
232
 
204
233
  [1]: https://platform.openai.com/docs/pricing "Pricing | OpenAI"
205
234
  [2]: https://docs.anthropic.com/en/docs/about-claude/pricing "Pricing - Anthropic"
@@ -221,6 +250,9 @@ The multi-provider shortcuts also expose the current catalog alternatives: `gptO
221
250
  [18]: https://openrouter.ai/qwen/qwen3.6-plus "Qwen 3.6 Plus on OpenRouter"
222
251
  [19]: https://openrouter.ai/qwen/qwen3.8-flash "Qwen3.8 Flash on OpenRouter"
223
252
  [20]: https://openrouter.ai/z-ai/glm-5.3-flash "GLM 5.3 Flash on OpenRouter"
253
+ [21]: https://openrouter.ai/anthropic/claude-fable-5.1 "Claude Fable 5.1 on OpenRouter"
254
+ [22]: https://openrouter.ai/meta/muse-spark-1.2-contributor "Muse Spark 1.2 Contributor on OpenRouter"
255
+ [23]: https://openrouter.ai/openai "OpenAI Models on OpenRouter"
224
256
 
225
257
  Each method accepts optional `options`, `config`, and (for multi-provider methods) `mix` parameters to customize behavior.
226
258
 
@@ -927,7 +959,7 @@ const model = ModelMix.new()
927
959
  .addText('Summarize this request.');
928
960
  ```
929
961
 
930
- A plugin may edit `context.request`, call `next()`, or return a complete ModelMix result itself. It can also create history-free child executions with `context.invoke()` and choose plugin inheritance:
962
+ A plugin may edit `context.request`, call `next()`, or return a complete ModelMix result itself. The current execution signal is available as `context.signal`, and child executions created with `context.invoke()` inherit it automatically. Local tool callbacks receive the same signal as their second argument. A plugin can also choose plugin inheritance:
931
963
 
932
964
  ```javascript
933
965
  const child = await context.invoke({
@@ -1038,8 +1070,8 @@ new ModelMix(args = { options: {}, config: {} })
1038
1070
  - `assign(keyValues)`: Assigns EJS data for messages and system prompts.
1039
1071
  - `assignKey(key, value)`: Assigns one EJS data value.
1040
1072
  - `assignKeyFromFile(key, filePath)`: Renders an EJS file through `include` and assigns its output to one key.
1041
- - `message()`: Sends the message and returns the response.
1042
- - `raw()`: Sends the message and returns the complete response data including:
1073
+ - `message(signal?)`: Sends the message and returns the response.
1074
+ - `raw(signal?)`: Sends the message and returns the complete response data including:
1043
1075
  - `message`: The text response from the model
1044
1076
  - `think`: Reasoning/thinking content (if available)
1045
1077
  - `toolCalls`: Array of tool calls made by the model (if any)
@@ -1057,8 +1089,8 @@ new ModelMix(args = { options: {}, config: {} })
1057
1089
 
1058
1090
  if (profile.flagged) throw new Error('Profile rejected by moderation');
1059
1091
  ```
1060
- - `stream(callback)`: Sends the message and streams the response, invoking the callback with each streamed part.
1061
- - `json(schemaExample, descriptions = {}, options = {})`: Forces the model to return a response in a specific JSON format.
1092
+ - `stream(callback, signal?)`: Sends the message and streams the response, invoking the callback with each streamed part.
1093
+ - `json(schemaExample, descriptions = {}, options = {}, signal?)`: Forces the model to return a response in a specific JSON format.
1062
1094
  - `schemaExample`: Example of the JSON structure to be returned. Top-level arrays are auto-wrapped for better LLM compatibility.
1063
1095
  - `descriptions`: Descriptions for each field — can be strings or descriptor objects with `{ description, required, enum, default }`.
1064
1096
  - `options`: `{ addSchema: true, addExample: false, addNote: false }`
package/demo/free.js CHANGED
@@ -1,12 +1,10 @@
1
1
  import { ModelMix } from '../index.js';
2
2
  try { process.loadEnvFile(); } catch {}
3
3
 
4
- const ai = ModelMix.new({ config: { debug: 2 }, mix: { openrouter: false } })
4
+ const ai = ModelMix.new({ config: { debug: 2 } })
5
5
  .gptOss()
6
6
  .kimiK25()
7
- .hermes3()
8
7
  .addText('What is the capital of France?');
9
8
 
10
9
  const response = await ai.message();
11
10
  console.log('Response from a free-tier provider:', response);
12
-
package/demo/short.js CHANGED
@@ -9,19 +9,21 @@ const setup = {
9
9
  };
10
10
 
11
11
  const mmix = await ModelMix.new(setup)
12
- .sonnet46() // (main model) Anthropic claude-sonnet-4-6
13
- .gpt56luna() // (fallback 1) OpenAI gpt-5.6-luna
14
- .gemini37flash() // (fallback 2) Google gemini-3.7-flash
15
- .gpt5nano() // (fallback 3) OpenAI gpt-5-nano
16
- .grok46() // (fallback 4) Grok grok-4.6
17
- .qwen35397b() // (fallback 5) OpenRouter qwen/qwen3.5-397b-a17b
18
- .qwen3827b() // (fallback 6) OpenRouter qwen/qwen3.8-27b
19
- .qwen38flash() // (fallback 7) OpenRouter qwen/qwen3.8-flash
20
- .GLM53() // (fallback 8) OpenRouter z-ai/glm-5.3
21
- .GLM53Flash() // (fallback 9) OpenRouter z-ai/glm-5.3-flash
22
- .museGlimmer30b({ mix: { fireworks: false, openrouter: true } }) // (fallback 10) OpenRouter meta/muse-glimmer-30b
23
- .hermes470b() // (fallback 11) OpenRouter nousresearch/hermes-4-70b
24
- .hermes4405b() // (fallback 12) OpenRouter nousresearch/hermes-4-405b
12
+ .fable51({ mix: { openrouter: true } }) // (main + provider fallback) Anthropic/OpenRouter Claude Fable 5.1
13
+ .sonnet46() // (fallback 2) Anthropic claude-sonnet-4-6
14
+ .gpt56luna({ mix: { openrouter: true } }) // (fallback 3 + provider fallback) OpenAI/OpenRouter gpt-5.6-luna
15
+ .gemini37flash() // (fallback 4) Google gemini-3.7-flash
16
+ .gpt5nano({ mix: { openrouter: true } }) // (fallback 5 + provider fallback) OpenAI/OpenRouter gpt-5-nano
17
+ .grok46() // (fallback 6) Grok grok-4.6
18
+ .qwen35397b() // (fallback 7) OpenRouter qwen/qwen3.5-397b-a17b
19
+ .qwen3827b() // (fallback 8) OpenRouter qwen/qwen3.8-27b
20
+ .qwen38flash() // (fallback 9) OpenRouter qwen/qwen3.8-flash
21
+ .GLM53() // (fallback 10) OpenRouter z-ai/glm-5.3
22
+ .GLM53Flash() // (fallback 11) OpenRouter z-ai/glm-5.3-flash
23
+ .museGlimmer30b({ mix: { fireworks: false, openrouter: true } }) // (fallback 12) OpenRouter meta/muse-glimmer-30b
24
+ .museSpark12Contributor() // (fallback 13) OpenRouter meta/muse-spark-1.2-contributor
25
+ .hermes470b() // (fallback 14) OpenRouter nousresearch/hermes-4-70b
26
+ .hermes4405b() // (fallback 15) OpenRouter nousresearch/hermes-4-405b
25
27
  .addText("What's your name?");
26
28
 
27
29
  console.log(await mmix.message());
package/effort.js CHANGED
@@ -7,7 +7,7 @@
7
7
  */
8
8
 
9
9
  const OPENAI_LEVELS = ['none', 'low', 'medium', 'high', 'xhigh'];
10
- const OPENAI_LEVEL_LADDER = [...OPENAI_LEVELS, 'max'];
10
+ const OPENAI_LEVEL_LADDER = ['none', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'];
11
11
  const ANTHROPIC_LEVELS = ['low', 'medium', 'high', 'xhigh', 'max'];
12
12
  const GEMINI_LEVELS = ['minimal', 'low', 'medium', 'high'];
13
13
 
@@ -45,9 +45,11 @@ const OPENAI_MODEL_LEVELS = {
45
45
  'gpt-5.6-sol': ['none', 'low', 'medium', 'high', 'xhigh', 'max'],
46
46
  'gpt-5.6-terra': ['none', 'low', 'medium', 'high', 'xhigh', 'max'],
47
47
  'gpt-5.6-luna': ['none', 'low', 'medium', 'high', 'xhigh', 'max'],
48
+ 'anthropic/claude-fable-5.1': ['low', 'medium', 'high', 'xhigh', 'max'],
48
49
  'meta/muse-glimmer-30b': ['low', 'medium', 'high'],
49
50
  'accounts/fireworks/models/muse-glimmer-30b': ['low', 'medium', 'high', 'xhigh'],
50
51
  'meta-models/Muse-Glimmer-30B': ['low', 'medium', 'high', 'xhigh'],
52
+ 'meta/muse-spark-1.2-contributor': ['minimal', 'low', 'medium', 'high', 'xhigh'],
51
53
  'accounts/fireworks/models/qwen3p8-2p4t-a95b': ['none', 'low', 'medium', 'high'],
52
54
  'qwen/qwen3.8-27b': ['low', 'medium', 'xhigh'],
53
55
  'qwen/qwen3.8-flash': ['low', 'medium', 'xhigh'],
@@ -87,7 +89,6 @@ const DEEPSEEK_BANDS = [
87
89
  * MiniMax M3 (OpenAI-compatible): thinking.type disabled | adaptive.
88
90
  * @see https://platform.minimax.io/docs/api-reference/text-openai-api
89
91
  */
90
- const MINIMAX_LEVELS = ['disabled', 'adaptive'];
91
92
  const MINIMAX_BANDS = [
92
93
  [0, 19, 'disabled'],
93
94
  [20, 100, 'adaptive'],
@@ -203,8 +204,11 @@ function pickNearestLevel(desired, ladder, supported) {
203
204
  }
204
205
 
205
206
  function supportedOpenAILevels(modelKey) {
206
- if (modelKey && OPENAI_MODEL_LEVELS[modelKey]) {
207
- return OPENAI_MODEL_LEVELS[modelKey];
207
+ const canonicalModelKey = typeof modelKey === 'string' && modelKey.startsWith('openai/')
208
+ ? modelKey.slice('openai/'.length)
209
+ : modelKey;
210
+ if (canonicalModelKey && OPENAI_MODEL_LEVELS[canonicalModelKey]) {
211
+ return OPENAI_MODEL_LEVELS[canonicalModelKey];
208
212
  }
209
213
  return OPENAI_LEVELS;
210
214
  }
@@ -400,9 +404,11 @@ function mapEffort(providerFamily, effort, modelKey) {
400
404
  const supported = supportedOpenAILevels(modelKey);
401
405
  const desired = modelKey === 'z-ai/glm-5.3' || modelKey === 'z-ai/glm-5.3-flash'
402
406
  ? levelFromBands(normalized, GLM53_BANDS)
403
- : normalized === 100 && supported.includes('max')
404
- ? 'max'
405
- : levelFromBands(normalized, OPENAI_BANDS);
407
+ : modelKey === 'anthropic/claude-fable-5.1'
408
+ ? levelFromBands(normalized, ANTHROPIC_BANDS)
409
+ : normalized === 100 && supported.includes('max')
410
+ ? 'max'
411
+ : levelFromBands(normalized, OPENAI_BANDS);
406
412
  const level = pickNearestLevel(desired, OPENAI_LEVEL_LADDER, supported);
407
413
  return { reasoning_effort: level };
408
414
  }
package/http-client.js CHANGED
@@ -45,8 +45,8 @@ async function buildHttpError(url, response) {
45
45
  return error;
46
46
  }
47
47
 
48
- async function fetchJsonResponse(url, { method = 'POST', headers = {}, body } = {}) {
49
- const response = await fetch(url, { method, headers, body });
48
+ async function fetchJsonResponse(url, { method = 'POST', headers = {}, body, signal } = {}) {
49
+ const response = await fetch(url, { method, headers, body, signal });
50
50
  if (!response.ok) {
51
51
  throw await buildHttpError(url, response);
52
52
  }
@@ -58,8 +58,8 @@ async function fetchJsonResponse(url, { method = 'POST', headers = {}, body } =
58
58
  };
59
59
  }
60
60
 
61
- async function fetchBinaryResponse(url, { method = 'GET', headers = {}, body } = {}) {
62
- const response = await fetch(url, { method, headers, body });
61
+ async function fetchBinaryResponse(url, { method = 'GET', headers = {}, body, signal } = {}) {
62
+ const response = await fetch(url, { method, headers, body, signal });
63
63
  if (!response.ok) {
64
64
  throw await buildHttpError(url, response);
65
65
  }
@@ -71,8 +71,8 @@ async function fetchBinaryResponse(url, { method = 'GET', headers = {}, body } =
71
71
  };
72
72
  }
73
73
 
74
- async function fetchStreamResponse(url, { method = 'POST', headers = {}, body } = {}) {
75
- const response = await fetch(url, { method, headers, body });
74
+ async function fetchStreamResponse(url, { method = 'POST', headers = {}, body, signal } = {}) {
75
+ const response = await fetch(url, { method, headers, body, signal });
76
76
  if (!response.ok) {
77
77
  throw await buildHttpError(url, response);
78
78
  }
package/index.d.ts CHANGED
@@ -76,6 +76,7 @@ export interface ModelMixConfig {
76
76
  }
77
77
 
78
78
  export interface ModelMixMixFlags {
79
+ anthropic?: boolean;
79
80
  openrouter?: boolean;
80
81
  cerebras?: boolean;
81
82
  groq?: boolean;
@@ -258,6 +259,7 @@ export interface PluginExecutionContext {
258
259
  outputMode: ModelMixOutputMode;
259
260
  };
260
261
  execution: Readonly<PluginExecutionMetadata>;
262
+ signal?: AbortSignal;
261
263
  invoke(input: ChildInvocation): Promise<ModelMixResult>;
262
264
  }
263
265
 
@@ -311,7 +313,8 @@ export interface ToolDefinition {
311
313
  }
312
314
 
313
315
  export type ToolCallback = (
314
- args: Record<string, unknown>
316
+ args: Record<string, unknown>,
317
+ signal?: AbortSignal
315
318
  ) => unknown | Promise<unknown>;
316
319
 
317
320
  export interface ToolWithCallback {
@@ -342,6 +345,7 @@ export interface ProviderConstructorArgs {
342
345
  export interface CreateArgs {
343
346
  config?: ModelMixConfig;
344
347
  options?: ModelMixOptions;
348
+ signal?: AbortSignal;
345
349
  outputMode?: ModelMixOutputMode;
346
350
  }
347
351
 
@@ -462,6 +466,7 @@ export declare class ModelMix {
462
466
  gptOss(args?: ModelAttachArgs): this;
463
467
 
464
468
  // Anthropic
469
+ fable51(args?: ModelAttachArgs): this;
465
470
  fable50(args?: ModelAttachArgs): this;
466
471
  fable5(args?: ModelAttachArgs): this;
467
472
  opus50(args?: ModelAttachArgs): this;
@@ -497,6 +502,7 @@ export declare class ModelMix {
497
502
 
498
503
  // Multi-provider
499
504
  museGlimmer30b(args?: ModelAttachArgs): this;
505
+ museSpark12Contributor(args?: ModelAttachArgs): this;
500
506
  qwen35397b(args?: ModelAttachArgs): this;
501
507
  qwen36plus(args?: ModelAttachArgs): this;
502
508
  qwen37plus(args?: ModelAttachArgs): this;
@@ -530,15 +536,16 @@ export declare class ModelMix {
530
536
  addImageFromUrl(url: string, options?: RoleOptions): Promise<this>;
531
537
  processImages(): Promise<void>;
532
538
 
533
- message(): Promise<string>;
539
+ message(signal?: AbortSignal): Promise<string>;
534
540
  json<T = unknown>(
535
541
  schemaExample?: T | T[] | null,
536
542
  schemaDescription?: SchemaDescription,
537
- options?: JsonMethodOptions
543
+ options?: JsonMethodOptions,
544
+ signal?: AbortSignal
538
545
  ): Promise<T>;
539
- block(options?: BlockOptions): Promise<string>;
540
- raw(): Promise<ModelMixResult>;
541
- stream(callback: StreamCallback): Promise<ModelMixResult>;
546
+ block(options?: BlockOptions, signal?: AbortSignal): Promise<string>;
547
+ raw(signal?: AbortSignal): Promise<ModelMixResult>;
548
+ stream(callback: StreamCallback, signal?: AbortSignal): Promise<ModelMixResult>;
542
549
 
543
550
  assignKeyFromFile(key: string, filePath: string): this;
544
551
  groupByRoles(messages: ChatMessage[]): ChatMessage[];