@tyvm/knowhow 0.0.143 → 0.0.145
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/bin/knowhow.js +22 -20
- package/package.json +1 -1
- package/scripts/migrate-usage-json.js +147 -0
- package/src/agents/base/base.ts +65 -10
- package/src/agents/tools/patch.ts +84 -2
- package/src/agents/tools/textSearch.ts +98 -3
- package/src/chat/modules/AgentModule.ts +129 -6
- package/src/chat/modules/SessionsModule.ts +6 -0
- package/src/chat/types.ts +14 -0
- package/src/chat.ts +11 -0
- package/src/cli.ts +2 -0
- package/src/clients/anthropic.ts +56 -1
- package/src/clients/contextLimits.ts +40 -0
- package/src/clients/gemini.ts +59 -5
- package/src/clients/http.ts +85 -4
- package/src/clients/index.ts +4 -0
- package/src/clients/openai.ts +179 -31
- package/src/clients/pricing/fireworks.ts +50 -1
- package/src/clients/pricing/index.ts +1 -1
- package/src/clients/pricing/models.ts +3 -1
- package/src/clients/pricing/openai.ts +2 -2
- package/src/clients/pricing/qwen.ts +80 -0
- package/src/clients/qwen.ts +16 -0
- package/src/clients/types.ts +69 -1
- package/src/clients/xai.ts +73 -4
- package/src/commands/agent.ts +20 -1
- package/src/commands/sandbox.ts +139 -114
- package/src/index.ts +9 -0
- package/src/processors/Base64ImageDetector.ts +46 -7
- package/src/processors/GemmaToolProcessor.ts +163 -0
- package/src/processors/index.ts +1 -0
- package/src/services/AgentSyncFs.ts +37 -0
- package/src/services/SessionManager.ts +14 -0
- package/src/services/Tools.ts +3 -0
- package/src/services/modules/computerUse.ts +216 -0
- package/src/services/modules/index.ts +47 -9
- package/src/services/modules/types.ts +23 -1
- package/src/types.ts +3 -0
- package/tests/clients/contextLimits.test.ts +4 -0
- package/tests/clients/openai.images.test.ts +105 -0
- package/tests/clients/pricing.test.ts +6 -0
- package/tests/compressor/usageBase64.json +159 -0
- package/tests/compressor/usageBase64.test.ts +175 -0
- package/tests/patching/fixtures/transcript-mangles/agent-sync-fs-nested-catch.json +9 -0
- package/tests/patching/fixtures/transcript-mangles/automation-functions-crossed.json +9 -0
- package/tests/patching/fixtures/transcript-mangles/fruit-ninja-confirmed-retirement.json +8 -0
- package/tests/patching/patchFileOutput.test.ts +112 -1
- package/tests/patching/transcriptMangleRegression.test.ts +125 -0
- package/tests/processors/Base64ImageDetector.test.ts +36 -1
- package/tests/unit/commands/sandbox.test.ts +350 -0
- package/ts_build/package.json +1 -1
- package/ts_build/src/agents/base/base.d.ts +8 -1
- package/ts_build/src/agents/base/base.js +37 -3
- package/ts_build/src/agents/base/base.js.map +1 -1
- package/ts_build/src/agents/tools/patch.d.ts +1 -0
- package/ts_build/src/agents/tools/patch.js +35 -2
- package/ts_build/src/agents/tools/patch.js.map +1 -1
- package/ts_build/src/agents/tools/textSearch.js +56 -2
- package/ts_build/src/agents/tools/textSearch.js.map +1 -1
- package/ts_build/src/chat/modules/AgentModule.d.ts +14 -1
- package/ts_build/src/chat/modules/AgentModule.js +73 -5
- package/ts_build/src/chat/modules/AgentModule.js.map +1 -1
- package/ts_build/src/chat/modules/SessionsModule.js +4 -0
- package/ts_build/src/chat/modules/SessionsModule.js.map +1 -1
- package/ts_build/src/chat/types.d.ts +11 -0
- package/ts_build/src/chat.js +7 -0
- package/ts_build/src/chat.js.map +1 -1
- package/ts_build/src/cli.d.ts +2 -1
- package/ts_build/src/cli.js +1 -0
- package/ts_build/src/cli.js.map +1 -1
- package/ts_build/src/clients/anthropic.js +28 -1
- package/ts_build/src/clients/anthropic.js.map +1 -1
- package/ts_build/src/clients/contextLimits.js +25 -0
- package/ts_build/src/clients/contextLimits.js.map +1 -1
- package/ts_build/src/clients/gemini.js +48 -4
- package/ts_build/src/clients/gemini.js.map +1 -1
- package/ts_build/src/clients/http.d.ts +2 -0
- package/ts_build/src/clients/http.js +66 -9
- package/ts_build/src/clients/http.js.map +1 -1
- package/ts_build/src/clients/index.d.ts +1 -0
- package/ts_build/src/clients/index.js +4 -0
- package/ts_build/src/clients/index.js.map +1 -1
- package/ts_build/src/clients/openai.d.ts +6 -1
- package/ts_build/src/clients/openai.js +131 -33
- package/ts_build/src/clients/openai.js.map +1 -1
- package/ts_build/src/clients/pricing/fireworks.d.ts +4 -1
- package/ts_build/src/clients/pricing/fireworks.js +33 -1
- package/ts_build/src/clients/pricing/fireworks.js.map +1 -1
- package/ts_build/src/clients/pricing/index.d.ts +1 -1
- package/ts_build/src/clients/pricing/index.js +6 -2
- package/ts_build/src/clients/pricing/index.js.map +1 -1
- package/ts_build/src/clients/pricing/models.d.ts +2 -1
- package/ts_build/src/clients/pricing/models.js +4 -1
- package/ts_build/src/clients/pricing/models.js.map +1 -1
- package/ts_build/src/clients/pricing/openai.js +2 -2
- package/ts_build/src/clients/pricing/openai.js.map +1 -1
- package/ts_build/src/clients/pricing/qwen.d.ts +5 -0
- package/ts_build/src/clients/pricing/qwen.js +30 -0
- package/ts_build/src/clients/pricing/qwen.js.map +1 -0
- package/ts_build/src/clients/qwen.d.ts +4 -0
- package/ts_build/src/clients/qwen.js +15 -0
- package/ts_build/src/clients/qwen.js.map +1 -0
- package/ts_build/src/clients/types.d.ts +12 -0
- package/ts_build/src/clients/xai.d.ts +1 -0
- package/ts_build/src/clients/xai.js +56 -9
- package/ts_build/src/clients/xai.js.map +1 -1
- package/ts_build/src/commands/agent.js +10 -1
- package/ts_build/src/commands/agent.js.map +1 -1
- package/ts_build/src/commands/sandbox.js +79 -80
- package/ts_build/src/commands/sandbox.js.map +1 -1
- package/ts_build/src/index.d.ts +2 -0
- package/ts_build/src/index.js +1 -0
- package/ts_build/src/index.js.map +1 -1
- package/ts_build/src/processors/Base64ImageDetector.js +22 -3
- package/ts_build/src/processors/Base64ImageDetector.js.map +1 -1
- package/ts_build/src/processors/GemmaToolProcessor.d.ts +14 -0
- package/ts_build/src/processors/GemmaToolProcessor.js +97 -0
- package/ts_build/src/processors/GemmaToolProcessor.js.map +1 -0
- package/ts_build/src/processors/index.d.ts +1 -0
- package/ts_build/src/processors/index.js +3 -1
- package/ts_build/src/processors/index.js.map +1 -1
- package/ts_build/src/services/AgentSyncFs.js +17 -0
- package/ts_build/src/services/AgentSyncFs.js.map +1 -1
- package/ts_build/src/services/SessionManager.js +12 -0
- package/ts_build/src/services/SessionManager.js.map +1 -1
- package/ts_build/src/services/Tools.d.ts +2 -0
- package/ts_build/src/services/Tools.js.map +1 -1
- package/ts_build/src/services/modules/computerUse.d.ts +163 -0
- package/ts_build/src/services/modules/computerUse.js +3 -0
- package/ts_build/src/services/modules/computerUse.js.map +1 -0
- package/ts_build/src/services/modules/index.d.ts +1 -0
- package/ts_build/src/services/modules/index.js +23 -6
- package/ts_build/src/services/modules/index.js.map +1 -1
- package/ts_build/src/services/modules/types.d.ts +4 -0
- package/ts_build/src/types.d.ts +3 -0
- package/ts_build/src/types.js.map +1 -1
- package/ts_build/src/workers/tools/index.d.ts +1 -1
- package/ts_build/tests/clients/contextLimits.test.js +2 -0
- package/ts_build/tests/clients/contextLimits.test.js.map +1 -1
- package/ts_build/tests/clients/openai.images.test.d.ts +1 -0
- package/ts_build/tests/clients/openai.images.test.js +94 -0
- package/ts_build/tests/clients/openai.images.test.js.map +1 -0
- package/ts_build/tests/clients/pricing.test.js +4 -0
- package/ts_build/tests/clients/pricing.test.js.map +1 -1
- package/ts_build/tests/compressor/usageBase64.test.d.ts +1 -0
- package/ts_build/tests/compressor/usageBase64.test.js +109 -0
- package/ts_build/tests/compressor/usageBase64.test.js.map +1 -0
- package/ts_build/tests/patching/patchFileOutput.test.js +82 -0
- package/ts_build/tests/patching/patchFileOutput.test.js.map +1 -1
- package/ts_build/tests/patching/transcriptMangleRegression.test.d.ts +1 -0
- package/ts_build/tests/patching/transcriptMangleRegression.test.js +104 -0
- package/ts_build/tests/patching/transcriptMangleRegression.test.js.map +1 -0
- package/ts_build/tests/processors/Base64ImageDetector.test.js +24 -0
- package/ts_build/tests/processors/Base64ImageDetector.test.js.map +1 -1
- package/ts_build/tests/unit/commands/sandbox.test.d.ts +1 -0
- package/ts_build/tests/unit/commands/sandbox.test.js +258 -0
- package/ts_build/tests/unit/commands/sandbox.test.js.map +1 -0
- package/x-home.png +0 -0
package/src/clients/http.ts
CHANGED
|
@@ -92,6 +92,33 @@ export class HttpClient implements GenericClient {
|
|
|
92
92
|
this.pricingMap = pricingMap;
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
/**
|
|
96
|
+
* A stable provider id used to tag/read the provider-agnostic
|
|
97
|
+
* `_reasoning_details` slot. Reasoning items are opaque and only meaningful
|
|
98
|
+
* to the provider that produced them, so we key on the client's baseUrl.
|
|
99
|
+
*/
|
|
100
|
+
protected reasoningProviderId(): string {
|
|
101
|
+
return `http:${this.baseUrl}`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Resolve the provider-native reasoning items (Responses API `reasoning`
|
|
106
|
+
* items with encrypted_content) to re-inject for an assistant message,
|
|
107
|
+
* reading the provider-agnostic `_reasoning_details` slot only when it was
|
|
108
|
+
* produced by this same client.
|
|
109
|
+
*/
|
|
110
|
+
protected getReasoningItems(msg: any): any[] {
|
|
111
|
+
const details = msg?._reasoning_details;
|
|
112
|
+
if (
|
|
113
|
+
details &&
|
|
114
|
+
details.provider === this.reasoningProviderId() &&
|
|
115
|
+
Array.isArray(details.items)
|
|
116
|
+
) {
|
|
117
|
+
return details.items;
|
|
118
|
+
}
|
|
119
|
+
return [];
|
|
120
|
+
}
|
|
121
|
+
|
|
95
122
|
/**
|
|
96
123
|
* Returns the pricing entry for a specific model, or all pricing entries if no model is given.
|
|
97
124
|
* Returns undefined for a specific model if no pricing is known.
|
|
@@ -299,22 +326,44 @@ export class HttpClient implements GenericClient {
|
|
|
299
326
|
.trim() || undefined;
|
|
300
327
|
|
|
301
328
|
// Convert messages to Responses API input format
|
|
329
|
+
// Responses API hard limit: 10,485,760 chars per function_call_output.output
|
|
330
|
+
const MAX_TOOL_OUTPUT = 10_485_000;
|
|
331
|
+
const truncateToolOutput = (s: string) =>
|
|
332
|
+
s.length > MAX_TOOL_OUTPUT
|
|
333
|
+
? s.slice(0, MAX_TOOL_OUTPUT) + "\n\n[TRUNCATED: output exceeded 10MB API limit]"
|
|
334
|
+
: s;
|
|
302
335
|
const input: any[] = nonSystemMessages.map((msg) => {
|
|
303
336
|
if (msg.role === "tool") {
|
|
304
337
|
return {
|
|
305
338
|
type: "function_call_output",
|
|
306
339
|
call_id: msg.tool_call_id,
|
|
307
|
-
output: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content),
|
|
340
|
+
output: truncateToolOutput(typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)),
|
|
308
341
|
};
|
|
309
342
|
}
|
|
310
343
|
if (msg.role === "assistant" && msg.tool_calls?.length) {
|
|
311
|
-
|
|
344
|
+
// Re-inject any captured reasoning items (encrypted_content) BEFORE
|
|
345
|
+
// the function_call items so the model preserves its chain-of-thought
|
|
346
|
+
// across tool-use turns instead of re-deriving it each turn.
|
|
347
|
+
const reasoningItems = this.getReasoningItems(msg);
|
|
348
|
+
return [
|
|
349
|
+
...reasoningItems,
|
|
350
|
+
...(msg.tool_calls as any[]).map((tc: any) => ({
|
|
312
351
|
type: "function_call",
|
|
313
352
|
id: tc.id.startsWith("fc") ? tc.id : `fc_${tc.id}`,
|
|
314
353
|
call_id: tc.id,
|
|
315
354
|
name: tc.function.name,
|
|
316
355
|
arguments: tc.function.arguments,
|
|
317
|
-
|
|
356
|
+
})),
|
|
357
|
+
];
|
|
358
|
+
}
|
|
359
|
+
if (msg.role === "assistant" && this.getReasoningItems(msg).length) {
|
|
360
|
+
return [
|
|
361
|
+
...this.getReasoningItems(msg),
|
|
362
|
+
{
|
|
363
|
+
role: msg.role,
|
|
364
|
+
content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content),
|
|
365
|
+
},
|
|
366
|
+
];
|
|
318
367
|
}
|
|
319
368
|
return {
|
|
320
369
|
role: msg.role,
|
|
@@ -336,7 +385,18 @@ export class HttpClient implements GenericClient {
|
|
|
336
385
|
...(instructions && { instructions }),
|
|
337
386
|
...(options.max_tokens && { max_output_tokens: options.max_tokens }),
|
|
338
387
|
...(tools?.length && { tools, tool_choice: "auto" }),
|
|
339
|
-
store,
|
|
388
|
+
// Reasoning persistence: store server-side when requested, otherwise
|
|
389
|
+
// ask the provider to return encrypted reasoning content so we can
|
|
390
|
+
// thread the reasoning items back into the next request ourselves.
|
|
391
|
+
store: options.store === true ? true : store,
|
|
392
|
+
...(options.previous_response_id && {
|
|
393
|
+
previous_response_id: options.previous_response_id,
|
|
394
|
+
}),
|
|
395
|
+
...(options.store !== true &&
|
|
396
|
+
options.reasoning_effort &&
|
|
397
|
+
options.reasoning_effort !== "none" && {
|
|
398
|
+
include: ["reasoning.encrypted_content"],
|
|
399
|
+
}),
|
|
340
400
|
...this.extra_body,
|
|
341
401
|
};
|
|
342
402
|
|
|
@@ -368,6 +428,10 @@ export class HttpClient implements GenericClient {
|
|
|
368
428
|
// Collect text content and tool calls from output items
|
|
369
429
|
let textContent: string | null = null;
|
|
370
430
|
const toolCalls: any[] = [];
|
|
431
|
+
// Reasoning items (encrypted_content) to thread back into the next turn,
|
|
432
|
+
// plus a human-readable summary of the model's thinking (if present).
|
|
433
|
+
const reasoningItems: any[] = [];
|
|
434
|
+
let reasoningSummary: string | null = null;
|
|
371
435
|
|
|
372
436
|
for (const item of data.output ?? []) {
|
|
373
437
|
if (item.type === "message") {
|
|
@@ -376,6 +440,15 @@ export class HttpClient implements GenericClient {
|
|
|
376
440
|
textContent = (textContent ?? "") + part.text;
|
|
377
441
|
}
|
|
378
442
|
}
|
|
443
|
+
} else if (item.type === "reasoning") {
|
|
444
|
+
reasoningItems.push(item);
|
|
445
|
+
const summaryParts = item.summary ?? [];
|
|
446
|
+
for (const part of summaryParts) {
|
|
447
|
+
const text = typeof part === "string" ? part : part?.text;
|
|
448
|
+
if (text) {
|
|
449
|
+
reasoningSummary = (reasoningSummary ?? "") + text;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
379
452
|
} else if (item.type === "function_call") {
|
|
380
453
|
toolCalls.push({
|
|
381
454
|
id: item.call_id,
|
|
@@ -392,12 +465,20 @@ export class HttpClient implements GenericClient {
|
|
|
392
465
|
role: "assistant",
|
|
393
466
|
content: textContent,
|
|
394
467
|
...(toolCalls.length > 0 && { tool_calls: toolCalls }),
|
|
468
|
+
...(reasoningSummary && { reasoning_summary: reasoningSummary }),
|
|
469
|
+
...(reasoningItems.length > 0 && {
|
|
470
|
+
_reasoning_details: {
|
|
471
|
+
provider: this.reasoningProviderId(),
|
|
472
|
+
items: reasoningItems,
|
|
473
|
+
},
|
|
474
|
+
}),
|
|
395
475
|
},
|
|
396
476
|
},
|
|
397
477
|
],
|
|
398
478
|
model: data.model ?? options.model,
|
|
399
479
|
usage,
|
|
400
480
|
usd_cost: data.usd_cost ?? this.calculateCost(options.model, usage),
|
|
481
|
+
response_id: data.id,
|
|
401
482
|
};
|
|
402
483
|
});
|
|
403
484
|
}
|
package/src/clients/index.ts
CHANGED
|
@@ -58,6 +58,7 @@ import { GitHubCopilotClient } from "./copilot";
|
|
|
58
58
|
import { GenericLlamaClient } from "./llama";
|
|
59
59
|
import { GenericFireworksClient } from "./fireworks";
|
|
60
60
|
import { GenericMetaClient } from "./meta";
|
|
61
|
+
import { GenericQwenClient } from "./qwen";
|
|
61
62
|
export {
|
|
62
63
|
OpenAiTextPricing,
|
|
63
64
|
AnthropicTextPricing,
|
|
@@ -103,6 +104,7 @@ const BUILT_IN_PROVIDER_REGISTRY: Record<string, ProviderRegistryEntry> = {
|
|
|
103
104
|
llama: { clientClass: GenericLlamaClient },
|
|
104
105
|
fireworks: { clientClass: GenericFireworksClient },
|
|
105
106
|
meta: { clientClass: GenericMetaClient },
|
|
107
|
+
qwen: { clientClass: GenericQwenClient },
|
|
106
108
|
knowhow: {
|
|
107
109
|
createClient: (entry: ModelProvider) => {
|
|
108
110
|
const jwt = loadKnowhowJwt();
|
|
@@ -134,6 +136,7 @@ const DEFAULT_PROVIDERS: ModelProvider[] = [
|
|
|
134
136
|
{ provider: "llama", envKey: "LLAMA_API_KEY" },
|
|
135
137
|
{ provider: "fireworks", envKey: "FIREWORKS_API_KEY" },
|
|
136
138
|
{ provider: "meta", envKey: "META_API_KEY" },
|
|
139
|
+
{ provider: "qwen", envKey: "QWEN_CLOUD_API_KEY" },
|
|
137
140
|
];
|
|
138
141
|
|
|
139
142
|
export class AIClient {
|
|
@@ -1075,3 +1078,4 @@ export * from "./llama";
|
|
|
1075
1078
|
export * from "./copilot";
|
|
1076
1079
|
export * from "./fireworks";
|
|
1077
1080
|
export * from "./meta";
|
|
1081
|
+
export * from "./qwen";
|
package/src/clients/openai.ts
CHANGED
|
@@ -22,13 +22,17 @@ import {
|
|
|
22
22
|
FileUploadResponse,
|
|
23
23
|
FileDownloadOptions,
|
|
24
24
|
FileDownloadResponse,
|
|
25
|
+
Message,
|
|
25
26
|
} from "./types";
|
|
26
27
|
import {
|
|
27
28
|
ChatCompletionMessageParam,
|
|
28
29
|
ChatCompletionToolMessageParam,
|
|
29
30
|
ChatCompletionMessageToolCall,
|
|
30
31
|
} from "openai/resources/chat";
|
|
31
|
-
import {
|
|
32
|
+
import {
|
|
33
|
+
ResponseFunctionToolCall,
|
|
34
|
+
ResponseInputMessageContentList,
|
|
35
|
+
} from "openai/resources/responses/responses";
|
|
32
36
|
|
|
33
37
|
import {
|
|
34
38
|
EmbeddingModels,
|
|
@@ -140,6 +144,75 @@ export class GenericOpenAiClient implements GenericClient {
|
|
|
140
144
|
return options.reasoning_effort === "none";
|
|
141
145
|
}
|
|
142
146
|
|
|
147
|
+
/**
|
|
148
|
+
* OpenAI only permits image parts on user messages. A Chat Completions
|
|
149
|
+
* `tool` message may contain text parts, but not `image_url` parts. Keep the
|
|
150
|
+
* required tool result and attach images in a following user message.
|
|
151
|
+
*/
|
|
152
|
+
toChatCompletionMessages(
|
|
153
|
+
messages: CompletionOptions["messages"]
|
|
154
|
+
): ChatCompletionMessageParam[] {
|
|
155
|
+
return messages.flatMap((msg): ChatCompletionMessageParam[] => {
|
|
156
|
+
if (msg.role !== "tool") return [msg as ChatCompletionMessageParam];
|
|
157
|
+
|
|
158
|
+
const parts = Array.isArray(msg.content) ? msg.content : [];
|
|
159
|
+
const unsupportedPart = parts.find(
|
|
160
|
+
(part) => part.type !== "text" && part.type !== "image_url"
|
|
161
|
+
);
|
|
162
|
+
if (unsupportedPart) {
|
|
163
|
+
throw new Error(
|
|
164
|
+
`OpenAI Chat Completions cannot attach ${unsupportedPart.type} content to a tool result.`
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
const imageParts = parts.filter((part) => part.type === "image_url");
|
|
168
|
+
const textParts = parts.filter((part) => part.type === "text");
|
|
169
|
+
const toolMessage: ChatCompletionToolMessageParam = {
|
|
170
|
+
role: "tool",
|
|
171
|
+
tool_call_id: msg.tool_call_id!,
|
|
172
|
+
content: Array.isArray(msg.content)
|
|
173
|
+
? textParts.length
|
|
174
|
+
? textParts
|
|
175
|
+
: [{
|
|
176
|
+
type: "text",
|
|
177
|
+
text: "Image output attached in the following user message.",
|
|
178
|
+
}]
|
|
179
|
+
: msg.content || "",
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
if (!imageParts.length) return [toolMessage];
|
|
183
|
+
return [
|
|
184
|
+
toolMessage,
|
|
185
|
+
{ role: "user", content: imageParts } as ChatCompletionMessageParam,
|
|
186
|
+
];
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** Convert the CLI's Chat Completions-style parts to Responses API parts. */
|
|
191
|
+
toResponseContent(
|
|
192
|
+
content: Message["content"]
|
|
193
|
+
): string | ResponseInputMessageContentList {
|
|
194
|
+
if (!Array.isArray(content)) return content || "";
|
|
195
|
+
const result: ResponseInputMessageContentList = [];
|
|
196
|
+
for (const part of content) {
|
|
197
|
+
if (part.type === "text") {
|
|
198
|
+
result.push({ type: "input_text", text: part.text });
|
|
199
|
+
}
|
|
200
|
+
else if (part.type === "image_url") {
|
|
201
|
+
result.push({
|
|
202
|
+
type: "input_image",
|
|
203
|
+
image_url: part.image_url.url,
|
|
204
|
+
detail: part.image_url.detail || "auto",
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
throw new Error(
|
|
209
|
+
`OpenAI Responses API conversion does not support ${part.type} content.`
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return result;
|
|
214
|
+
}
|
|
215
|
+
|
|
143
216
|
async createChatCompletion(
|
|
144
217
|
options: CompletionOptions
|
|
145
218
|
): Promise<CompletionResponse> {
|
|
@@ -148,17 +221,7 @@ export class GenericOpenAiClient implements GenericClient {
|
|
|
148
221
|
return this.createChatResponse(options);
|
|
149
222
|
}
|
|
150
223
|
|
|
151
|
-
const openaiMessages = options.messages
|
|
152
|
-
if (msg.role === "tool") {
|
|
153
|
-
return {
|
|
154
|
-
...msg,
|
|
155
|
-
content: msg.content || "",
|
|
156
|
-
role: "tool",
|
|
157
|
-
tool_call_id: msg.tool_call_id,
|
|
158
|
-
} as ChatCompletionToolMessageParam;
|
|
159
|
-
}
|
|
160
|
-
return msg as ChatCompletionMessageParam;
|
|
161
|
-
});
|
|
224
|
+
const openaiMessages = this.toChatCompletionMessages(options.messages);
|
|
162
225
|
|
|
163
226
|
const response = await this.client.chat.completions.create({
|
|
164
227
|
model: options.model,
|
|
@@ -222,17 +285,7 @@ export class GenericOpenAiClient implements GenericClient {
|
|
|
222
285
|
return;
|
|
223
286
|
}
|
|
224
287
|
|
|
225
|
-
const openaiMessages = options.messages
|
|
226
|
-
if (msg.role === "tool") {
|
|
227
|
-
return {
|
|
228
|
-
...msg,
|
|
229
|
-
content: msg.content || "",
|
|
230
|
-
role: "tool",
|
|
231
|
-
tool_call_id: msg.tool_call_id,
|
|
232
|
-
} as ChatCompletionToolMessageParam;
|
|
233
|
-
}
|
|
234
|
-
return msg as ChatCompletionMessageParam;
|
|
235
|
-
});
|
|
288
|
+
const openaiMessages = this.toChatCompletionMessages(options.messages);
|
|
236
289
|
|
|
237
290
|
const stream = await this.client.chat.completions.create({
|
|
238
291
|
model: options.model,
|
|
@@ -307,6 +360,20 @@ export class GenericOpenAiClient implements GenericClient {
|
|
|
307
360
|
}
|
|
308
361
|
}
|
|
309
362
|
|
|
363
|
+
/**
|
|
364
|
+
* Resolve the OpenAI-native reasoning items to re-inject for an assistant
|
|
365
|
+
* message. Reads the provider-agnostic `_reasoning_details` slot (only when
|
|
366
|
+
* it was produced by this provider) and falls back to the deprecated
|
|
367
|
+
* `_reasoning_items` for backward compatibility.
|
|
368
|
+
*/
|
|
369
|
+
private getReasoningItems(msg: any): any[] {
|
|
370
|
+
const details = (msg as any)._reasoning_details;
|
|
371
|
+
if (details && details.provider === "openai" && Array.isArray(details.items)) {
|
|
372
|
+
return details.items;
|
|
373
|
+
}
|
|
374
|
+
return (msg as any)._reasoning_items ?? [];
|
|
375
|
+
}
|
|
376
|
+
|
|
310
377
|
async createChatResponse(
|
|
311
378
|
options: CompletionOptions
|
|
312
379
|
): Promise<CompletionResponse> {
|
|
@@ -324,30 +391,68 @@ export class GenericOpenAiClient implements GenericClient {
|
|
|
324
391
|
|
|
325
392
|
// Convert chat messages to Responses API input items
|
|
326
393
|
// The Responses API accepts: user/assistant/system messages and function_call_output items
|
|
394
|
+
// OpenAI Responses API hard limit: 10,485,760 chars per function_call_output.output
|
|
395
|
+
const MAX_TOOL_OUTPUT = 10_485_000;
|
|
396
|
+
const truncateToolOutput = (s: string) =>
|
|
397
|
+
s.length > MAX_TOOL_OUTPUT
|
|
398
|
+
? s.slice(0, MAX_TOOL_OUTPUT) + "\n\n[TRUNCATED: output exceeded 10MB API limit]"
|
|
399
|
+
: s;
|
|
327
400
|
const input: any[] = nonSystemMessages.map((msg) => {
|
|
328
401
|
if (msg.role === "tool") {
|
|
329
|
-
//
|
|
330
|
-
|
|
402
|
+
// OpenAI function outputs cannot directly contain image content. Send
|
|
403
|
+
// the required function output, then attach images as a user message.
|
|
404
|
+
const parts = Array.isArray(msg.content) ? msg.content : [];
|
|
405
|
+
const imageParts = parts.filter((part) => part.type === "image_url");
|
|
406
|
+
const text = parts
|
|
407
|
+
.filter((part) => part.type === "text")
|
|
408
|
+
.map((part: any) => part.text)
|
|
409
|
+
.join("\n");
|
|
410
|
+
const toolOutput = {
|
|
331
411
|
type: "function_call_output",
|
|
332
412
|
call_id: msg.tool_call_id,
|
|
333
|
-
output: typeof msg.content === "string"
|
|
413
|
+
output: truncateToolOutput(typeof msg.content === "string"
|
|
414
|
+
? msg.content
|
|
415
|
+
: text || "Image output attached in the following user message."),
|
|
334
416
|
};
|
|
417
|
+
if (!imageParts.length) return toolOutput;
|
|
418
|
+
return [
|
|
419
|
+
toolOutput,
|
|
420
|
+
{ role: "user", content: this.toResponseContent(imageParts) },
|
|
421
|
+
];
|
|
335
422
|
}
|
|
336
423
|
if (msg.role === "assistant" && msg.tool_calls?.length) {
|
|
337
|
-
// assistant message with tool calls → function_call items
|
|
338
|
-
|
|
424
|
+
// assistant message with tool calls → function_call items.
|
|
425
|
+
// If the prior turn captured reasoning items (encrypted_content path),
|
|
426
|
+
// re-inject them *before* the function_call items so the model keeps its
|
|
427
|
+
// chain-of-thought instead of re-deriving it from scratch.
|
|
428
|
+
const reasoningItems = this.getReasoningItems(msg);
|
|
429
|
+
return [
|
|
430
|
+
...reasoningItems,
|
|
431
|
+
...msg.tool_calls.map((tc) => ({
|
|
339
432
|
type: "function_call",
|
|
340
433
|
// id must start with 'fc_'; call_id is the original call_ ID used for function_call_output matching
|
|
341
434
|
id: tc.id.startsWith("fc") ? tc.id : `fc_${tc.id}`,
|
|
342
435
|
call_id: tc.id,
|
|
343
436
|
name: tc.function.name,
|
|
344
437
|
arguments: tc.function.arguments,
|
|
345
|
-
|
|
438
|
+
})),
|
|
439
|
+
];
|
|
440
|
+
}
|
|
441
|
+
if (msg.role === "assistant" && this.getReasoningItems(msg).length) {
|
|
442
|
+
// assistant message (no tool calls) that carried reasoning items →
|
|
443
|
+
// re-inject reasoning items, then the visible message content.
|
|
444
|
+
return [
|
|
445
|
+
...this.getReasoningItems(msg),
|
|
446
|
+
{
|
|
447
|
+
role: msg.role,
|
|
448
|
+
content: this.toResponseContent(msg.content),
|
|
449
|
+
},
|
|
450
|
+
];
|
|
346
451
|
}
|
|
347
452
|
// Regular user/assistant message
|
|
348
453
|
return {
|
|
349
454
|
role: msg.role,
|
|
350
|
-
content:
|
|
455
|
+
content: this.toResponseContent(msg.content),
|
|
351
456
|
};
|
|
352
457
|
}).flat();
|
|
353
458
|
|
|
@@ -360,10 +465,29 @@ export class GenericOpenAiClient implements GenericClient {
|
|
|
360
465
|
strict: false,
|
|
361
466
|
}));
|
|
362
467
|
|
|
468
|
+
// Reasoning persistence:
|
|
469
|
+
// - store === true → OpenAI persists the response server-side; the next
|
|
470
|
+
// turn passes previous_response_id to continue the reasoning chain.
|
|
471
|
+
// - store !== true → stateless; ask OpenAI to return encrypted reasoning
|
|
472
|
+
// content so we can thread it back into the next request's input ourselves.
|
|
473
|
+
const store = options.store === true;
|
|
474
|
+
const wantsReasoning =
|
|
475
|
+
OpenAiReasoningModels.includes(options.model) &&
|
|
476
|
+
!this.isReasoningDisabled(options);
|
|
477
|
+
|
|
363
478
|
const response = await this.client.responses.create({
|
|
364
479
|
model: options.model as any,
|
|
365
480
|
input,
|
|
366
481
|
...(instructions && { instructions }),
|
|
482
|
+
...(store && { store: true }),
|
|
483
|
+
...(!store && { store: false }),
|
|
484
|
+
...(options.previous_response_id && {
|
|
485
|
+
previous_response_id: options.previous_response_id,
|
|
486
|
+
}),
|
|
487
|
+
// When stateless, request encrypted reasoning so we can re-inject it next turn.
|
|
488
|
+
...(!store && wantsReasoning && {
|
|
489
|
+
include: ["reasoning.encrypted_content"],
|
|
490
|
+
}),
|
|
367
491
|
// Don't limit max_output_tokens for Responses API - codex truncates tool call arguments when limited
|
|
368
492
|
...(OpenAiReasoningModels.includes(options.model) && !this.isReasoningDisabled(options) && {
|
|
369
493
|
max_output_tokens: Math.max(options.max_tokens || 0, 16000),
|
|
@@ -376,7 +500,6 @@ export class GenericOpenAiClient implements GenericClient {
|
|
|
376
500
|
tools,
|
|
377
501
|
tool_choice: "auto",
|
|
378
502
|
}),
|
|
379
|
-
store: false,
|
|
380
503
|
} as any);
|
|
381
504
|
|
|
382
505
|
// Map Responses API usage to Chat Completions usage format
|
|
@@ -405,6 +528,10 @@ export class GenericOpenAiClient implements GenericClient {
|
|
|
405
528
|
// Collect text content and tool calls from the output items
|
|
406
529
|
let textContent: string | null = null;
|
|
407
530
|
const toolCalls: ChatCompletionMessageToolCall[] = [];
|
|
531
|
+
// Reasoning items (with encrypted_content) to thread back into the next turn,
|
|
532
|
+
// and a human-readable summary of the model's thinking (when requested).
|
|
533
|
+
const reasoningItems: any[] = [];
|
|
534
|
+
let reasoningSummary: string | null = null;
|
|
408
535
|
|
|
409
536
|
for (const item of response.output) {
|
|
410
537
|
if (item.type === "message") {
|
|
@@ -415,6 +542,18 @@ export class GenericOpenAiClient implements GenericClient {
|
|
|
415
542
|
textContent = (textContent ?? "") + part.text;
|
|
416
543
|
}
|
|
417
544
|
}
|
|
545
|
+
} else if (item.type === "reasoning") {
|
|
546
|
+
// ResponseReasoningItem — keep the raw item so we can re-inject it
|
|
547
|
+
// (it carries encrypted_content when include was requested), and pull
|
|
548
|
+
// out any human-readable summary text.
|
|
549
|
+
reasoningItems.push(item);
|
|
550
|
+
const summaryParts = (item as any).summary ?? [];
|
|
551
|
+
for (const part of summaryParts) {
|
|
552
|
+
const text = typeof part === "string" ? part : part?.text;
|
|
553
|
+
if (text) {
|
|
554
|
+
reasoningSummary = (reasoningSummary ?? "") + text;
|
|
555
|
+
}
|
|
556
|
+
}
|
|
418
557
|
} else if (item.type === "function_call") {
|
|
419
558
|
// ResponseFunctionToolCall
|
|
420
559
|
const fc = item as ResponseFunctionToolCall;
|
|
@@ -444,12 +583,21 @@ export class GenericOpenAiClient implements GenericClient {
|
|
|
444
583
|
role: "assistant",
|
|
445
584
|
content: textContent,
|
|
446
585
|
...(toolCalls.length > 0 && { tool_calls: toolCalls }),
|
|
586
|
+
...(reasoningSummary && { reasoning_summary: reasoningSummary }),
|
|
587
|
+
// Stash the raw reasoning items (encrypted_content) so the next call
|
|
588
|
+
// to createChatResponse can re-inject them into the input. We populate
|
|
589
|
+
// both the provider-agnostic slot and the deprecated legacy field.
|
|
590
|
+
...(reasoningItems.length > 0 && {
|
|
591
|
+
_reasoning_items: reasoningItems,
|
|
592
|
+
_reasoning_details: { provider: "openai", items: reasoningItems },
|
|
593
|
+
}),
|
|
447
594
|
},
|
|
448
595
|
},
|
|
449
596
|
],
|
|
450
597
|
model: options.model,
|
|
451
598
|
usage,
|
|
452
599
|
usd_cost: usdCost,
|
|
600
|
+
response_id: (response as any).id,
|
|
453
601
|
};
|
|
454
602
|
}
|
|
455
603
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ModelPricing } from "./types";
|
|
1
|
+
import { ModelPricing, ModelCatalogEntry, completions, embeddings } from "./types";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Fireworks AI model IDs, pricing, and catalog.
|
|
@@ -101,3 +101,52 @@ export const FireworksEmbeddingPricing: Record<string, ModelPricing> = {
|
|
|
101
101
|
// Qwen3 Reranker 8B — $0.20/M tokens, context 40k
|
|
102
102
|
[FireworksModels.Qwen3Reranker8b]: { input: 0.20, output: 0.20 },
|
|
103
103
|
};
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
// ─── Active text models (non-deprecated) ─────────────────────────────────────
|
|
107
|
+
|
|
108
|
+
export const FireworksActiveTextModels: string[] = [
|
|
109
|
+
// Moonshot AI
|
|
110
|
+
FireworksModels.KimiK3,
|
|
111
|
+
FireworksModels.KimiK2p7Code,
|
|
112
|
+
FireworksModels.KimiK2_6,
|
|
113
|
+
FireworksModels.KimiK2_5,
|
|
114
|
+
// MiniMax
|
|
115
|
+
FireworksModels.MinimaxM3,
|
|
116
|
+
FireworksModels.MinimaxM2_7,
|
|
117
|
+
FireworksModels.MinimaxM2_5,
|
|
118
|
+
// Qwen
|
|
119
|
+
FireworksModels.Qwen3p7Plus,
|
|
120
|
+
// Z.ai
|
|
121
|
+
FireworksModels.Glm5p2,
|
|
122
|
+
FireworksModels.Glm5p1,
|
|
123
|
+
// DeepSeek AI
|
|
124
|
+
FireworksModels.DeepseekV4Pro,
|
|
125
|
+
FireworksModels.DeepseekV4Flash,
|
|
126
|
+
// OpenAI OSS
|
|
127
|
+
FireworksModels.GptOss120b,
|
|
128
|
+
];
|
|
129
|
+
|
|
130
|
+
export const FireworksDeprecatedTextModels: string[] = [
|
|
131
|
+
FireworksModels.Qwen3_6Plus,
|
|
132
|
+
FireworksModels.Qwen3p6Plus,
|
|
133
|
+
FireworksModels.Qwen3Vl30bA3bThinking,
|
|
134
|
+
FireworksModels.Qwen3Vl30bA3bInstruct,
|
|
135
|
+
FireworksModels.Qwen3_8b,
|
|
136
|
+
FireworksModels.Glm5,
|
|
137
|
+
FireworksModels.Glm4_7,
|
|
138
|
+
FireworksModels.DeepseekV3_2,
|
|
139
|
+
FireworksModels.DeepseekV3_1,
|
|
140
|
+
];
|
|
141
|
+
|
|
142
|
+
// ─── Catalog ──────────────────────────────────────────────────────────────────
|
|
143
|
+
|
|
144
|
+
export const FIREWORKS_MODEL_CATALOG: ModelCatalogEntry[] = [
|
|
145
|
+
...completions(FireworksActiveTextModels, "fireworks", FireworksTextPricing),
|
|
146
|
+
...completions(FireworksDeprecatedTextModels, "fireworks", FireworksTextPricing),
|
|
147
|
+
...embeddings(
|
|
148
|
+
[FireworksModels.Qwen3Embedding8b, FireworksModels.Qwen3Reranker8b],
|
|
149
|
+
"fireworks",
|
|
150
|
+
FireworksEmbeddingPricing,
|
|
151
|
+
),
|
|
152
|
+
];
|
|
@@ -35,7 +35,7 @@ export { GitHubModelsTextPricing } from "./github";
|
|
|
35
35
|
export { OpenRouterTextPricing } from "./openrouter";
|
|
36
36
|
export { LlamaTextPricing } from "./llama";
|
|
37
37
|
export { CopilotTextPricing, CopilotModelMultipliers } from "./copilot";
|
|
38
|
-
export { FireworksTextPricing } from "./fireworks";
|
|
38
|
+
export { FireworksTextPricing, FireworksEmbeddingPricing, FIREWORKS_MODEL_CATALOG, FireworksActiveTextModels, FireworksDeprecatedTextModels } from "./fireworks";
|
|
39
39
|
export { MetaTextPricing, MetaWebSearchPricingPer1000Queries } from "./meta";
|
|
40
40
|
|
|
41
41
|
// ─── Combined catalog ─────────────────────────────────────────────────────────
|
|
@@ -7,17 +7,19 @@ import { OPENAI_MODEL_CATALOG } from "./openai";
|
|
|
7
7
|
import { ANTHROPIC_MODEL_CATALOG } from "./anthropic";
|
|
8
8
|
import { GOOGLE_MODEL_CATALOG } from "./google";
|
|
9
9
|
import { XAI_MODEL_CATALOG } from "./xai";
|
|
10
|
+
import { FIREWORKS_MODEL_CATALOG } from "./fireworks";
|
|
10
11
|
|
|
11
12
|
export { ModelPricing, ModelType, ModelCatalogEntry };
|
|
12
13
|
|
|
13
14
|
/** 2.5% platform markup applied on top of all provider base rates */
|
|
14
15
|
export const USAGE_MARKUP_PERCENT = 2.5 / 100;
|
|
15
16
|
|
|
16
|
-
export { OPENAI_MODEL_CATALOG, ANTHROPIC_MODEL_CATALOG, GOOGLE_MODEL_CATALOG, XAI_MODEL_CATALOG };
|
|
17
|
+
export { OPENAI_MODEL_CATALOG, ANTHROPIC_MODEL_CATALOG, GOOGLE_MODEL_CATALOG, XAI_MODEL_CATALOG, FIREWORKS_MODEL_CATALOG };
|
|
17
18
|
|
|
18
19
|
export const ALL_MODEL_CATALOG: ModelCatalogEntry[] = [
|
|
19
20
|
...OPENAI_MODEL_CATALOG,
|
|
20
21
|
...ANTHROPIC_MODEL_CATALOG,
|
|
21
22
|
...GOOGLE_MODEL_CATALOG,
|
|
22
23
|
...XAI_MODEL_CATALOG,
|
|
24
|
+
...FIREWORKS_MODEL_CATALOG,
|
|
23
25
|
];
|
|
@@ -232,8 +232,8 @@ export const OpenAiSearchModels: string[] = [
|
|
|
232
232
|
export const OpenAiTextPricing: Record<string, ModelPricing> = {
|
|
233
233
|
// gpt-5.6 series — short context / long context pricing
|
|
234
234
|
[OpenAiModels.GPT_56_Sol]: { input: 5.0, cached_input: 0.5, cache_write: 6.25, output: 30.0, input_gt_200k: 10.0, output_gt_200k: 45.0 },
|
|
235
|
-
[OpenAiModels.GPT_56_Terra]: { input: 2.
|
|
236
|
-
[OpenAiModels.GPT_56_Luna]: { input:
|
|
235
|
+
[OpenAiModels.GPT_56_Terra]: { input: 2.0, cached_input: 0.20, cache_write: 2.5, output: 12.0, input_gt_200k: 4.0, output_gt_200k: 18.0 },
|
|
236
|
+
[OpenAiModels.GPT_56_Luna]: { input: 0.2, cached_input: 0.02, cache_write: 0.25, output: 1.2, input_gt_200k: 0.4, output_gt_200k: 1.8 },
|
|
237
237
|
[OpenAiModels.GPT_55]: { input: 5.0, cached_input: 0.5, output: 30.0 },
|
|
238
238
|
[OpenAiModels.GPT_55_Pro]: { input: 30.0, cached_input: 0, output: 180.0, reasoningLevels: ["medium", "high", "xhigh"] },
|
|
239
239
|
[OpenAiModels.GPT_54]: { input: 2.5, cached_input: 0.25, output: 15.0 },
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QWEN Cloud pricing (USD per 1M tokens)
|
|
3
|
+
* Source: https://home.qwencloud.com/data/api.json?product=AliyunDeliveryService&action=ListModelSeries
|
|
4
|
+
* International DashScope endpoint: https://dashscope-intl.aliyuncs.com/compatible-mode/v1
|
|
5
|
+
* Last updated: 2026-08-03
|
|
6
|
+
*
|
|
7
|
+
* Notes:
|
|
8
|
+
* - Several Qwen models use tiered pricing based on input context length.
|
|
9
|
+
* We use the standard tier (≤256k) as the base price here.
|
|
10
|
+
* - DeepSeek and GLM models are also available via QwenCloud's DashScope endpoint.
|
|
11
|
+
* - Video/image/audio generation models have per-second/per-image pricing (not per-token)
|
|
12
|
+
* and are NOT included in QwenTextPricing.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export const QwenTextPricing: Record<string, { input: number; output: number; cached_input?: number }> = {
|
|
16
|
+
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
// Qwen3.8 series (newest flagship, released 2026-08)
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// qwen3.8-max: $2/$6, implicit cache: $0.25, explicit cache creation: $2.5, explicit cache read: $0.17
|
|
21
|
+
"qwen3.8-max": { input: 2.00, output: 6.00, cached_input: 0.25 },
|
|
22
|
+
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Qwen3.7 series
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// qwen3.7-plus: tiered — ≤256k: $0.4/$1.6, 256k-1M: $1.2/$4.8
|
|
27
|
+
// implicit cache: $0.08 (≤256k), explicit cache creation: $0.5, explicit cache read: $0.04
|
|
28
|
+
"qwen3.7-plus": { input: 0.40, output: 1.60, cached_input: 0.08 },
|
|
29
|
+
"qwen3.7-plus-2026-05-26": { input: 0.40, output: 1.60, cached_input: 0.08 },
|
|
30
|
+
|
|
31
|
+
// qwen3.7-flash: tiered — ≤32k: $0.03/$0.13, 32k-256k: $0.10/$0.40, 256k-1M: $0.20/$0.80
|
|
32
|
+
"qwen3.7-flash": { input: 0.03, output: 0.13, cached_input: 0.006 },
|
|
33
|
+
"qwen3.7-flash-2026-07-15": { input: 0.03, output: 0.13, cached_input: 0.006 },
|
|
34
|
+
|
|
35
|
+
// qwen3.7-max: $2.50/$7.50, cache: $0.50
|
|
36
|
+
"qwen3.7-max": { input: 2.50, output: 7.50, cached_input: 0.50 },
|
|
37
|
+
"qwen3.7-max-2026-06-08": { input: 2.50, output: 7.50, cached_input: 0.50 },
|
|
38
|
+
"qwen3.7-max-2026-05-20": { input: 2.50, output: 7.50 },
|
|
39
|
+
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
// Qwen3.6 series
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
// qwen3.6-flash: tiered — ≤256k: $0.25/$1.50, 256k-1M: $1.00/$4.00
|
|
44
|
+
"qwen3.6-flash": { input: 0.25, output: 1.50, cached_input: 0.025 },
|
|
45
|
+
"qwen3.6-flash-2026-04-16": { input: 0.25, output: 1.50 },
|
|
46
|
+
// qwen3.6-plus: tiered — ≤256k: $0.50/$3.00, 256k-1M: $2.00/$6.00
|
|
47
|
+
"qwen3.6-plus": { input: 0.50, output: 3.00, cached_input: 0.05 },
|
|
48
|
+
"qwen3.6-plus-2026-04-02": { input: 0.50, output: 3.00 },
|
|
49
|
+
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// Legacy alias models (qwen-turbo, qwen-plus, qwen-max, qwen-long)
|
|
52
|
+
// These are rolling aliases that point to the latest generation.
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
"qwen-turbo": { input: 0.05, output: 0.20, cached_input: 0.005 },
|
|
55
|
+
"qwen-turbo-latest": { input: 0.05, output: 0.20, cached_input: 0.005 },
|
|
56
|
+
"qwen-plus": { input: 0.40, output: 1.20, cached_input: 0.04 },
|
|
57
|
+
"qwen-plus-latest": { input: 0.40, output: 1.20, cached_input: 0.04 },
|
|
58
|
+
"qwen-max": { input: 0.861, output: 3.44, cached_input: 0.086 },
|
|
59
|
+
"qwen-max-latest": { input: 0.861, output: 3.44, cached_input: 0.086 },
|
|
60
|
+
// qwen-long: extended context up to 10M tokens
|
|
61
|
+
"qwen-long": { input: 0.05, output: 0.20, cached_input: 0.005 },
|
|
62
|
+
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
// DeepSeek models (available via QwenCloud/DashScope endpoint)
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
// DeepSeek-V4-Flash: lightweight MoE, 284B params, 13B activated, 1M context
|
|
67
|
+
"deepseek-v4-flash": { input: 0.20, output: 0.40, cached_input: 0.04 },
|
|
68
|
+
"deepseek-v4-flash-0731": { input: 0.20, output: 0.40, cached_input: 0.04 },
|
|
69
|
+
// DeepSeek-V4-Pro: flagship MoE, 1.6T params, 49B activated, 1M context
|
|
70
|
+
"deepseek-v4-pro": { input: 2.40, output: 4.80, cached_input: 0.20 },
|
|
71
|
+
// DeepSeek-V3.2: sparse attention, 128k context
|
|
72
|
+
// explicit cache creation: $0.713, explicit cache read: $0.057
|
|
73
|
+
"deepseek-v3.2": { input: 0.57, output: 1.71, cached_input: 0.114 },
|
|
74
|
+
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
// GLM models (Zhipu AI, available via QwenCloud/DashScope endpoint)
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
// GLM-5.2-Fast-Preview: 1M context, high-speed inference
|
|
79
|
+
"glm-5.2-fast-preview": { input: 2.80, output: 8.80, cached_input: 0.56 },
|
|
80
|
+
};
|