modelmix 5.0.2 → 5.0.4
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 +121 -7
- package/RLM_PLUGIN_SPEC.md +465 -0
- package/demo/gemini.js +3 -4
- package/demo/moderation.js +17 -0
- package/demo/short.js +1 -1
- package/effort.js +2 -0
- package/index.d.ts +107 -1
- package/index.js +443 -46
- package/package.json +4 -2
- package/plugins/rlm/index.d.ts +194 -0
- package/plugins/rlm/index.js +25 -0
- package/plugins/rlm/lib/budget.js +153 -0
- package/plugins/rlm/lib/isolated-vm-sandbox.js +90 -0
- package/plugins/rlm/lib/markdown.js +156 -0
- package/plugins/rlm/lib/planner-prompt.js +137 -0
- package/plugins/rlm/lib/plugin.js +203 -0
- package/plugins/rlm/lib/runtime.js +146 -0
- package/plugins/rlm/lib/variable-descriptors.js +228 -0
- package/plugins/rlm/lib/worker-catalog.js +70 -0
- package/plugins/rlm/package.json +32 -0
- package/plugins/rlm/prompts/partials/processing-rules.md +8 -0
- package/plugins/rlm/prompts/planner.md +53 -0
- package/plugins/rlm/test/budget.test.js +86 -0
- package/plugins/rlm/test/fixtures/book.md +24 -0
- package/plugins/rlm/test/isolated-vm-sandbox.test.js +114 -0
- package/plugins/rlm/test/markdown.test.js +64 -0
- package/plugins/rlm/test/planner-template.test.js +140 -0
- package/plugins/rlm/test/plugin-contract.test.js +182 -0
- package/plugins/rlm/test/rlm-e2e.test.js +338 -0
- package/plugins/rlm/test/variable-descriptors.test.js +170 -0
- package/plugins/rlm/test/worker-catalog.test.js +104 -0
- package/pnpm-workspace.yaml +6 -0
- package/skills/modelmix/SKILL.md +26 -4
- package/test/effort.test.js +14 -1
- package/test/live.mcp.js +6 -6
- package/test/live.test.js +2 -2
- package/test/moderation.test.js +135 -0
- package/test/plugins.test.js +356 -0
- package/test/tokens.test.js +37 -5
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ModerationMix } from '../index.js';
|
|
2
|
+
try { process.loadEnvFile(); } catch {}
|
|
3
|
+
|
|
4
|
+
const username = 'player_name';
|
|
5
|
+
const avatarUrl = 'https://example.com/avatar.png';
|
|
6
|
+
|
|
7
|
+
const { moderation: [profileModeration] } = await ModerationMix.new()
|
|
8
|
+
.openai()
|
|
9
|
+
.addText(username)
|
|
10
|
+
.addImageFromUrl(avatarUrl)
|
|
11
|
+
.raw();
|
|
12
|
+
|
|
13
|
+
console.log({
|
|
14
|
+
allowed: !profileModeration.flagged,
|
|
15
|
+
categories: profileModeration.categories,
|
|
16
|
+
scores: profileModeration.category_scores
|
|
17
|
+
});
|
package/demo/short.js
CHANGED
|
@@ -11,7 +11,7 @@ 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
|
-
.
|
|
14
|
+
.gemini37flash() // (fallback 2) Google gemini-3.7-flash
|
|
15
15
|
.gpt41nano() // (fallback 3) OpenAI gpt-4.1-nano
|
|
16
16
|
.grok46() // (fallback 4) Grok grok-4.6
|
|
17
17
|
.addText("What's your name?");
|
package/effort.js
CHANGED
|
@@ -70,6 +70,7 @@ const MINIMAX_BANDS = [
|
|
|
70
70
|
|
|
71
71
|
/** Exact model → supported Gemini thinkingLevel values */
|
|
72
72
|
const GEMINI_MODEL_LEVELS = {
|
|
73
|
+
'gemini-3.7-flash': ['low', 'medium', 'high'],
|
|
73
74
|
'gemini-3-pro-preview': ['low', 'high'],
|
|
74
75
|
'gemini-3.1-pro-preview': ['low', 'medium', 'high'],
|
|
75
76
|
'gemini-2.5-pro': ['low', 'medium', 'high'],
|
|
@@ -290,6 +291,7 @@ function mapAdaptiveEffort(providerFamily, modelKey) {
|
|
|
290
291
|
return { thinking: { type: 'adaptive' } };
|
|
291
292
|
}
|
|
292
293
|
if (providerFamily === 'google') {
|
|
294
|
+
if (modelKey === 'gemini-3.7-flash') return null;
|
|
293
295
|
// Gemini dynamic thinking: thinkingBudget -1 (2.5 official; accepted on 3.x as dynamic)
|
|
294
296
|
return { thinkingConfig: { thinkingBudget: -1 } };
|
|
295
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,86 @@ export interface ModelMixResult {
|
|
|
187
189
|
tokens?: TokenUsage;
|
|
188
190
|
response?: unknown;
|
|
189
191
|
assistantMessage?: ChatMessage;
|
|
192
|
+
execution?: PluginExecutionMetadata;
|
|
193
|
+
moderation?: ModerationResult[];
|
|
190
194
|
[key: string]: unknown;
|
|
191
195
|
}
|
|
192
196
|
|
|
197
|
+
export interface ModerationCategories {
|
|
198
|
+
harassment: boolean;
|
|
199
|
+
'harassment/threatening': boolean;
|
|
200
|
+
hate: boolean;
|
|
201
|
+
'hate/threatening': boolean;
|
|
202
|
+
illicit: boolean | null;
|
|
203
|
+
'illicit/violent': boolean | null;
|
|
204
|
+
'self-harm': boolean;
|
|
205
|
+
'self-harm/instructions': boolean;
|
|
206
|
+
'self-harm/intent': boolean;
|
|
207
|
+
sexual: boolean;
|
|
208
|
+
'sexual/minors': boolean;
|
|
209
|
+
violence: boolean;
|
|
210
|
+
'violence/graphic': boolean;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export type ModerationCategoryScores = Record<keyof ModerationCategories, number>;
|
|
214
|
+
export type ModerationAppliedInputTypes = Record<keyof ModerationCategories, Array<'text' | 'image'>>;
|
|
215
|
+
|
|
216
|
+
export interface ModerationResult {
|
|
217
|
+
flagged: boolean;
|
|
218
|
+
categories: ModerationCategories;
|
|
219
|
+
category_scores: ModerationCategoryScores;
|
|
220
|
+
category_applied_input_types: ModerationAppliedInputTypes;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export type ModelMixOutputMode = 'message' | 'json' | 'block' | 'raw' | 'stream';
|
|
224
|
+
|
|
225
|
+
export interface PluginExecutionMetadata {
|
|
226
|
+
executionId: string;
|
|
227
|
+
parentExecutionId: string | null;
|
|
228
|
+
depth: number;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export type PluginInheritancePolicy =
|
|
232
|
+
| 'inherit'
|
|
233
|
+
| 'none'
|
|
234
|
+
| { include: string[] }
|
|
235
|
+
| { exclude: string[] };
|
|
236
|
+
|
|
237
|
+
export interface ChildInvocation {
|
|
238
|
+
system?: string;
|
|
239
|
+
systemFile?: string;
|
|
240
|
+
assign?: Record<string, unknown>;
|
|
241
|
+
messages: ChatMessage[];
|
|
242
|
+
tools?: ToolWithCallback[];
|
|
243
|
+
options?: ModelMixOptions;
|
|
244
|
+
config?: ModelMixConfig;
|
|
245
|
+
mix?: ModelMixMixFlags;
|
|
246
|
+
model?: ModelMix;
|
|
247
|
+
plugins?: PluginInheritancePolicy;
|
|
248
|
+
history?: false;
|
|
249
|
+
outputMode?: ModelMixOutputMode;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export interface PluginExecutionContext {
|
|
253
|
+
request: {
|
|
254
|
+
system: string;
|
|
255
|
+
messages: ChatMessage[];
|
|
256
|
+
options: ModelMixOptions;
|
|
257
|
+
config: ModelMixConfig;
|
|
258
|
+
outputMode: ModelMixOutputMode;
|
|
259
|
+
};
|
|
260
|
+
execution: Readonly<PluginExecutionMetadata>;
|
|
261
|
+
invoke(input: ChildInvocation): Promise<ModelMixResult>;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export interface ModelMixPlugin {
|
|
265
|
+
name: string;
|
|
266
|
+
execute(
|
|
267
|
+
context: PluginExecutionContext,
|
|
268
|
+
next: () => Promise<ModelMixResult>
|
|
269
|
+
): Promise<ModelMixResult>;
|
|
270
|
+
}
|
|
271
|
+
|
|
193
272
|
export interface StreamChunk {
|
|
194
273
|
response: unknown;
|
|
195
274
|
message: string;
|
|
@@ -263,6 +342,7 @@ export interface ProviderConstructorArgs {
|
|
|
263
342
|
export interface CreateArgs {
|
|
264
343
|
config?: ModelMixConfig;
|
|
265
344
|
options?: ModelMixOptions;
|
|
345
|
+
outputMode?: ModelMixOutputMode;
|
|
266
346
|
}
|
|
267
347
|
|
|
268
348
|
export type ProviderFamily =
|
|
@@ -279,6 +359,7 @@ export declare class ModelMix {
|
|
|
279
359
|
tools: Record<string, ToolDefinition[]>;
|
|
280
360
|
toolClient: Record<string, unknown>;
|
|
281
361
|
mcp: Record<string, unknown>;
|
|
362
|
+
plugins: ModelMixPlugin[];
|
|
282
363
|
options: ModelMixOptions;
|
|
283
364
|
config: ModelMixConfig;
|
|
284
365
|
mix: ModelMixMixFlags;
|
|
@@ -288,6 +369,7 @@ export declare class ModelMix {
|
|
|
288
369
|
constructor(setup?: ModelMixSetup);
|
|
289
370
|
|
|
290
371
|
static new(setup?: ModelMixSetup): ModelMix;
|
|
372
|
+
use(plugin: ModelMixPlugin): this;
|
|
291
373
|
static formatJSON(obj: unknown): string;
|
|
292
374
|
static formatMessage(message: unknown): unknown;
|
|
293
375
|
static truncate(str: string, maxLen?: number): string;
|
|
@@ -296,6 +378,7 @@ export declare class ModelMix {
|
|
|
296
378
|
tokens: {
|
|
297
379
|
input: number;
|
|
298
380
|
output: number;
|
|
381
|
+
thinking?: number;
|
|
299
382
|
total?: number;
|
|
300
383
|
cached?: number;
|
|
301
384
|
cacheWrite?: number;
|
|
@@ -308,6 +391,7 @@ export declare class ModelMix {
|
|
|
308
391
|
tokens: {
|
|
309
392
|
input: number;
|
|
310
393
|
output: number;
|
|
394
|
+
thinking?: number;
|
|
311
395
|
total?: number;
|
|
312
396
|
cached?: number;
|
|
313
397
|
cacheWrite?: number;
|
|
@@ -320,6 +404,7 @@ export declare class ModelMix {
|
|
|
320
404
|
tokens: {
|
|
321
405
|
input: number;
|
|
322
406
|
output: number;
|
|
407
|
+
thinking?: number;
|
|
323
408
|
total?: number;
|
|
324
409
|
cached?: number;
|
|
325
410
|
cacheWrite?: number;
|
|
@@ -330,6 +415,7 @@ export declare class ModelMix {
|
|
|
330
415
|
static normalizeTokenUsage(tokens?: {
|
|
331
416
|
input?: number;
|
|
332
417
|
output?: number;
|
|
418
|
+
thinking?: number;
|
|
333
419
|
total?: number;
|
|
334
420
|
cached?: number;
|
|
335
421
|
cacheWrite?: number;
|
|
@@ -395,6 +481,7 @@ export declare class ModelMix {
|
|
|
395
481
|
gemini31pro(args?: ModelAttachArgs): this;
|
|
396
482
|
gemini3pro(args?: ModelAttachArgs): this;
|
|
397
483
|
gemini3flash(args?: ModelAttachArgs): this;
|
|
484
|
+
gemini37flash(args?: ModelAttachArgs): this;
|
|
398
485
|
gemini36flash(args?: ModelAttachArgs): this;
|
|
399
486
|
gemini35flash(args?: ModelAttachArgs): this;
|
|
400
487
|
gemini35flashLite(args?: ModelAttachArgs): this;
|
|
@@ -455,7 +542,7 @@ export declare class ModelMix {
|
|
|
455
542
|
|
|
456
543
|
assignKeyFromFile(key: string, filePath: string): this;
|
|
457
544
|
groupByRoles(messages: ChatMessage[]): ChatMessage[];
|
|
458
|
-
prepareMessages(): Promise<
|
|
545
|
+
prepareMessages(): Promise<ChatMessage[]>;
|
|
459
546
|
readFile(filePath: string, options?: { encoding?: BufferEncoding | null }): string | Buffer;
|
|
460
547
|
execute(args?: CreateArgs): Promise<ModelMixResult>;
|
|
461
548
|
processToolCalls(toolCalls: ToolCall[]): Promise<
|
|
@@ -511,7 +598,14 @@ export declare class MixCustom {
|
|
|
511
598
|
}
|
|
512
599
|
|
|
513
600
|
export declare class MixOpenAI extends MixCustom {}
|
|
601
|
+
export declare class MixModeration extends MixCustom {}
|
|
514
602
|
export declare class MixOpenAIResponses extends MixOpenAI {}
|
|
603
|
+
export declare class MixOpenAIModeration extends MixModeration {
|
|
604
|
+
static messagesToModerationInput(messages?: ChatMessage[]): Array<
|
|
605
|
+
| { type: 'text'; text: string }
|
|
606
|
+
| { type: 'image_url'; image_url: { url: string } }
|
|
607
|
+
>;
|
|
608
|
+
}
|
|
515
609
|
export declare class MixOpenAIWebSocket extends MixOpenAIResponses {}
|
|
516
610
|
export declare class MixOpenRouter extends MixOpenAI {}
|
|
517
611
|
export declare class MixKimi extends MixOpenAI {}
|
|
@@ -530,6 +624,18 @@ export declare class MixFireworks extends MixCustom {}
|
|
|
530
624
|
export declare class MixNVIDIA extends MixCustom {}
|
|
531
625
|
export declare class MixGoogle extends MixCustom {}
|
|
532
626
|
|
|
627
|
+
export declare class ModerationMix extends ModelMix {
|
|
628
|
+
constructor(setup?: Omit<ModelMixSetup, 'mix'>);
|
|
629
|
+
static new(setup?: Omit<ModelMixSetup, 'mix'>): ModerationMix;
|
|
630
|
+
new(setup?: Omit<ModelMixSetup, 'mix'>): ModerationMix;
|
|
631
|
+
attach(key: string, provider: MixModeration): this;
|
|
632
|
+
openai(args?: ModelAttachArgs): this;
|
|
633
|
+
message(): Promise<never>;
|
|
634
|
+
json(): Promise<never>;
|
|
635
|
+
block(): Promise<never>;
|
|
636
|
+
stream(): Promise<never>;
|
|
637
|
+
}
|
|
638
|
+
|
|
533
639
|
/** Normalize unified effort to integer -1 or 0..100. */
|
|
534
640
|
export function normalizeEffort(value: unknown): EffortValue;
|
|
535
641
|
|