modelmix 5.0.1 → 5.0.3

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 (40) hide show
  1. package/README.md +111 -8
  2. package/RLM_PLUGIN_SPEC.md +465 -0
  3. package/demo/gemini.js +3 -4
  4. package/demo/grok.js +2 -2
  5. package/demo/images.js +2 -2
  6. package/demo/short.js +3 -3
  7. package/effort.js +3 -0
  8. package/index.d.ts +62 -1
  9. package/index.js +355 -49
  10. package/package.json +7 -4
  11. package/plugins/rlm/index.d.ts +194 -0
  12. package/plugins/rlm/index.js +25 -0
  13. package/plugins/rlm/lib/budget.js +153 -0
  14. package/plugins/rlm/lib/isolated-vm-sandbox.js +90 -0
  15. package/plugins/rlm/lib/markdown.js +156 -0
  16. package/plugins/rlm/lib/planner-prompt.js +137 -0
  17. package/plugins/rlm/lib/plugin.js +203 -0
  18. package/plugins/rlm/lib/runtime.js +146 -0
  19. package/plugins/rlm/lib/variable-descriptors.js +228 -0
  20. package/plugins/rlm/lib/worker-catalog.js +70 -0
  21. package/plugins/rlm/package.json +32 -0
  22. package/plugins/rlm/prompts/partials/processing-rules.md +8 -0
  23. package/plugins/rlm/prompts/planner.md +53 -0
  24. package/plugins/rlm/test/budget.test.js +86 -0
  25. package/plugins/rlm/test/fixtures/book.md +24 -0
  26. package/plugins/rlm/test/isolated-vm-sandbox.test.js +114 -0
  27. package/plugins/rlm/test/markdown.test.js +64 -0
  28. package/plugins/rlm/test/planner-template.test.js +140 -0
  29. package/plugins/rlm/test/plugin-contract.test.js +182 -0
  30. package/plugins/rlm/test/rlm-e2e.test.js +338 -0
  31. package/plugins/rlm/test/variable-descriptors.test.js +170 -0
  32. package/plugins/rlm/test/worker-catalog.test.js +104 -0
  33. package/pnpm-workspace.yaml +6 -0
  34. package/skills/modelmix/SKILL.md +23 -4
  35. package/test/effort.test.js +14 -1
  36. package/test/grok.test.js +74 -0
  37. package/test/live.mcp.js +8 -8
  38. package/test/live.test.js +9 -9
  39. package/test/plugins.test.js +356 -0
  40. package/test/tokens.test.js +37 -5
package/demo/images.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import { ModelMix } from '../index.js';
2
2
  try { process.loadEnvFile(); } catch {}
3
3
 
4
- const model = ModelMix.new({ config: { max_history: 2, debug: 2 } }).grok43()
4
+ const model = ModelMix.new({ config: { max_history: 2, debug: 2 } }).grok46()
5
5
  // model.addImageFromUrl('https://pbs.twimg.com/media/F6-GsjraAAADDGy?format=jpg');
6
6
  model.addImage('./img.png');
7
7
  model.addText('in one word, which is the main color of the image?');
8
8
 
9
- console.log(await model.json({ color: "string" }));
9
+ console.log(await model.json({ color: "string" }));
package/demo/short.js CHANGED
@@ -11,9 +11,9 @@ const setup = {
11
11
  const mmix = await ModelMix.new(setup)
12
12
  .sonnet46() // (main model) Anthropic claude-sonnet-4-6
13
13
  .gpt56luna() // (fallback 1) OpenAI gpt-5.6-luna
14
- .gemini36flash({ config: { temperature: 0 } }) // (fallback 2) Google gemini-3.6-flash
14
+ .gemini37flash() // (fallback 2) Google gemini-3.7-flash
15
15
  .gpt41nano() // (fallback 3) OpenAI gpt-4.1-nano
16
- .grok43() // (fallback 4) Grok grok-4.3
16
+ .grok46() // (fallback 4) Grok grok-4.6
17
17
  .addText("What's your name?");
18
18
 
19
- console.log(await mmix.message());
19
+ console.log(await mmix.message());
package/effort.js CHANGED
@@ -35,6 +35,7 @@ const GEMINI_BANDS = [
35
35
 
36
36
  /** Exact model → supported OpenAI reasoning_effort values */
37
37
  const OPENAI_MODEL_LEVELS = {
38
+ 'grok-4.6': ['low', 'medium', 'high', 'xhigh'],
38
39
  'gpt-5': ['minimal', 'low', 'medium', 'high'],
39
40
  'gpt-5-mini': ['minimal', 'low', 'medium', 'high'],
40
41
  'gpt-5-nano': ['minimal', 'low', 'medium', 'high'],
@@ -69,6 +70,7 @@ const MINIMAX_BANDS = [
69
70
 
70
71
  /** Exact model → supported Gemini thinkingLevel values */
71
72
  const GEMINI_MODEL_LEVELS = {
73
+ 'gemini-3.7-flash': ['low', 'medium', 'high'],
72
74
  'gemini-3-pro-preview': ['low', 'high'],
73
75
  'gemini-3.1-pro-preview': ['low', 'medium', 'high'],
74
76
  'gemini-2.5-pro': ['low', 'medium', 'high'],
@@ -289,6 +291,7 @@ function mapAdaptiveEffort(providerFamily, modelKey) {
289
291
  return { thinking: { type: 'adaptive' } };
290
292
  }
291
293
  if (providerFamily === 'google') {
294
+ if (modelKey === 'gemini-3.7-flash') return null;
292
295
  // Gemini dynamic thinking: thinkingBudget -1 (2.5 official; accepted on 3.x as dynamic)
293
296
  return { thinkingConfig: { thinkingBudget: -1 } };
294
297
  }
package/index.d.ts CHANGED
@@ -161,6 +161,8 @@ export interface TokenCostBreakdown {
161
161
  export interface TokenUsage {
162
162
  input: number;
163
163
  output: number;
164
+ /** Internal reasoning tokens billed at the output rate when reported separately. */
165
+ thinking: number;
164
166
  total: number;
165
167
  cached: number;
166
168
  cacheWrite: number;
@@ -187,9 +189,59 @@ export interface ModelMixResult {
187
189
  tokens?: TokenUsage;
188
190
  response?: unknown;
189
191
  assistantMessage?: ChatMessage;
192
+ execution?: PluginExecutionMetadata;
190
193
  [key: string]: unknown;
191
194
  }
192
195
 
196
+ export type ModelMixOutputMode = 'message' | 'json' | 'block' | 'raw' | 'stream';
197
+
198
+ export interface PluginExecutionMetadata {
199
+ executionId: string;
200
+ parentExecutionId: string | null;
201
+ depth: number;
202
+ }
203
+
204
+ export type PluginInheritancePolicy =
205
+ | 'inherit'
206
+ | 'none'
207
+ | { include: string[] }
208
+ | { exclude: string[] };
209
+
210
+ export interface ChildInvocation {
211
+ system?: string;
212
+ systemFile?: string;
213
+ assign?: Record<string, unknown>;
214
+ messages: ChatMessage[];
215
+ tools?: ToolWithCallback[];
216
+ options?: ModelMixOptions;
217
+ config?: ModelMixConfig;
218
+ mix?: ModelMixMixFlags;
219
+ model?: ModelMix;
220
+ plugins?: PluginInheritancePolicy;
221
+ history?: false;
222
+ outputMode?: ModelMixOutputMode;
223
+ }
224
+
225
+ export interface PluginExecutionContext {
226
+ request: {
227
+ system: string;
228
+ messages: ChatMessage[];
229
+ options: ModelMixOptions;
230
+ config: ModelMixConfig;
231
+ outputMode: ModelMixOutputMode;
232
+ };
233
+ execution: Readonly<PluginExecutionMetadata>;
234
+ invoke(input: ChildInvocation): Promise<ModelMixResult>;
235
+ }
236
+
237
+ export interface ModelMixPlugin {
238
+ name: string;
239
+ execute(
240
+ context: PluginExecutionContext,
241
+ next: () => Promise<ModelMixResult>
242
+ ): Promise<ModelMixResult>;
243
+ }
244
+
193
245
  export interface StreamChunk {
194
246
  response: unknown;
195
247
  message: string;
@@ -263,6 +315,7 @@ export interface ProviderConstructorArgs {
263
315
  export interface CreateArgs {
264
316
  config?: ModelMixConfig;
265
317
  options?: ModelMixOptions;
318
+ outputMode?: ModelMixOutputMode;
266
319
  }
267
320
 
268
321
  export type ProviderFamily =
@@ -279,6 +332,7 @@ export declare class ModelMix {
279
332
  tools: Record<string, ToolDefinition[]>;
280
333
  toolClient: Record<string, unknown>;
281
334
  mcp: Record<string, unknown>;
335
+ plugins: ModelMixPlugin[];
282
336
  options: ModelMixOptions;
283
337
  config: ModelMixConfig;
284
338
  mix: ModelMixMixFlags;
@@ -288,6 +342,7 @@ export declare class ModelMix {
288
342
  constructor(setup?: ModelMixSetup);
289
343
 
290
344
  static new(setup?: ModelMixSetup): ModelMix;
345
+ use(plugin: ModelMixPlugin): this;
291
346
  static formatJSON(obj: unknown): string;
292
347
  static formatMessage(message: unknown): unknown;
293
348
  static truncate(str: string, maxLen?: number): string;
@@ -296,6 +351,7 @@ export declare class ModelMix {
296
351
  tokens: {
297
352
  input: number;
298
353
  output: number;
354
+ thinking?: number;
299
355
  total?: number;
300
356
  cached?: number;
301
357
  cacheWrite?: number;
@@ -308,6 +364,7 @@ export declare class ModelMix {
308
364
  tokens: {
309
365
  input: number;
310
366
  output: number;
367
+ thinking?: number;
311
368
  total?: number;
312
369
  cached?: number;
313
370
  cacheWrite?: number;
@@ -320,6 +377,7 @@ export declare class ModelMix {
320
377
  tokens: {
321
378
  input: number;
322
379
  output: number;
380
+ thinking?: number;
323
381
  total?: number;
324
382
  cached?: number;
325
383
  cacheWrite?: number;
@@ -330,6 +388,7 @@ export declare class ModelMix {
330
388
  static normalizeTokenUsage(tokens?: {
331
389
  input?: number;
332
390
  output?: number;
391
+ thinking?: number;
333
392
  total?: number;
334
393
  cached?: number;
335
394
  cacheWrite?: number;
@@ -395,6 +454,7 @@ export declare class ModelMix {
395
454
  gemini31pro(args?: ModelAttachArgs): this;
396
455
  gemini3pro(args?: ModelAttachArgs): this;
397
456
  gemini3flash(args?: ModelAttachArgs): this;
457
+ gemini37flash(args?: ModelAttachArgs): this;
398
458
  gemini36flash(args?: ModelAttachArgs): this;
399
459
  gemini35flash(args?: ModelAttachArgs): this;
400
460
  gemini35flashLite(args?: ModelAttachArgs): this;
@@ -406,6 +466,7 @@ export declare class ModelMix {
406
466
  sonar(args?: ModelAttachArgs): this;
407
467
 
408
468
  // Grok
469
+ grok46(args?: ModelAttachArgs): this;
409
470
  grok45(args?: ModelAttachArgs): this;
410
471
  grok43(args?: ModelAttachArgs): this;
411
472
  grok420multiAgent(args?: ModelAttachArgs): this;
@@ -454,7 +515,7 @@ export declare class ModelMix {
454
515
 
455
516
  assignKeyFromFile(key: string, filePath: string): this;
456
517
  groupByRoles(messages: ChatMessage[]): ChatMessage[];
457
- prepareMessages(): Promise<void>;
518
+ prepareMessages(): Promise<ChatMessage[]>;
458
519
  readFile(filePath: string, options?: { encoding?: BufferEncoding | null }): string | Buffer;
459
520
  execute(args?: CreateArgs): Promise<ModelMixResult>;
460
521
  processToolCalls(toolCalls: ToolCall[]): Promise<