modelmix 4.6.10 → 4.6.14
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/MODELS.md +1 -0
- package/index.d.ts +467 -0
- package/index.js +29 -2
- package/package.json +2 -1
- package/skills/modelmix/SKILL.md +1 -0
- package/test/anthropic.test.js +94 -0
- package/test/live.mcp.js +1 -1
package/MODELS.md
CHANGED
|
@@ -128,6 +128,7 @@ All providers inherit from `MixCustom` base class which provides common function
|
|
|
128
128
|
}
|
|
129
129
|
```
|
|
130
130
|
- **Special Notes**:
|
|
131
|
+
- Removes `temperature`, `top_p`, and `top_k` for Opus 4.7+ / Claude 5 family (API rejects them)
|
|
131
132
|
- Removes `top_p` when thinking mode is enabled
|
|
132
133
|
- Uses `x-api-key` header instead of `authorization`
|
|
133
134
|
- Requires `anthropic-version` header
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for modelmix
|
|
3
|
+
* @see https://github.com/clasen/ModelMix
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type MessageRole = 'user' | 'assistant' | 'system' | 'tool' | string;
|
|
7
|
+
|
|
8
|
+
export type DebugLevel = 0 | 1 | 2 | 3 | 4;
|
|
9
|
+
|
|
10
|
+
/** Unified effort: -1 = adaptive, 0–100 = intensity. */
|
|
11
|
+
export type EffortValue = number;
|
|
12
|
+
|
|
13
|
+
export interface BottleneckConfig {
|
|
14
|
+
maxConcurrent?: number;
|
|
15
|
+
minTime?: number;
|
|
16
|
+
reservoir?: number;
|
|
17
|
+
reservoirRefreshAmount?: number;
|
|
18
|
+
reservoirRefreshInterval?: number;
|
|
19
|
+
[key: string]: unknown;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface RetryConfig {
|
|
23
|
+
enabled?: boolean;
|
|
24
|
+
retries?: number;
|
|
25
|
+
baseDelayMs?: number;
|
|
26
|
+
maxDelayMs?: number;
|
|
27
|
+
retryableStatusCodes?: number[];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface ModelMixOptions {
|
|
31
|
+
max_tokens?: number;
|
|
32
|
+
temperature?: number;
|
|
33
|
+
top_p?: number;
|
|
34
|
+
stream?: boolean;
|
|
35
|
+
model?: string;
|
|
36
|
+
messages?: ChatMessage[];
|
|
37
|
+
response_format?: { type: string; [key: string]: unknown };
|
|
38
|
+
[key: string]: unknown;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface ModelMixConfig {
|
|
42
|
+
system?: string;
|
|
43
|
+
/** 0 = stateless, N = last N messages, -1 = unlimited */
|
|
44
|
+
max_history?: number;
|
|
45
|
+
/** 0=silent, 1=minimal, 2=summary, 3=full, 4=verbose */
|
|
46
|
+
debug?: DebugLevel | number;
|
|
47
|
+
bottleneck?: BottleneckConfig;
|
|
48
|
+
retry?: RetryConfig;
|
|
49
|
+
roundRobin?: boolean;
|
|
50
|
+
/** Unified effort (-1 adaptive, or 0–100). Not a native provider field. */
|
|
51
|
+
effort?: EffortValue | null;
|
|
52
|
+
replace?: Record<string, string>;
|
|
53
|
+
schema?: Record<string, unknown>;
|
|
54
|
+
[key: string]: unknown;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface ModelMixMixFlags {
|
|
58
|
+
openrouter?: boolean;
|
|
59
|
+
cerebras?: boolean;
|
|
60
|
+
groq?: boolean;
|
|
61
|
+
together?: boolean;
|
|
62
|
+
lambda?: boolean;
|
|
63
|
+
fireworks?: boolean;
|
|
64
|
+
nvidia?: boolean;
|
|
65
|
+
moonshot?: boolean;
|
|
66
|
+
minimax?: boolean;
|
|
67
|
+
mimo?: boolean;
|
|
68
|
+
[key: string]: boolean | undefined;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface ModelMixSetup {
|
|
72
|
+
options?: ModelMixOptions;
|
|
73
|
+
config?: ModelMixConfig;
|
|
74
|
+
mix?: ModelMixMixFlags;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface ModelAttachArgs {
|
|
78
|
+
options?: ModelMixOptions;
|
|
79
|
+
config?: ModelMixConfig;
|
|
80
|
+
mix?: ModelMixMixFlags;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface RoleOptions {
|
|
84
|
+
role?: MessageRole;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface TextContentPart {
|
|
88
|
+
type: 'text';
|
|
89
|
+
text: string;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface ImageContentPart {
|
|
93
|
+
type: 'image';
|
|
94
|
+
source: {
|
|
95
|
+
type: 'base64' | 'url' | 'file' | 'buffer' | string;
|
|
96
|
+
media_type?: string;
|
|
97
|
+
data: string | Buffer;
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export type ContentPart = TextContentPart | ImageContentPart | Record<string, unknown>;
|
|
102
|
+
|
|
103
|
+
export interface ChatMessage {
|
|
104
|
+
role: MessageRole;
|
|
105
|
+
content?: string | ContentPart[] | null;
|
|
106
|
+
tool_calls?: ToolCall[];
|
|
107
|
+
tool_call_id?: string;
|
|
108
|
+
name?: string;
|
|
109
|
+
[key: string]: unknown;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export interface ToolCall {
|
|
113
|
+
id?: string;
|
|
114
|
+
type?: string;
|
|
115
|
+
name?: string;
|
|
116
|
+
input?: unknown;
|
|
117
|
+
arguments?: unknown;
|
|
118
|
+
function?: {
|
|
119
|
+
name: string;
|
|
120
|
+
arguments: string | Record<string, unknown>;
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export interface TokenUsage {
|
|
125
|
+
input: number;
|
|
126
|
+
output: number;
|
|
127
|
+
total: number;
|
|
128
|
+
cached: number;
|
|
129
|
+
cost?: number | null;
|
|
130
|
+
speed?: number;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export interface ModelMixResult {
|
|
134
|
+
message?: string;
|
|
135
|
+
think?: string | null;
|
|
136
|
+
signature?: string;
|
|
137
|
+
toolCalls?: ToolCall[];
|
|
138
|
+
tokens?: TokenUsage;
|
|
139
|
+
response?: unknown;
|
|
140
|
+
assistantMessage?: ChatMessage;
|
|
141
|
+
[key: string]: unknown;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export interface StreamChunk {
|
|
145
|
+
response: unknown;
|
|
146
|
+
message: string;
|
|
147
|
+
delta: string;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export type StreamCallback = (chunk: StreamChunk) => void;
|
|
151
|
+
|
|
152
|
+
export interface SchemaFieldDescriptor {
|
|
153
|
+
description?: string;
|
|
154
|
+
required?: boolean;
|
|
155
|
+
enum?: unknown[];
|
|
156
|
+
default?: unknown;
|
|
157
|
+
nullable?: boolean;
|
|
158
|
+
[key: string]: unknown;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export type SchemaDescription =
|
|
162
|
+
| string
|
|
163
|
+
| SchemaFieldDescriptor
|
|
164
|
+
| SchemaDescription[]
|
|
165
|
+
| { [key: string]: SchemaDescription };
|
|
166
|
+
|
|
167
|
+
export interface JsonMethodOptions {
|
|
168
|
+
type?: string;
|
|
169
|
+
addExample?: boolean;
|
|
170
|
+
addSchema?: boolean;
|
|
171
|
+
addNote?: boolean;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export interface BlockOptions {
|
|
175
|
+
addSystemExtra?: boolean;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export interface ToolDefinition {
|
|
179
|
+
name: string;
|
|
180
|
+
description?: string;
|
|
181
|
+
inputSchema?: Record<string, unknown>;
|
|
182
|
+
[key: string]: unknown;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export type ToolCallback = (
|
|
186
|
+
args: Record<string, unknown>
|
|
187
|
+
) => unknown | Promise<unknown>;
|
|
188
|
+
|
|
189
|
+
export interface ToolWithCallback {
|
|
190
|
+
tool: ToolDefinition;
|
|
191
|
+
callback: ToolCallback;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export interface ListedTools {
|
|
195
|
+
local: ToolDefinition[];
|
|
196
|
+
mcp: ToolDefinition[];
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export interface AttachedModel {
|
|
200
|
+
key: string;
|
|
201
|
+
provider: MixCustom;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export interface ProviderConstructorArgs {
|
|
205
|
+
config?: ModelMixConfig & {
|
|
206
|
+
url?: string;
|
|
207
|
+
apiKey?: string;
|
|
208
|
+
[key: string]: unknown;
|
|
209
|
+
};
|
|
210
|
+
options?: ModelMixOptions;
|
|
211
|
+
headers?: Record<string, string>;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export interface CreateArgs {
|
|
215
|
+
config?: ModelMixConfig;
|
|
216
|
+
options?: ModelMixOptions;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export type ProviderFamily =
|
|
220
|
+
| 'openai'
|
|
221
|
+
| 'anthropic'
|
|
222
|
+
| 'google'
|
|
223
|
+
| 'deepseek'
|
|
224
|
+
| 'minimax'
|
|
225
|
+
| string;
|
|
226
|
+
|
|
227
|
+
export declare class ModelMix {
|
|
228
|
+
models: AttachedModel[];
|
|
229
|
+
messages: ChatMessage[];
|
|
230
|
+
tools: Record<string, ToolDefinition[]>;
|
|
231
|
+
toolClient: Record<string, unknown>;
|
|
232
|
+
mcp: Record<string, unknown>;
|
|
233
|
+
options: ModelMixOptions;
|
|
234
|
+
config: ModelMixConfig;
|
|
235
|
+
mix: ModelMixMixFlags;
|
|
236
|
+
lastRaw: ModelMixResult | null;
|
|
237
|
+
streamCallback: StreamCallback | null;
|
|
238
|
+
|
|
239
|
+
constructor(setup?: ModelMixSetup);
|
|
240
|
+
|
|
241
|
+
static new(setup?: ModelMixSetup): ModelMix;
|
|
242
|
+
static formatJSON(obj: unknown): string;
|
|
243
|
+
static formatMessage(message: unknown): unknown;
|
|
244
|
+
static truncate(str: string, maxLen?: number): string;
|
|
245
|
+
static calculateCost(
|
|
246
|
+
modelKey: string,
|
|
247
|
+
tokens: { input: number; output: number }
|
|
248
|
+
): number | null;
|
|
249
|
+
static extractCacheTokens(usage?: Record<string, unknown>): number;
|
|
250
|
+
static formatInputSummary(
|
|
251
|
+
messages: ChatMessage[],
|
|
252
|
+
system: string,
|
|
253
|
+
debug?: number
|
|
254
|
+
): string;
|
|
255
|
+
static formatOutputSummary(result: ModelMixResult, debug: number): string;
|
|
256
|
+
static hasToolInteraction(message: ChatMessage | null | undefined): boolean;
|
|
257
|
+
|
|
258
|
+
new(setup?: ModelMixSetup): ModelMix;
|
|
259
|
+
replace(keyValues: Record<string, string>): this;
|
|
260
|
+
effort(value: EffortValue): this;
|
|
261
|
+
attach(key: string, provider: MixCustom): this;
|
|
262
|
+
|
|
263
|
+
// OpenAI
|
|
264
|
+
gpt41(args?: ModelAttachArgs): this;
|
|
265
|
+
gpt41mini(args?: ModelAttachArgs): this;
|
|
266
|
+
gpt41nano(args?: ModelAttachArgs): this;
|
|
267
|
+
gpt5(args?: ModelAttachArgs): this;
|
|
268
|
+
gpt5mini(args?: ModelAttachArgs): this;
|
|
269
|
+
gpt5nano(args?: ModelAttachArgs): this;
|
|
270
|
+
gpt51(args?: ModelAttachArgs): this;
|
|
271
|
+
gpt52(args?: ModelAttachArgs): this;
|
|
272
|
+
gpt54(args?: ModelAttachArgs): this;
|
|
273
|
+
gpt54mini(args?: ModelAttachArgs): this;
|
|
274
|
+
gpt54nano(args?: ModelAttachArgs): this;
|
|
275
|
+
gpt54pro(args?: ModelAttachArgs): this;
|
|
276
|
+
gpt55(args?: ModelAttachArgs): this;
|
|
277
|
+
gpt55pro(args?: ModelAttachArgs): this;
|
|
278
|
+
gpt56sol(args?: ModelAttachArgs): this;
|
|
279
|
+
gpt56terra(args?: ModelAttachArgs): this;
|
|
280
|
+
gpt56luna(args?: ModelAttachArgs): this;
|
|
281
|
+
gptRealtime(args?: ModelAttachArgs): this;
|
|
282
|
+
gptRealtimeMini(args?: ModelAttachArgs): this;
|
|
283
|
+
gpt53codex(args?: ModelAttachArgs): this;
|
|
284
|
+
gpt53chat(args?: ModelAttachArgs): this;
|
|
285
|
+
gptOss(args?: ModelAttachArgs): this;
|
|
286
|
+
|
|
287
|
+
// Anthropic
|
|
288
|
+
fable5(args?: ModelAttachArgs): this;
|
|
289
|
+
fable5think(args?: ModelAttachArgs): this;
|
|
290
|
+
opus5(args?: ModelAttachArgs): this;
|
|
291
|
+
opus5think(args?: ModelAttachArgs): this;
|
|
292
|
+
opus48think(args?: ModelAttachArgs): this;
|
|
293
|
+
opus47think(args?: ModelAttachArgs): this;
|
|
294
|
+
opus46think(args?: ModelAttachArgs): this;
|
|
295
|
+
opus48(args?: ModelAttachArgs): this;
|
|
296
|
+
opus47(args?: ModelAttachArgs): this;
|
|
297
|
+
opus46(args?: ModelAttachArgs): this;
|
|
298
|
+
opus41(args?: ModelAttachArgs): this;
|
|
299
|
+
opus41think(args?: ModelAttachArgs): this;
|
|
300
|
+
sonnet5(args?: ModelAttachArgs): this;
|
|
301
|
+
sonnet5think(args?: ModelAttachArgs): this;
|
|
302
|
+
sonnet4(args?: ModelAttachArgs): this;
|
|
303
|
+
sonnet4think(args?: ModelAttachArgs): this;
|
|
304
|
+
sonnet46(args?: ModelAttachArgs): this;
|
|
305
|
+
sonnet46think(args?: ModelAttachArgs): this;
|
|
306
|
+
sonnet45(args?: ModelAttachArgs): this;
|
|
307
|
+
sonnet45think(args?: ModelAttachArgs): this;
|
|
308
|
+
haiku35(args?: ModelAttachArgs): this;
|
|
309
|
+
haiku45(args?: ModelAttachArgs): this;
|
|
310
|
+
haiku45think(args?: ModelAttachArgs): this;
|
|
311
|
+
|
|
312
|
+
// Google
|
|
313
|
+
gemini25flash(args?: ModelAttachArgs): this;
|
|
314
|
+
gemini31pro(args?: ModelAttachArgs): this;
|
|
315
|
+
gemini3pro(args?: ModelAttachArgs): this;
|
|
316
|
+
gemini3flash(args?: ModelAttachArgs): this;
|
|
317
|
+
gemini36flash(args?: ModelAttachArgs): this;
|
|
318
|
+
gemini35flash(args?: ModelAttachArgs): this;
|
|
319
|
+
gemini31flashLite(args?: ModelAttachArgs): this;
|
|
320
|
+
gemini25pro(args?: ModelAttachArgs): this;
|
|
321
|
+
|
|
322
|
+
// Perplexity
|
|
323
|
+
sonarPro(args?: ModelAttachArgs): this;
|
|
324
|
+
sonar(args?: ModelAttachArgs): this;
|
|
325
|
+
|
|
326
|
+
// Grok
|
|
327
|
+
grok43(args?: ModelAttachArgs): this;
|
|
328
|
+
grok420multiAgent(args?: ModelAttachArgs): this;
|
|
329
|
+
grok420think(args?: ModelAttachArgs): this;
|
|
330
|
+
grok420(args?: ModelAttachArgs): this;
|
|
331
|
+
grok41think(args?: ModelAttachArgs): this;
|
|
332
|
+
grok41(args?: ModelAttachArgs): this;
|
|
333
|
+
|
|
334
|
+
// Multi-provider
|
|
335
|
+
qwen3(args?: ModelAttachArgs): this;
|
|
336
|
+
qwen36plus(args?: ModelAttachArgs): this;
|
|
337
|
+
hermes3(args?: ModelAttachArgs): this;
|
|
338
|
+
kimiK26think(args?: ModelAttachArgs): this;
|
|
339
|
+
kimiK27Code(args?: ModelAttachArgs): this;
|
|
340
|
+
kimiK3(args?: ModelAttachArgs): this;
|
|
341
|
+
kimiK25think(args?: ModelAttachArgs): this;
|
|
342
|
+
lmstudio(model?: string, args?: ModelAttachArgs): this;
|
|
343
|
+
minimaxM25(args?: ModelAttachArgs): this;
|
|
344
|
+
minimaxM27(args?: ModelAttachArgs): this;
|
|
345
|
+
minimaxM3(args?: ModelAttachArgs): this;
|
|
346
|
+
mimo25(args?: ModelAttachArgs): this;
|
|
347
|
+
mimo25pro(args?: ModelAttachArgs): this;
|
|
348
|
+
deepseekV4Pro(args?: ModelAttachArgs): this;
|
|
349
|
+
deepseekV4Flash(args?: ModelAttachArgs): this;
|
|
350
|
+
GLM51(args?: ModelAttachArgs): this;
|
|
351
|
+
GLM52(args?: ModelAttachArgs): this;
|
|
352
|
+
GLM5(args?: ModelAttachArgs): this;
|
|
353
|
+
|
|
354
|
+
addText(text: string, options?: RoleOptions): this;
|
|
355
|
+
addTextFromFile(filePath: string, options?: RoleOptions): this;
|
|
356
|
+
setSystem(text: string): this;
|
|
357
|
+
setSystemFromFile(filePath: string): this;
|
|
358
|
+
addImageFromBuffer(buffer: Buffer, options?: RoleOptions): this;
|
|
359
|
+
addImage(filePath: string, options?: RoleOptions): this;
|
|
360
|
+
addImageFromUrl(url: string, options?: RoleOptions): Promise<this>;
|
|
361
|
+
processImages(): Promise<void>;
|
|
362
|
+
|
|
363
|
+
message(): Promise<string>;
|
|
364
|
+
json<T = unknown>(
|
|
365
|
+
schemaExample?: T | T[] | null,
|
|
366
|
+
schemaDescription?: SchemaDescription,
|
|
367
|
+
options?: JsonMethodOptions
|
|
368
|
+
): Promise<T>;
|
|
369
|
+
block(options?: BlockOptions): Promise<string>;
|
|
370
|
+
raw(): Promise<ModelMixResult>;
|
|
371
|
+
stream(callback: StreamCallback): Promise<ModelMixResult>;
|
|
372
|
+
|
|
373
|
+
replaceKeyFromFile(key: string, filePath: string): this;
|
|
374
|
+
groupByRoles(messages: ChatMessage[]): ChatMessage[];
|
|
375
|
+
applyTemplate(): void;
|
|
376
|
+
prepareMessages(): Promise<void>;
|
|
377
|
+
readFile(filePath: string, options?: { encoding?: BufferEncoding | null }): string | Buffer;
|
|
378
|
+
execute(args?: CreateArgs): Promise<ModelMixResult>;
|
|
379
|
+
processToolCalls(toolCalls: ToolCall[]): Promise<
|
|
380
|
+
Array<{ name: string; tool_call_id: string; content: string }>
|
|
381
|
+
>;
|
|
382
|
+
|
|
383
|
+
addMCP(...npxArgs: string[]): Promise<void>;
|
|
384
|
+
addTool(toolDefinition: ToolDefinition, callback: ToolCallback): this;
|
|
385
|
+
addTools(toolsWithCallbacks: ToolWithCallback[]): this;
|
|
386
|
+
removeTool(toolName: string): this;
|
|
387
|
+
listTools(): ListedTools;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
export declare class MixCustom {
|
|
391
|
+
config: ModelMixConfig & { url?: string; apiKey?: string };
|
|
392
|
+
options: ModelMixOptions;
|
|
393
|
+
headers: Record<string, string>;
|
|
394
|
+
streamCallback: StreamCallback | null;
|
|
395
|
+
|
|
396
|
+
constructor(args?: ProviderConstructorArgs);
|
|
397
|
+
|
|
398
|
+
getDefaultOptions(customOptions?: ModelMixOptions): ModelMixOptions;
|
|
399
|
+
getDefaultConfig(customConfig?: Record<string, unknown>): Record<string, unknown>;
|
|
400
|
+
getDefaultHeaders(customHeaders?: Record<string, string>): Record<string, string>;
|
|
401
|
+
convertMessages(messages: ChatMessage[], config?: ModelMixConfig): ChatMessage[];
|
|
402
|
+
|
|
403
|
+
static stripContentTypeHeader(headers?: Record<string, string>): Record<string, string>;
|
|
404
|
+
static createMultipartFormData(args?: {
|
|
405
|
+
fields?: Record<string, unknown>;
|
|
406
|
+
files?: unknown[];
|
|
407
|
+
}): { body: Buffer; headers: Record<string, string> };
|
|
408
|
+
static buildRequestBodyAndHeaders(
|
|
409
|
+
options: ModelMixOptions,
|
|
410
|
+
headers: Record<string, string>
|
|
411
|
+
): { body: unknown; headers: Record<string, string>; options: ModelMixOptions };
|
|
412
|
+
static extractMessage(data: unknown): string;
|
|
413
|
+
static extractThink(data: unknown): string | null;
|
|
414
|
+
static extractToolCalls(data: unknown): ToolCall[];
|
|
415
|
+
static extractTokens(data: unknown): TokenUsage;
|
|
416
|
+
|
|
417
|
+
create(args?: CreateArgs): Promise<ModelMixResult>;
|
|
418
|
+
handleError(
|
|
419
|
+
error: unknown,
|
|
420
|
+
context: CreateArgs
|
|
421
|
+
): {
|
|
422
|
+
message: string;
|
|
423
|
+
statusCode: number | null;
|
|
424
|
+
details: unknown;
|
|
425
|
+
stack?: string;
|
|
426
|
+
config?: ModelMixConfig;
|
|
427
|
+
options?: ModelMixOptions;
|
|
428
|
+
};
|
|
429
|
+
processStream(response: { data: NodeJS.ReadableStream }): Promise<ModelMixResult>;
|
|
430
|
+
extractDelta(data: unknown): string;
|
|
431
|
+
processResponse(response: { data: unknown }): ModelMixResult;
|
|
432
|
+
getOptionsTools(tools: Record<string, ToolDefinition[]>): Partial<ModelMixOptions>;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
export declare class MixOpenAI extends MixCustom {}
|
|
436
|
+
export declare class MixOpenAIResponses extends MixOpenAI {}
|
|
437
|
+
export declare class MixOpenAIWebSocket extends MixOpenAIResponses {}
|
|
438
|
+
export declare class MixOpenRouter extends MixOpenAI {}
|
|
439
|
+
export declare class MixKimi extends MixOpenAI {}
|
|
440
|
+
export declare class MixAnthropic extends MixCustom {}
|
|
441
|
+
export declare class MixMiniMax extends MixOpenAI {}
|
|
442
|
+
export declare class MixMiMo extends MixOpenAI {}
|
|
443
|
+
export declare class MixPerplexity extends MixCustom {}
|
|
444
|
+
export declare class MixOllama extends MixCustom {}
|
|
445
|
+
export declare class MixGrok extends MixOpenAI {}
|
|
446
|
+
export declare class MixLambda extends MixCustom {}
|
|
447
|
+
export declare class MixLMStudio extends MixCustom {}
|
|
448
|
+
export declare class MixGroq extends MixCustom {}
|
|
449
|
+
export declare class MixTogether extends MixCustom {}
|
|
450
|
+
export declare class MixCerebras extends MixCustom {}
|
|
451
|
+
export declare class MixFireworks extends MixCustom {}
|
|
452
|
+
export declare class MixNVIDIA extends MixCustom {}
|
|
453
|
+
export declare class MixGoogle extends MixCustom {}
|
|
454
|
+
|
|
455
|
+
/** Normalize unified effort to integer -1 or 0..100. */
|
|
456
|
+
export function normalizeEffort(value: unknown): EffortValue;
|
|
457
|
+
|
|
458
|
+
/** Map unified effort onto provider-native option fields (no-op if native already set). */
|
|
459
|
+
export function applyUnifiedEffort(
|
|
460
|
+
options: ModelMixOptions,
|
|
461
|
+
config: ModelMixConfig,
|
|
462
|
+
providerFamily: ProviderFamily,
|
|
463
|
+
modelKey?: string
|
|
464
|
+
): ModelMixOptions;
|
|
465
|
+
|
|
466
|
+
/** Resolve provider family from a Mix* instance. */
|
|
467
|
+
export function resolveProviderFamily(providerInstance: MixCustom): ProviderFamily;
|
package/index.js
CHANGED
|
@@ -2207,10 +2207,31 @@ class MixAnthropic extends MixCustom {
|
|
|
2207
2207
|
|
|
2208
2208
|
static maxEffortThinkingOptions = {
|
|
2209
2209
|
output_config: { effort: 'max' },
|
|
2210
|
-
thinking: { display: 'summarized' }
|
|
2211
|
-
temperature: 1
|
|
2210
|
+
thinking: { display: 'summarized' }
|
|
2212
2211
|
};
|
|
2213
2212
|
|
|
2213
|
+
/**
|
|
2214
|
+
* Opus 4.7+ and Claude 5 family reject sampling params (temperature/top_p/top_k).
|
|
2215
|
+
* See: https://platform.claude.com/docs/en/about-claude/models/migration-guide
|
|
2216
|
+
*/
|
|
2217
|
+
static rejectsSamplingParams(model = '') {
|
|
2218
|
+
const id = String(model).toLowerCase();
|
|
2219
|
+
if (!id.includes('claude')) return false;
|
|
2220
|
+
if (id.includes('mythos') || id.includes('fable')) return true;
|
|
2221
|
+
|
|
2222
|
+
const opus = id.match(/claude-opus-(\d+)(?:-(\d+))?/);
|
|
2223
|
+
if (opus) {
|
|
2224
|
+
const major = Number(opus[1]);
|
|
2225
|
+
const minor = opus[2] !== undefined ? Number(opus[2]) : 0;
|
|
2226
|
+
return major > 4 || (major === 4 && minor >= 7);
|
|
2227
|
+
}
|
|
2228
|
+
|
|
2229
|
+
const sonnet = id.match(/claude-sonnet-(\d+)/);
|
|
2230
|
+
if (sonnet) return Number(sonnet[1]) >= 5;
|
|
2231
|
+
|
|
2232
|
+
return false;
|
|
2233
|
+
}
|
|
2234
|
+
|
|
2214
2235
|
getDefaultConfig(customConfig) {
|
|
2215
2236
|
|
|
2216
2237
|
if (!process.env.ANTHROPIC_API_KEY) {
|
|
@@ -2228,6 +2249,12 @@ class MixAnthropic extends MixCustom {
|
|
|
2228
2249
|
|
|
2229
2250
|
delete options.response_format;
|
|
2230
2251
|
|
|
2252
|
+
if (MixAnthropic.rejectsSamplingParams(options.model)) {
|
|
2253
|
+
delete options.temperature;
|
|
2254
|
+
delete options.top_p;
|
|
2255
|
+
delete options.top_k;
|
|
2256
|
+
}
|
|
2257
|
+
|
|
2231
2258
|
options.system = config.system;
|
|
2232
2259
|
|
|
2233
2260
|
try {
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "modelmix",
|
|
3
|
-
"version": "4.6.
|
|
3
|
+
"version": "4.6.14",
|
|
4
4
|
"description": "🧬 Reliable interface with automatic fallback for AI LLMs.",
|
|
5
5
|
"main": "index.js",
|
|
6
|
+
"types": "index.d.ts",
|
|
6
7
|
"repository": {
|
|
7
8
|
"type": "git",
|
|
8
9
|
"url": "git+https://github.com/clasen/ModelMix.git"
|
package/skills/modelmix/SKILL.md
CHANGED
|
@@ -469,6 +469,7 @@ const model = ModelMix.new({
|
|
|
469
469
|
- The library uses CommonJS internally but supports ESM import via `{ ModelMix }`.
|
|
470
470
|
- GPT-5+ models automatically use `max_completion_tokens` instead of `max_tokens`.
|
|
471
471
|
- o-series models (o3, o4mini) automatically strip `max_tokens` and `temperature` since those APIs don't support them.
|
|
472
|
+
- Anthropic Opus 4.7+ / Claude 5 family automatically strip `temperature`, `top_p`, and `top_k` (API rejects them).
|
|
472
473
|
- `addText()`, `addImage()`, `addImageFromUrl()`, and `addImageFromBuffer()` all accept `{ role }` as second argument (default `"user"`).
|
|
473
474
|
|
|
474
475
|
## API Quick Reference
|
package/test/anthropic.test.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const { expect } = require('chai');
|
|
2
|
+
const nock = require('nock');
|
|
2
3
|
const { ModelMix, MixAnthropic } = require('../index.js');
|
|
3
4
|
|
|
4
5
|
describe('Anthropic Model Registration Tests', () => {
|
|
@@ -19,6 +20,7 @@ describe('Anthropic Model Registration Tests', () => {
|
|
|
19
20
|
expect(model.models[0].key).to.equal('claude-fable-5');
|
|
20
21
|
expect(model.models[0].provider.options.output_config).to.deep.equal({ effort: 'max' });
|
|
21
22
|
expect(model.models[0].provider.options.thinking).to.deep.equal({ display: 'summarized' });
|
|
23
|
+
expect(model.models[0].provider.options).to.not.have.property('temperature');
|
|
22
24
|
});
|
|
23
25
|
|
|
24
26
|
it('should register Claude Opus 5', () => {
|
|
@@ -38,6 +40,98 @@ describe('Anthropic Model Registration Tests', () => {
|
|
|
38
40
|
expect(model.models[0].key).to.equal('claude-opus-5');
|
|
39
41
|
expect(model.models[0].provider.options.output_config).to.deep.equal({ effort: 'max' });
|
|
40
42
|
expect(model.models[0].provider.options.thinking).to.deep.equal({ display: 'summarized' });
|
|
43
|
+
expect(model.models[0].provider.options).to.not.have.property('temperature');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe('Sampling params (temperature/top_p/top_k)', () => {
|
|
47
|
+
it('should detect models that reject sampling params', () => {
|
|
48
|
+
expect(MixAnthropic.rejectsSamplingParams('claude-opus-5')).to.equal(true);
|
|
49
|
+
expect(MixAnthropic.rejectsSamplingParams('claude-opus-4-8')).to.equal(true);
|
|
50
|
+
expect(MixAnthropic.rejectsSamplingParams('claude-opus-4-7')).to.equal(true);
|
|
51
|
+
expect(MixAnthropic.rejectsSamplingParams('claude-sonnet-5')).to.equal(true);
|
|
52
|
+
expect(MixAnthropic.rejectsSamplingParams('claude-fable-5')).to.equal(true);
|
|
53
|
+
expect(MixAnthropic.rejectsSamplingParams('anthropic/claude-opus-5')).to.equal(true);
|
|
54
|
+
|
|
55
|
+
expect(MixAnthropic.rejectsSamplingParams('claude-opus-4-6')).to.equal(false);
|
|
56
|
+
expect(MixAnthropic.rejectsSamplingParams('claude-opus-4-1-20250805')).to.equal(false);
|
|
57
|
+
expect(MixAnthropic.rejectsSamplingParams('claude-sonnet-4-6')).to.equal(false);
|
|
58
|
+
expect(MixAnthropic.rejectsSamplingParams('claude-haiku-4-5-20251001')).to.equal(false);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('should strip sampling params for Opus 5 requests', async () => {
|
|
62
|
+
const originalApiKey = process.env.ANTHROPIC_API_KEY;
|
|
63
|
+
process.env.ANTHROPIC_API_KEY = 'test-anthropic-key';
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
const provider = new MixAnthropic();
|
|
67
|
+
let requestBody;
|
|
68
|
+
nock('https://api.anthropic.com')
|
|
69
|
+
.post('/v1/messages', body => {
|
|
70
|
+
requestBody = body;
|
|
71
|
+
return true;
|
|
72
|
+
})
|
|
73
|
+
.reply(200, {
|
|
74
|
+
content: [{ type: 'text', text: 'Done' }],
|
|
75
|
+
usage: { input_tokens: 1, output_tokens: 1 }
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
await provider.create({
|
|
79
|
+
config: { system: 'You are an assistant.' },
|
|
80
|
+
options: {
|
|
81
|
+
model: 'claude-opus-5',
|
|
82
|
+
messages: [{ role: 'user', content: 'Hello' }],
|
|
83
|
+
max_tokens: 100,
|
|
84
|
+
temperature: 0.5,
|
|
85
|
+
top_p: 0.9,
|
|
86
|
+
top_k: 40
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
expect(requestBody).to.not.have.property('temperature');
|
|
91
|
+
expect(requestBody).to.not.have.property('top_p');
|
|
92
|
+
expect(requestBody).to.not.have.property('top_k');
|
|
93
|
+
expect(requestBody.model).to.equal('claude-opus-5');
|
|
94
|
+
} finally {
|
|
95
|
+
if (originalApiKey === undefined) delete process.env.ANTHROPIC_API_KEY;
|
|
96
|
+
else process.env.ANTHROPIC_API_KEY = originalApiKey;
|
|
97
|
+
nock.cleanAll();
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('should keep temperature for Opus 4.6 requests', async () => {
|
|
102
|
+
const originalApiKey = process.env.ANTHROPIC_API_KEY;
|
|
103
|
+
process.env.ANTHROPIC_API_KEY = 'test-anthropic-key';
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
const provider = new MixAnthropic();
|
|
107
|
+
let requestBody;
|
|
108
|
+
nock('https://api.anthropic.com')
|
|
109
|
+
.post('/v1/messages', body => {
|
|
110
|
+
requestBody = body;
|
|
111
|
+
return true;
|
|
112
|
+
})
|
|
113
|
+
.reply(200, {
|
|
114
|
+
content: [{ type: 'text', text: 'Done' }],
|
|
115
|
+
usage: { input_tokens: 1, output_tokens: 1 }
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
await provider.create({
|
|
119
|
+
config: { system: 'You are an assistant.' },
|
|
120
|
+
options: {
|
|
121
|
+
model: 'claude-opus-4-6',
|
|
122
|
+
messages: [{ role: 'user', content: 'Hello' }],
|
|
123
|
+
max_tokens: 100,
|
|
124
|
+
temperature: 0.5
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
expect(requestBody.temperature).to.equal(0.5);
|
|
129
|
+
} finally {
|
|
130
|
+
if (originalApiKey === undefined) delete process.env.ANTHROPIC_API_KEY;
|
|
131
|
+
else process.env.ANTHROPIC_API_KEY = originalApiKey;
|
|
132
|
+
nock.cleanAll();
|
|
133
|
+
}
|
|
134
|
+
});
|
|
41
135
|
});
|
|
42
136
|
|
|
43
137
|
it('should register Claude Opus 4.8', () => {
|
package/test/live.mcp.js
CHANGED
|
@@ -140,7 +140,7 @@ describe('Live MCP Integration Tests', function () {
|
|
|
140
140
|
});
|
|
141
141
|
|
|
142
142
|
it('should use custom MCP tools with Claude Opus 5', async function () {
|
|
143
|
-
// Opus 5 rejects temperature
|
|
143
|
+
// Opus 5 rejects temperature; MixAnthropic strips it on request.
|
|
144
144
|
const model = ModelMix.new({ config: setup.config }).opus5();
|
|
145
145
|
|
|
146
146
|
model.addTool({
|