modelmix 5.1.12 → 5.1.16

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/README.md CHANGED
@@ -88,13 +88,33 @@ const setup = {
88
88
  const model = await ModelMix.new(setup)
89
89
  .sonnet5() // (main model) Anthropic claude-sonnet-5
90
90
  .gpt56luna() // (fallback 2) OpenAI gpt-5.6-luna
91
- .gemini37flash() // (fallback 3) Google gemini-3.7-flash
91
+ .gemini38flash() // (fallback 3) Google gemini-3.8-flash
92
92
  .grok46() // (fallback 4) Grok grok-4.6
93
93
  .addText("What's your name?");
94
94
 
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
@@ -102,7 +122,7 @@ has no configured effort:
102
122
 
103
123
  ```javascript
104
124
  const model = ModelMix.new(setup)
105
- .chain('sonnet5', 'gpt56luna@20', 'gemini37flash@-1')
125
+ .chain('sonnet5', 'gpt56luna@20', 'gemini38flash@-1')
106
126
  .addText("What's your name?");
107
127
 
108
128
  console.log(await model.message());
@@ -165,6 +185,7 @@ ModelMix provides convenient shorthand methods for quickly accessing different A
165
185
  | `sonnet46()` | Anthropic | claude-sonnet-4-6 | [\$3.00][2] | [\$15.00][2] |
166
186
  | `haiku45()` | Anthropic | claude-haiku-4-5-20251001 | [\$1.00][2] | [\$5.00][2] |
167
187
  | `gemini31pro()` | Google | gemini-3.1-pro-preview | [\$2.00][3] | [\$12.00][3] |
188
+ | `gemini38flash()` | Google | gemini-3.8-flash | [\$0.75][3] | [\$3.75][3] |
168
189
  | `gemini37flash()` | Google | gemini-3.7-flash | [\$0.75][3] | [\$3.75][3] |
169
190
  | `gemini36flash()` | Google | gemini-3.6-flash | [\$0.75][3] | [\$3.75][3] |
170
191
  | `gemini35flash()` | Google | gemini-3.5-flash | [\$0.75][3] | [\$4.50][3] |
@@ -200,7 +221,7 @@ ModelMix provides convenient shorthand methods for quickly accessing different A
200
221
  | `kimiK25()` | Together | Kimi-K2.5 | [\$0.50][7] | [\$2.80][7] |
201
222
  | `kimiK26()` | Fireworks | models/kimi-k2p6 | [\$0.95][10] | [\$4.00][10] |
202
223
 
203
- 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.
224
+ Gemini 3.8 Flash, 3.7 Flash, and 3.6 Flash use Google's introductory standard pricing through December 31, 2026; standard rates double on January 1, 2027.
204
225
 
205
226
  `museGlimmer30b()` uses Fireworks by default. OpenRouter, NVIDIA NIM, and Together are available through `mix.openrouter`, `mix.nvidia`, and `mix.together`.
206
227
 
@@ -271,7 +292,7 @@ ModelMix.new().effort(-1).minimaxM3().addText('...').message();
271
292
 
272
293
  ### Provider-specific behavior
273
294
 
274
- - **Gemini:** Gemini 3+ uses bands 0–24 / 25–49 / 50–74 / 75–100. Gemini 3.7 Flash clamps these bands to `low` / `low` / `medium` / `high`; `-1` leaves its native `medium` default unchanged. Gemini 2.5 maps 0–100 to `thinkingBudget`.
295
+ - **Gemini:** Gemini 3+ uses bands 0–24 / 25–49 / 50–74 / 75–100. Gemini 3.8 Flash and 3.7 Flash clamp these bands to `low` / `low` / `medium` / `high`; `-1` leaves their native `medium` default unchanged. Gemini 2.5 maps 0–100 to `thinkingBudget`.
275
296
  - **GPT-5.6:** `100` maps to `max`; 80–99 remains `xhigh`.
276
297
  - **Qwen 3.8 27B and Flash:** 0–39 / 40–79 / 80–100 map to `low` / `medium` / `xhigh`; `-1` leaves the native `xhigh` default unchanged. Qwen 3.8 Flash is the managed production version based on the open-weight Flash-Next architecture.
277
298
  - **GLM 5.3 and GLM 5.3 Flash:** reasoning is mandatory; 0–39 / 40–79 / 80–100 map to `low` / `high` / `max`; `-1` leaves the native `max` default unchanged.
@@ -939,7 +960,7 @@ const model = ModelMix.new()
939
960
  .addText('Summarize this request.');
940
961
  ```
941
962
 
942
- 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:
963
+ 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:
943
964
 
944
965
  ```javascript
945
966
  const child = await context.invoke({
@@ -1050,8 +1071,8 @@ new ModelMix(args = { options: {}, config: {} })
1050
1071
  - `assign(keyValues)`: Assigns EJS data for messages and system prompts.
1051
1072
  - `assignKey(key, value)`: Assigns one EJS data value.
1052
1073
  - `assignKeyFromFile(key, filePath)`: Renders an EJS file through `include` and assigns its output to one key.
1053
- - `message()`: Sends the message and returns the response.
1054
- - `raw()`: Sends the message and returns the complete response data including:
1074
+ - `message(signal?)`: Sends the message and returns the response.
1075
+ - `raw(signal?)`: Sends the message and returns the complete response data including:
1055
1076
  - `message`: The text response from the model
1056
1077
  - `think`: Reasoning/thinking content (if available)
1057
1078
  - `toolCalls`: Array of tool calls made by the model (if any)
@@ -1069,8 +1090,8 @@ new ModelMix(args = { options: {}, config: {} })
1069
1090
 
1070
1091
  if (profile.flagged) throw new Error('Profile rejected by moderation');
1071
1092
  ```
1072
- - `stream(callback)`: Sends the message and streams the response, invoking the callback with each streamed part.
1073
- - `json(schemaExample, descriptions = {}, options = {})`: Forces the model to return a response in a specific JSON format.
1093
+ - `stream(callback, signal?)`: Sends the message and streams the response, invoking the callback with each streamed part.
1094
+ - `json(schemaExample, descriptions = {}, options = {}, signal?)`: Forces the model to return a response in a specific JSON format.
1074
1095
  - `schemaExample`: Example of the JSON structure to be returned. Top-level arrays are auto-wrapped for better LLM compatibility.
1075
1096
  - `descriptions`: Descriptions for each field — can be strings or descriptor objects with `{ description, required, enum, default }`.
1076
1097
  - `options`: `{ addSchema: true, addExample: false, addNote: false }`
package/demo/gemini.js CHANGED
@@ -12,9 +12,9 @@ const mmix = new ModelMix({
12
12
  }
13
13
  });
14
14
 
15
- // Using Gemini 3.7 Flash with the built-in method
16
- console.log("\n" + '--------| gemini37flash() |--------');
17
- const flash = await mmix.gemini37flash()
15
+ // Using Gemini 3.8 Flash with the built-in method
16
+ console.log("\n" + '--------| gemini38flash() |--------');
17
+ const flash = await mmix.gemini38flash()
18
18
  .addText('Hi there! Do you like cats?')
19
19
  .message();
20
20
 
package/demo/short.js CHANGED
@@ -12,7 +12,7 @@ const mmix = await ModelMix.new(setup)
12
12
  .fable51({ mix: { openrouter: true } }) // (main + provider fallback) Anthropic/OpenRouter Claude Fable 5.1
13
13
  .sonnet46() // (fallback 2) Anthropic claude-sonnet-4-6
14
14
  .gpt56luna({ mix: { openrouter: true } }) // (fallback 3 + provider fallback) OpenAI/OpenRouter gpt-5.6-luna
15
- .gemini37flash() // (fallback 4) Google gemini-3.7-flash
15
+ .gemini38flash() // (fallback 4) Google gemini-3.8-flash
16
16
  .gpt5nano({ mix: { openrouter: true } }) // (fallback 5 + provider fallback) OpenAI/OpenRouter gpt-5-nano
17
17
  .grok46() // (fallback 6) Grok grok-4.6
18
18
  .qwen35397b() // (fallback 7) OpenRouter qwen/qwen3.5-397b-a17b
package/effort.js CHANGED
@@ -96,6 +96,7 @@ const MINIMAX_BANDS = [
96
96
 
97
97
  /** Exact model → supported Gemini thinkingLevel values */
98
98
  const GEMINI_MODEL_LEVELS = {
99
+ 'gemini-3.8-flash': ['low', 'medium', 'high'],
99
100
  'gemini-3.7-flash': ['low', 'medium', 'high'],
100
101
  'gemini-3-pro-preview': ['low', 'high'],
101
102
  'gemini-3.1-pro-preview': ['low', 'medium', 'high'],
@@ -320,7 +321,7 @@ function mapAdaptiveEffort(providerFamily, modelKey) {
320
321
  return { thinking: { type: 'adaptive' } };
321
322
  }
322
323
  if (providerFamily === 'google') {
323
- if (modelKey === 'gemini-3.7-flash') return null;
324
+ if (modelKey === 'gemini-3.8-flash' || modelKey === 'gemini-3.7-flash') return null;
324
325
  // Gemini dynamic thinking: thinkingBudget -1 (2.5 official; accepted on 3.x as dynamic)
325
326
  return { thinkingConfig: { thinkingBudget: -1 } };
326
327
  }
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
@@ -259,6 +259,7 @@ export interface PluginExecutionContext {
259
259
  outputMode: ModelMixOutputMode;
260
260
  };
261
261
  execution: Readonly<PluginExecutionMetadata>;
262
+ signal?: AbortSignal;
262
263
  invoke(input: ChildInvocation): Promise<ModelMixResult>;
263
264
  }
264
265
 
@@ -312,7 +313,8 @@ export interface ToolDefinition {
312
313
  }
313
314
 
314
315
  export type ToolCallback = (
315
- args: Record<string, unknown>
316
+ args: Record<string, unknown>,
317
+ signal?: AbortSignal
316
318
  ) => unknown | Promise<unknown>;
317
319
 
318
320
  export interface ToolWithCallback {
@@ -343,6 +345,7 @@ export interface ProviderConstructorArgs {
343
345
  export interface CreateArgs {
344
346
  config?: ModelMixConfig;
345
347
  options?: ModelMixOptions;
348
+ signal?: AbortSignal;
346
349
  outputMode?: ModelMixOutputMode;
347
350
  }
348
351
 
@@ -479,6 +482,7 @@ export declare class ModelMix {
479
482
 
480
483
  // Google
481
484
  gemini31pro(args?: ModelAttachArgs): this;
485
+ gemini38flash(args?: ModelAttachArgs): this;
482
486
  gemini37flash(args?: ModelAttachArgs): this;
483
487
  gemini36flash(args?: ModelAttachArgs): this;
484
488
  gemini35flash(args?: ModelAttachArgs): this;
@@ -533,15 +537,16 @@ export declare class ModelMix {
533
537
  addImageFromUrl(url: string, options?: RoleOptions): Promise<this>;
534
538
  processImages(): Promise<void>;
535
539
 
536
- message(): Promise<string>;
540
+ message(signal?: AbortSignal): Promise<string>;
537
541
  json<T = unknown>(
538
542
  schemaExample?: T | T[] | null,
539
543
  schemaDescription?: SchemaDescription,
540
- options?: JsonMethodOptions
544
+ options?: JsonMethodOptions,
545
+ signal?: AbortSignal
541
546
  ): Promise<T>;
542
- block(options?: BlockOptions): Promise<string>;
543
- raw(): Promise<ModelMixResult>;
544
- stream(callback: StreamCallback): Promise<ModelMixResult>;
547
+ block(options?: BlockOptions, signal?: AbortSignal): Promise<string>;
548
+ raw(signal?: AbortSignal): Promise<ModelMixResult>;
549
+ stream(callback: StreamCallback, signal?: AbortSignal): Promise<ModelMixResult>;
545
550
 
546
551
  assignKeyFromFile(key: string, filePath: string): this;
547
552
  groupByRoles(messages: ChatMessage[]): ChatMessage[];