@codeworksh/aikit 0.3.0 → 0.3.1
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/agent/codemode/drivers/drivers.d.mts +1 -1
- package/{codemode-z3ZIw-5q.d.mts → codemode-SiETGR3z.d.mts} +255 -79
- package/index.d.mts +3 -3
- package/index.mjs +739 -52
- package/index.mjs.map +1 -1
- package/package.json +4 -1
package/index.mjs
CHANGED
|
@@ -6,15 +6,33 @@ import addFormats from "ajv-formats";
|
|
|
6
6
|
import { uuidv7 } from "uuidv7";
|
|
7
7
|
import Anthropic from "@anthropic-ai/sdk";
|
|
8
8
|
import { parse } from "partial-json";
|
|
9
|
+
import OpenAI from "openai";
|
|
9
10
|
import { transform } from "esbuild";
|
|
10
11
|
//#region src/provider/provider.ts
|
|
11
12
|
let Provider;
|
|
12
13
|
(function(_Provider) {
|
|
13
14
|
const KnownProviderEnum = _Provider.KnownProviderEnum = {
|
|
14
15
|
anthropic: "anthropic",
|
|
15
|
-
openai: "openai"
|
|
16
|
+
openai: "openai",
|
|
17
|
+
githubCopilot: "github-copilot",
|
|
18
|
+
openrouter: "openrouter",
|
|
19
|
+
groq: "groq",
|
|
20
|
+
xai: "xai",
|
|
21
|
+
cerebras: "cerebras",
|
|
22
|
+
zai: "zai",
|
|
23
|
+
opencode: "opencode"
|
|
16
24
|
};
|
|
17
|
-
const KnownProviderSchema = _Provider.KnownProviderSchema = Type.Union([
|
|
25
|
+
const KnownProviderSchema = _Provider.KnownProviderSchema = Type.Union([
|
|
26
|
+
Type.Literal(KnownProviderEnum.anthropic),
|
|
27
|
+
Type.Literal(KnownProviderEnum.openai),
|
|
28
|
+
Type.Literal(KnownProviderEnum.githubCopilot),
|
|
29
|
+
Type.Literal(KnownProviderEnum.openrouter),
|
|
30
|
+
Type.Literal(KnownProviderEnum.groq),
|
|
31
|
+
Type.Literal(KnownProviderEnum.xai),
|
|
32
|
+
Type.Literal(KnownProviderEnum.cerebras),
|
|
33
|
+
Type.Literal(KnownProviderEnum.zai),
|
|
34
|
+
Type.Literal(KnownProviderEnum.opencode)
|
|
35
|
+
]);
|
|
18
36
|
_Provider.Info = Type.Object({
|
|
19
37
|
id: KnownProviderSchema,
|
|
20
38
|
name: Type.String(),
|
|
@@ -76,6 +94,46 @@ let ModelCatalog;
|
|
|
76
94
|
}, 60 * 1e3 * 60).unref();
|
|
77
95
|
})(ModelCatalog || (ModelCatalog = {}));
|
|
78
96
|
//#endregion
|
|
97
|
+
//#region src/model/transform.ts
|
|
98
|
+
function defaultBaseUrl(providerId) {
|
|
99
|
+
switch (providerId) {
|
|
100
|
+
case Provider.KnownProviderEnum.openai: return "https://api.openai.com/v1";
|
|
101
|
+
default: return;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function resolveDefaultProtocol(providerId) {
|
|
105
|
+
switch (providerId) {
|
|
106
|
+
case Provider.KnownProviderEnum.anthropic: return Model.KnownProtocolEnum.anthropicMessages;
|
|
107
|
+
case Provider.KnownProviderEnum.openai: return Model.KnownProtocolEnum.openaiResponses;
|
|
108
|
+
default: return Model.KnownProtocolEnum.openaiCompletions;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function applyModification(providerId, provider, model) {
|
|
112
|
+
const baseUrl = model.baseUrl ?? provider.baseUrl ?? provider.api ?? defaultBaseUrl(providerId);
|
|
113
|
+
const normalized = {
|
|
114
|
+
id: model.id,
|
|
115
|
+
name: model.name,
|
|
116
|
+
provider: Model.toProviderInfo(providerId, provider),
|
|
117
|
+
baseUrl,
|
|
118
|
+
reasoning: Boolean(model.reasoning),
|
|
119
|
+
input: Model.normalizeInput(model.modalities.input),
|
|
120
|
+
cost: {
|
|
121
|
+
input: model.cost?.input ?? 0,
|
|
122
|
+
output: model.cost?.output ?? 0,
|
|
123
|
+
cacheRead: model.cost?.cache_read ?? 0,
|
|
124
|
+
cacheWrite: model.cost?.cache_write ?? 0
|
|
125
|
+
},
|
|
126
|
+
contextWindow: model.limit?.context ?? 0,
|
|
127
|
+
maxTokens: model.limit?.output ?? 0,
|
|
128
|
+
headers: model.headers ?? provider.headers,
|
|
129
|
+
protocol: resolveDefaultProtocol(providerId)
|
|
130
|
+
};
|
|
131
|
+
if (providerId === Provider.KnownProviderEnum.openai) {
|
|
132
|
+
normalized.supportedProtocols = { openaiCompletions: Model.KnownProtocolEnum.openaiCompletions };
|
|
133
|
+
return normalized;
|
|
134
|
+
} else return normalized;
|
|
135
|
+
}
|
|
136
|
+
//#endregion
|
|
79
137
|
//#region src/model/model.ts
|
|
80
138
|
let Model;
|
|
81
139
|
(function(_Model) {
|
|
@@ -97,6 +155,11 @@ let Model;
|
|
|
97
155
|
cacheWrite: Type.Number()
|
|
98
156
|
});
|
|
99
157
|
const HeadersSchema = Type.Optional(Type.Record(Type.String(), Type.String()));
|
|
158
|
+
const SupportedProtocolsSchema = _Model.SupportedProtocolsSchema = Type.Object({
|
|
159
|
+
anthropicMessages: Type.Optional(Type.Literal(KnownProtocolEnum.anthropicMessages)),
|
|
160
|
+
openaiCompletions: Type.Optional(Type.Literal(KnownProtocolEnum.openaiCompletions)),
|
|
161
|
+
openaiResponses: Type.Optional(Type.Literal(KnownProtocolEnum.openaiResponses))
|
|
162
|
+
});
|
|
100
163
|
const BaseSchema = _Model.BaseSchema = Type.Object({
|
|
101
164
|
id: Type.String(),
|
|
102
165
|
name: Type.String(),
|
|
@@ -107,7 +170,8 @@ let Model;
|
|
|
107
170
|
cost: CostSchema,
|
|
108
171
|
contextWindow: Type.Number(),
|
|
109
172
|
maxTokens: Type.Number(),
|
|
110
|
-
headers: HeadersSchema
|
|
173
|
+
headers: HeadersSchema,
|
|
174
|
+
supportedProtocols: Type.Optional(Type.Partial(SupportedProtocolsSchema))
|
|
111
175
|
});
|
|
112
176
|
const OpenAICompletionsCompatSchema = _Model.OpenAICompletionsCompatSchema = Type.Object({
|
|
113
177
|
supportsStore: Type.Optional(Type.Boolean()),
|
|
@@ -128,6 +192,7 @@ let Model;
|
|
|
128
192
|
])),
|
|
129
193
|
openRouterRouting: Type.Optional(Provider.OpenRouterRoutingSchema),
|
|
130
194
|
vercelGatewayRouting: Type.Optional(Provider.VercelGatewayRoutingSchema),
|
|
195
|
+
zaiToolStream: Type.Optional(Type.Boolean()),
|
|
131
196
|
supportsStrictMode: Type.Optional(Type.Boolean())
|
|
132
197
|
});
|
|
133
198
|
const OpenAIResponsesCompatSchema = _Model.OpenAIResponsesCompatSchema = Type.Object({});
|
|
@@ -141,7 +206,7 @@ let Model;
|
|
|
141
206
|
protocol: Type.Literal(KnownProtocolEnum.openaiResponses),
|
|
142
207
|
compat: Type.Optional(OpenAIResponsesCompatSchema)
|
|
143
208
|
})]);
|
|
144
|
-
_Model.
|
|
209
|
+
_Model.Info = Type.Union([
|
|
145
210
|
AnthropicSchema,
|
|
146
211
|
OpenAICompletionsSchema,
|
|
147
212
|
OpenAIResponsesSchema
|
|
@@ -155,18 +220,13 @@ let Model;
|
|
|
155
220
|
usage.cost.total = usage.cost.input + usage.cost.output + usage.cost.cacheRead + usage.cost.cacheWrite;
|
|
156
221
|
}
|
|
157
222
|
_Model.calculateCost = calculateCost;
|
|
158
|
-
function resolveProtocol(providerId) {
|
|
159
|
-
switch (providerId) {
|
|
160
|
-
case Provider.KnownProviderEnum.anthropic: return KnownProtocolEnum.anthropicMessages;
|
|
161
|
-
case Provider.KnownProviderEnum.openai: return KnownProtocolEnum.openaiResponses;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
223
|
function normalizeInput(input) {
|
|
165
224
|
const normalized = /* @__PURE__ */ new Set();
|
|
166
225
|
for (const modality of input ?? ["text"]) if (modality === "text" || modality === "image") normalized.add(modality);
|
|
167
226
|
if (normalized.size === 0) normalized.add("text");
|
|
168
227
|
return [...normalized];
|
|
169
228
|
}
|
|
229
|
+
_Model.normalizeInput = normalizeInput;
|
|
170
230
|
function toProviderInfo(providerId, provider) {
|
|
171
231
|
return {
|
|
172
232
|
id: providerId,
|
|
@@ -176,26 +236,9 @@ let Model;
|
|
|
176
236
|
options: provider.headers
|
|
177
237
|
};
|
|
178
238
|
}
|
|
239
|
+
_Model.toProviderInfo = toProviderInfo;
|
|
179
240
|
function toModelValue(providerId, provider, model) {
|
|
180
|
-
|
|
181
|
-
return {
|
|
182
|
-
id: model.id,
|
|
183
|
-
name: model.name,
|
|
184
|
-
provider: toProviderInfo(providerId, provider),
|
|
185
|
-
baseUrl,
|
|
186
|
-
reasoning: Boolean(model.reasoning),
|
|
187
|
-
input: normalizeInput(model.modalities.input),
|
|
188
|
-
cost: {
|
|
189
|
-
input: model.cost?.input ?? 0,
|
|
190
|
-
output: model.cost?.output ?? 0,
|
|
191
|
-
cacheRead: model.cost?.cache_read ?? 0,
|
|
192
|
-
cacheWrite: model.cost?.cache_write ?? 0
|
|
193
|
-
},
|
|
194
|
-
contextWindow: model.limit?.context ?? 0,
|
|
195
|
-
maxTokens: model.limit?.output ?? 0,
|
|
196
|
-
headers: model.headers ?? provider.headers,
|
|
197
|
-
protocol: resolveProtocol(providerId)
|
|
198
|
-
};
|
|
241
|
+
return applyModification(providerId, provider, model);
|
|
199
242
|
}
|
|
200
243
|
async function getBuiltInModels() {
|
|
201
244
|
return pipe(await ModelCatalog.get(), pick(BUILTINS), mapValues((provider, providerId) => mapValues(provider.models, (model) => toModelValue(providerId, provider, model))));
|
|
@@ -211,8 +254,13 @@ let Model;
|
|
|
211
254
|
}
|
|
212
255
|
return registry;
|
|
213
256
|
});
|
|
214
|
-
async function getModel(provider, model) {
|
|
215
|
-
|
|
257
|
+
async function getModel(provider, model, overrides) {
|
|
258
|
+
const result = (await registry()).get(provider)?.get(model);
|
|
259
|
+
if (result && overrides) return {
|
|
260
|
+
...result,
|
|
261
|
+
...overrides
|
|
262
|
+
};
|
|
263
|
+
return result;
|
|
216
264
|
}
|
|
217
265
|
_Model.getModel = getModel;
|
|
218
266
|
async function getProviders() {
|
|
@@ -1296,8 +1344,10 @@ function sanitizeSurrogates(text) {
|
|
|
1296
1344
|
return text.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "");
|
|
1297
1345
|
}
|
|
1298
1346
|
//#endregion
|
|
1299
|
-
//#region src/provider/providers/
|
|
1347
|
+
//#region src/provider/providers/utils.ts
|
|
1300
1348
|
function getEnvApiKey(_provider) {}
|
|
1349
|
+
//#endregion
|
|
1350
|
+
//#region src/provider/providers/anthropic/index.ts
|
|
1301
1351
|
function resolveCacheRetention(cacheRetention) {
|
|
1302
1352
|
if (cacheRetention) return cacheRetention;
|
|
1303
1353
|
if (typeof process !== "undefined" && process.env.CODEWORK_CACHE_RETENTION === "long") return "long";
|
|
@@ -1374,7 +1424,7 @@ function mergeHeaders(...headerSources) {
|
|
|
1374
1424
|
function isOAuthToken(apiKey) {
|
|
1375
1425
|
return apiKey.includes("sk-ant-oat");
|
|
1376
1426
|
}
|
|
1377
|
-
function createClient(model, apiKey, interleavedThinking, optionsHeaders) {
|
|
1427
|
+
function createClient$1(model, apiKey, interleavedThinking, optionsHeaders) {
|
|
1378
1428
|
const betaFeatures = ["fine-grained-tool-streaming-2025-05-14"];
|
|
1379
1429
|
if (interleavedThinking) betaFeatures.push("interleaved-thinking-2025-05-14");
|
|
1380
1430
|
if (isOAuthToken(apiKey)) {
|
|
@@ -1432,7 +1482,7 @@ function assertReplayableAssistant(message) {
|
|
|
1432
1482
|
if (!sawToolCall && message.stopReason === "toolUse") throw new Error("Assistant message with stopReason 'toolUse' must contain at least one toolCall");
|
|
1433
1483
|
if (sawTerminalToolCall && sawNonTerminalToolCall) throw new Error("Assistant message cannot mix terminal and non-terminal toolCall states");
|
|
1434
1484
|
}
|
|
1435
|
-
function buildSyntheticToolResult(block) {
|
|
1485
|
+
function buildSyntheticToolResult$1(block) {
|
|
1436
1486
|
return {
|
|
1437
1487
|
type: "tool_result",
|
|
1438
1488
|
tool_use_id: block.callID,
|
|
@@ -1447,7 +1497,7 @@ function buildSyntheticToolResult(block) {
|
|
|
1447
1497
|
is_error: true
|
|
1448
1498
|
};
|
|
1449
1499
|
}
|
|
1450
|
-
function convertMessages(messages, model, isOAuthToken, cacheControl) {
|
|
1500
|
+
function convertMessages$1(messages, model, isOAuthToken, cacheControl) {
|
|
1451
1501
|
const params = [];
|
|
1452
1502
|
const transformedMessages = Message.transformMessages(messages, model, normalizeToolCallId);
|
|
1453
1503
|
for (const msg of transformedMessages) {
|
|
@@ -1503,7 +1553,7 @@ function convertMessages(messages, model, isOAuthToken, cacheControl) {
|
|
|
1503
1553
|
name: isOAuthToken ? toClaudeCodeName(block.name) : block.name,
|
|
1504
1554
|
input: block.arguments ?? {}
|
|
1505
1555
|
});
|
|
1506
|
-
if (block.status === "pending" || block.status === "running") toolResults.push(buildSyntheticToolResult(block));
|
|
1556
|
+
if (block.status === "pending" || block.status === "running") toolResults.push(buildSyntheticToolResult$1(block));
|
|
1507
1557
|
else toolResults.push({
|
|
1508
1558
|
type: "tool_result",
|
|
1509
1559
|
tool_use_id: block.callID,
|
|
@@ -1535,7 +1585,7 @@ function convertMessages(messages, model, isOAuthToken, cacheControl) {
|
|
|
1535
1585
|
}
|
|
1536
1586
|
return params;
|
|
1537
1587
|
}
|
|
1538
|
-
function convertTools(tools, isOAuthToken) {
|
|
1588
|
+
function convertTools$1(tools, isOAuthToken) {
|
|
1539
1589
|
if (!tools) return [];
|
|
1540
1590
|
return tools.map((tool) => {
|
|
1541
1591
|
const jsonSchema = tool.parameters;
|
|
@@ -1550,7 +1600,7 @@ function convertTools(tools, isOAuthToken) {
|
|
|
1550
1600
|
};
|
|
1551
1601
|
});
|
|
1552
1602
|
}
|
|
1553
|
-
function mapStopReason(reason) {
|
|
1603
|
+
function mapStopReason$1(reason) {
|
|
1554
1604
|
switch (reason) {
|
|
1555
1605
|
case "end_turn": return "stop";
|
|
1556
1606
|
case "max_tokens": return "length";
|
|
@@ -1562,11 +1612,11 @@ function mapStopReason(reason) {
|
|
|
1562
1612
|
default: throw new Error("Unhandled Anthropic stop reason");
|
|
1563
1613
|
}
|
|
1564
1614
|
}
|
|
1565
|
-
function buildParams(model, context, isOAuthToken, options) {
|
|
1615
|
+
function buildParams$1(model, context, isOAuthToken, options) {
|
|
1566
1616
|
const { cacheControl } = getCacheControl(model.baseUrl, options?.cacheRetention);
|
|
1567
1617
|
const params = {
|
|
1568
1618
|
model: model.id,
|
|
1569
|
-
messages: convertMessages(context.messages, model, isOAuthToken, cacheControl),
|
|
1619
|
+
messages: convertMessages$1(context.messages, model, isOAuthToken, cacheControl),
|
|
1570
1620
|
max_tokens: options?.maxTokens || model.maxTokens / 3 | 0,
|
|
1571
1621
|
stream: true
|
|
1572
1622
|
};
|
|
@@ -1587,7 +1637,7 @@ function buildParams(model, context, isOAuthToken, options) {
|
|
|
1587
1637
|
...cacheControl ? { cache_control: cacheControl } : {}
|
|
1588
1638
|
}];
|
|
1589
1639
|
if (options?.temperature !== void 0 && !options?.thinkingEnabled) params.temperature = options.temperature;
|
|
1590
|
-
if (context.tools) params.tools = convertTools(context.tools, isOAuthToken);
|
|
1640
|
+
if (context.tools) params.tools = convertTools$1(context.tools, isOAuthToken);
|
|
1591
1641
|
if (options?.thinkingEnabled && model.reasoning) params.thinking = {
|
|
1592
1642
|
type: "enabled",
|
|
1593
1643
|
budget_tokens: options.thinkingBudgetTokens || 1024
|
|
@@ -1635,13 +1685,13 @@ const streamAnthropic = (model, context, options) => {
|
|
|
1635
1685
|
client = options.client;
|
|
1636
1686
|
isOAuthToken = false;
|
|
1637
1687
|
} else {
|
|
1638
|
-
const apiKey = options?.apiKey ?? getEnvApiKey(model.provider);
|
|
1688
|
+
const apiKey = options?.apiKey ?? /* @__PURE__ */ getEnvApiKey(model.provider);
|
|
1639
1689
|
if (!apiKey) throw new Error(`No API key for provider: ${model.provider.id}`);
|
|
1640
|
-
const created = createClient(model, apiKey, options?.interleavedThinking ?? true, options?.headers);
|
|
1690
|
+
const created = createClient$1(model, apiKey, options?.interleavedThinking ?? true, options?.headers);
|
|
1641
1691
|
client = created.client;
|
|
1642
1692
|
isOAuthToken = created.isOAuthToken;
|
|
1643
1693
|
}
|
|
1644
|
-
let params = buildParams(model, context, isOAuthToken, options);
|
|
1694
|
+
let params = buildParams$1(model, context, isOAuthToken, options);
|
|
1645
1695
|
const nextParams = await options?.onPayload?.(params, model);
|
|
1646
1696
|
if (nextParams !== void 0) params = nextParams;
|
|
1647
1697
|
const anthropicStream = client.messages.stream({
|
|
@@ -1784,7 +1834,7 @@ const streamAnthropic = (model, context, options) => {
|
|
|
1784
1834
|
}
|
|
1785
1835
|
}
|
|
1786
1836
|
} else if (event.type === "message_delta") {
|
|
1787
|
-
if (event.delta.stop_reason) output.stopReason = mapStopReason(event.delta.stop_reason);
|
|
1837
|
+
if (event.delta.stop_reason) output.stopReason = mapStopReason$1(event.delta.stop_reason);
|
|
1788
1838
|
if (event.usage.input_tokens != null) output.usage.input = event.usage.input_tokens;
|
|
1789
1839
|
if (event.usage.output_tokens != null) output.usage.output = event.usage.output_tokens;
|
|
1790
1840
|
if (event.usage.cache_read_input_tokens != null) output.usage.cacheRead = event.usage.cache_read_input_tokens;
|
|
@@ -1817,7 +1867,7 @@ const streamAnthropic = (model, context, options) => {
|
|
|
1817
1867
|
return stream;
|
|
1818
1868
|
};
|
|
1819
1869
|
const streamSimpleAnthropic = (model, context, options) => {
|
|
1820
|
-
const apiKey = options?.apiKey || getEnvApiKey(model.provider);
|
|
1870
|
+
const apiKey = options?.apiKey || /* @__PURE__ */ getEnvApiKey(model.provider);
|
|
1821
1871
|
if (!apiKey) throw new Error(`No API key for provider: ${model.provider.id}`);
|
|
1822
1872
|
const base = Stream.buildBaseOptions(model, options, apiKey);
|
|
1823
1873
|
if (!options?.reasoning) return streamAnthropic(model, context, {
|
|
@@ -1832,14 +1882,651 @@ const streamSimpleAnthropic = (model, context, options) => {
|
|
|
1832
1882
|
thinkingBudgetTokens: adjusted.thinkingBudget
|
|
1833
1883
|
});
|
|
1834
1884
|
};
|
|
1835
|
-
|
|
1836
|
-
//#region src/provider/register.ts
|
|
1837
|
-
const providerModules = [["providers/anthropic/index.ts", {
|
|
1885
|
+
const anthropicProtocolProvider = {
|
|
1838
1886
|
protocol: Model.KnownProtocolEnum.anthropicMessages,
|
|
1839
1887
|
stream: streamAnthropic,
|
|
1840
1888
|
streamSimple: streamSimpleAnthropic
|
|
1841
|
-
}
|
|
1842
|
-
|
|
1889
|
+
};
|
|
1890
|
+
//#endregion
|
|
1891
|
+
//#region src/provider/providers/github/github.ts
|
|
1892
|
+
function inferCopilotInitiator(messages) {
|
|
1893
|
+
const last = messages[messages.length - 1];
|
|
1894
|
+
return last && last.role !== "user" ? "agent" : "user";
|
|
1895
|
+
}
|
|
1896
|
+
function hasCopilotVisionInput(messages) {
|
|
1897
|
+
return messages.some((msg) => {
|
|
1898
|
+
if (msg.role === "user") return msg.parts.some((c) => c.type === "image");
|
|
1899
|
+
return false;
|
|
1900
|
+
});
|
|
1901
|
+
}
|
|
1902
|
+
function buildCopilotDynamicHeaders(params) {
|
|
1903
|
+
const headers = {
|
|
1904
|
+
"X-Initiator": inferCopilotInitiator(params.messages),
|
|
1905
|
+
"Openai-Intent": "conversation-edits"
|
|
1906
|
+
};
|
|
1907
|
+
if (params.hasImages) headers["Copilot-Vision-Request"] = "true";
|
|
1908
|
+
return headers;
|
|
1909
|
+
}
|
|
1910
|
+
//#endregion
|
|
1911
|
+
//#region src/provider/providers/openai/completions.ts
|
|
1912
|
+
function hasToolHistory(messages) {
|
|
1913
|
+
for (const msg of messages) {
|
|
1914
|
+
if (msg.role !== "assistant") continue;
|
|
1915
|
+
if (msg.parts.some((block) => block.type === "toolCall")) return true;
|
|
1916
|
+
}
|
|
1917
|
+
return false;
|
|
1918
|
+
}
|
|
1919
|
+
function buildSyntheticToolResult(block, compat) {
|
|
1920
|
+
const message = {
|
|
1921
|
+
role: "tool",
|
|
1922
|
+
content: [
|
|
1923
|
+
"<result>",
|
|
1924
|
+
"No Result Provided",
|
|
1925
|
+
"</result>"
|
|
1926
|
+
].join("\n"),
|
|
1927
|
+
tool_call_id: block.callID
|
|
1928
|
+
};
|
|
1929
|
+
if (compat.requiresToolResultName) message.name = block.name;
|
|
1930
|
+
return message;
|
|
1931
|
+
}
|
|
1932
|
+
function resolveBaseUrl(model) {
|
|
1933
|
+
return model.baseUrl || (model.provider.id === Provider.KnownProviderEnum.openai ? "https://api.openai.com/v1" : "");
|
|
1934
|
+
}
|
|
1935
|
+
const streamOpenAICompletions = (model, context, options) => {
|
|
1936
|
+
const stream = new AssistantMessageEventStream();
|
|
1937
|
+
(async () => {
|
|
1938
|
+
const output = Message.createAssistantMessage({
|
|
1939
|
+
role: "assistant",
|
|
1940
|
+
parts: [],
|
|
1941
|
+
protocol: model.protocol,
|
|
1942
|
+
provider: model.provider,
|
|
1943
|
+
model: model.id,
|
|
1944
|
+
usage: {
|
|
1945
|
+
input: 0,
|
|
1946
|
+
output: 0,
|
|
1947
|
+
cacheRead: 0,
|
|
1948
|
+
cacheWrite: 0,
|
|
1949
|
+
totalTokens: 0,
|
|
1950
|
+
cost: {
|
|
1951
|
+
input: 0,
|
|
1952
|
+
output: 0,
|
|
1953
|
+
cacheRead: 0,
|
|
1954
|
+
cacheWrite: 0,
|
|
1955
|
+
total: 0
|
|
1956
|
+
}
|
|
1957
|
+
},
|
|
1958
|
+
stopReason: "stop",
|
|
1959
|
+
time: {
|
|
1960
|
+
created: Date.now(),
|
|
1961
|
+
completed: Date.now()
|
|
1962
|
+
}
|
|
1963
|
+
});
|
|
1964
|
+
try {
|
|
1965
|
+
const client = createClient(model, context, options?.apiKey || /* @__PURE__ */ getEnvApiKey(model.provider) || "", options?.headers);
|
|
1966
|
+
let params = buildParams(model, context, options);
|
|
1967
|
+
const nextParams = await options?.onPayload?.(params, model);
|
|
1968
|
+
if (nextParams !== void 0) params = nextParams;
|
|
1969
|
+
const openaiStream = await client.chat.completions.create(params, { signal: options?.signal });
|
|
1970
|
+
stream.push({
|
|
1971
|
+
type: "start",
|
|
1972
|
+
partial: output
|
|
1973
|
+
});
|
|
1974
|
+
let currentBlock = null;
|
|
1975
|
+
const blocks = output.parts;
|
|
1976
|
+
const activeToolCalls = /* @__PURE__ */ new Map();
|
|
1977
|
+
const finishCurrentBlock = (block) => {
|
|
1978
|
+
if (block) {
|
|
1979
|
+
const partIndex = block.index ?? blocks.indexOf(block);
|
|
1980
|
+
delete block.index;
|
|
1981
|
+
if (block.type === "text") stream.push({
|
|
1982
|
+
type: "text.end",
|
|
1983
|
+
partIndex,
|
|
1984
|
+
content: block.text,
|
|
1985
|
+
partial: output
|
|
1986
|
+
});
|
|
1987
|
+
else if (block.type === "thinking") stream.push({
|
|
1988
|
+
type: "thinking.end",
|
|
1989
|
+
partIndex,
|
|
1990
|
+
content: block.thinking,
|
|
1991
|
+
partial: output
|
|
1992
|
+
});
|
|
1993
|
+
else if (block.type === "toolCall") {
|
|
1994
|
+
if (block.streamIndex !== void 0) activeToolCalls.delete(block.streamIndex);
|
|
1995
|
+
block.arguments = parseStreamingJson(block.partialArgs);
|
|
1996
|
+
delete block.partialArgs;
|
|
1997
|
+
delete block.streamIndex;
|
|
1998
|
+
stream.push({
|
|
1999
|
+
type: "toolcall.end",
|
|
2000
|
+
partIndex,
|
|
2001
|
+
toolCall: block,
|
|
2002
|
+
partial: output
|
|
2003
|
+
});
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
};
|
|
2007
|
+
const flushActiveToolCalls = () => {
|
|
2008
|
+
for (const toolBlock of [...activeToolCalls.values()].sort((a, b) => (a.index ?? 0) - (b.index ?? 0))) finishCurrentBlock(toolBlock);
|
|
2009
|
+
};
|
|
2010
|
+
for await (const chunk of openaiStream) {
|
|
2011
|
+
if (!chunk || typeof chunk !== "object") continue;
|
|
2012
|
+
output.responseId ||= chunk.id;
|
|
2013
|
+
if (chunk.usage) output.usage = parseChunkUsage(chunk.usage, model);
|
|
2014
|
+
const choice = Array.isArray(chunk.choices) ? chunk.choices[0] : void 0;
|
|
2015
|
+
if (!choice) continue;
|
|
2016
|
+
const choiceWithUsage = choice;
|
|
2017
|
+
if (!chunk.usage && choiceWithUsage.usage) output.usage = parseChunkUsage(choiceWithUsage.usage, model);
|
|
2018
|
+
if (choice.finish_reason) {
|
|
2019
|
+
const finishReasonResult = mapStopReason(choice.finish_reason);
|
|
2020
|
+
output.stopReason = finishReasonResult.stopReason;
|
|
2021
|
+
if (finishReasonResult.errorMessage) output.errorMessage = finishReasonResult.errorMessage;
|
|
2022
|
+
}
|
|
2023
|
+
if (!choice.delta) continue;
|
|
2024
|
+
if (choice.delta.content !== null && choice.delta.content !== void 0 && choice.delta.content.length > 0) {
|
|
2025
|
+
if (activeToolCalls.size > 0) flushActiveToolCalls();
|
|
2026
|
+
if (!currentBlock || currentBlock.type !== "text") {
|
|
2027
|
+
finishCurrentBlock(currentBlock);
|
|
2028
|
+
currentBlock = {
|
|
2029
|
+
type: "text",
|
|
2030
|
+
text: "",
|
|
2031
|
+
index: output.parts.length
|
|
2032
|
+
};
|
|
2033
|
+
output.parts.push(currentBlock);
|
|
2034
|
+
stream.push({
|
|
2035
|
+
type: "text.start",
|
|
2036
|
+
partIndex: currentBlock.index,
|
|
2037
|
+
partial: output
|
|
2038
|
+
});
|
|
2039
|
+
}
|
|
2040
|
+
if (currentBlock.type === "text") {
|
|
2041
|
+
currentBlock.text += choice.delta.content;
|
|
2042
|
+
stream.push({
|
|
2043
|
+
type: "text.delta",
|
|
2044
|
+
partIndex: currentBlock.index,
|
|
2045
|
+
delta: choice.delta.content,
|
|
2046
|
+
partial: output
|
|
2047
|
+
});
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
const reasoningFields = [
|
|
2051
|
+
"reasoning_content",
|
|
2052
|
+
"reasoning",
|
|
2053
|
+
"reasoning_text"
|
|
2054
|
+
];
|
|
2055
|
+
let foundReasoningField = null;
|
|
2056
|
+
for (const field of reasoningFields) {
|
|
2057
|
+
const value = choice.delta[field];
|
|
2058
|
+
if (typeof value === "string" && value.length > 0) {
|
|
2059
|
+
foundReasoningField = field;
|
|
2060
|
+
break;
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
if (foundReasoningField) {
|
|
2064
|
+
if (activeToolCalls.size > 0) flushActiveToolCalls();
|
|
2065
|
+
if (!currentBlock || currentBlock.type !== "thinking") {
|
|
2066
|
+
finishCurrentBlock(currentBlock);
|
|
2067
|
+
currentBlock = {
|
|
2068
|
+
type: "thinking",
|
|
2069
|
+
thinking: "",
|
|
2070
|
+
thinkingSignature: foundReasoningField,
|
|
2071
|
+
index: output.parts.length
|
|
2072
|
+
};
|
|
2073
|
+
output.parts.push(currentBlock);
|
|
2074
|
+
stream.push({
|
|
2075
|
+
type: "thinking.start",
|
|
2076
|
+
partIndex: currentBlock.index,
|
|
2077
|
+
partial: output
|
|
2078
|
+
});
|
|
2079
|
+
}
|
|
2080
|
+
if (currentBlock.type === "thinking") {
|
|
2081
|
+
const delta = choice.delta[foundReasoningField];
|
|
2082
|
+
if (typeof delta === "string") {
|
|
2083
|
+
currentBlock.thinking += delta;
|
|
2084
|
+
stream.push({
|
|
2085
|
+
type: "thinking.delta",
|
|
2086
|
+
partIndex: currentBlock.index,
|
|
2087
|
+
delta,
|
|
2088
|
+
partial: output
|
|
2089
|
+
});
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
if (choice.delta.tool_calls) {
|
|
2094
|
+
if (currentBlock) {
|
|
2095
|
+
finishCurrentBlock(currentBlock);
|
|
2096
|
+
currentBlock = null;
|
|
2097
|
+
}
|
|
2098
|
+
for (const toolCall of choice.delta.tool_calls) {
|
|
2099
|
+
const streamIndex = toolCall.index ?? activeToolCalls.size;
|
|
2100
|
+
let toolBlock = activeToolCalls.get(streamIndex);
|
|
2101
|
+
if (!toolBlock) {
|
|
2102
|
+
toolBlock = {
|
|
2103
|
+
type: "toolCall",
|
|
2104
|
+
callID: toolCall.id || "",
|
|
2105
|
+
name: toolCall.function?.name || "",
|
|
2106
|
+
arguments: {},
|
|
2107
|
+
partialArgs: "",
|
|
2108
|
+
index: output.parts.length,
|
|
2109
|
+
streamIndex,
|
|
2110
|
+
status: "pending",
|
|
2111
|
+
time: {
|
|
2112
|
+
start: Date.now(),
|
|
2113
|
+
end: Date.now()
|
|
2114
|
+
}
|
|
2115
|
+
};
|
|
2116
|
+
activeToolCalls.set(streamIndex, toolBlock);
|
|
2117
|
+
output.parts.push(toolBlock);
|
|
2118
|
+
stream.push({
|
|
2119
|
+
type: "toolcall.start",
|
|
2120
|
+
partIndex: toolBlock.index,
|
|
2121
|
+
partial: output
|
|
2122
|
+
});
|
|
2123
|
+
}
|
|
2124
|
+
if (toolCall.id) toolBlock.callID = toolCall.id;
|
|
2125
|
+
if (toolCall.function?.name) toolBlock.name = toolCall.function.name;
|
|
2126
|
+
let delta = "";
|
|
2127
|
+
if (toolCall.function?.arguments) {
|
|
2128
|
+
delta = toolCall.function.arguments;
|
|
2129
|
+
toolBlock.partialArgs = (toolBlock.partialArgs || "") + delta;
|
|
2130
|
+
toolBlock.arguments = parseStreamingJson(toolBlock.partialArgs);
|
|
2131
|
+
}
|
|
2132
|
+
toolBlock.time.end = Date.now();
|
|
2133
|
+
stream.push({
|
|
2134
|
+
type: "toolcall.delta",
|
|
2135
|
+
partIndex: toolBlock.index,
|
|
2136
|
+
delta,
|
|
2137
|
+
partial: output
|
|
2138
|
+
});
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
2141
|
+
const reasoningDetails = choice.delta.reasoning_details;
|
|
2142
|
+
if (reasoningDetails && Array.isArray(reasoningDetails)) {
|
|
2143
|
+
for (const detail of reasoningDetails) if (detail.type === "reasoning.encrypted" && typeof detail.id === "string" && detail.data !== void 0) {
|
|
2144
|
+
const matchingToolCall = output.parts.find((block) => block.type === "toolCall" && block.callID === detail.id);
|
|
2145
|
+
if (matchingToolCall) matchingToolCall.thoughtSignature = JSON.stringify(detail);
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
}
|
|
2149
|
+
finishCurrentBlock(currentBlock);
|
|
2150
|
+
flushActiveToolCalls();
|
|
2151
|
+
if (options?.signal?.aborted) throw new Error("Request was aborted");
|
|
2152
|
+
if (output.stopReason === "aborted") throw new Error("Request was aborted");
|
|
2153
|
+
if (output.stopReason === "error") throw new Error(output.errorMessage || "Provider returned an error stop reason");
|
|
2154
|
+
output.time.completed = Date.now();
|
|
2155
|
+
stream.push({
|
|
2156
|
+
type: "done",
|
|
2157
|
+
reason: output.stopReason,
|
|
2158
|
+
message: output
|
|
2159
|
+
});
|
|
2160
|
+
stream.end();
|
|
2161
|
+
} catch (error) {
|
|
2162
|
+
for (const block of output.parts) {
|
|
2163
|
+
delete block.index;
|
|
2164
|
+
delete block.partialArgs;
|
|
2165
|
+
delete block.streamIndex;
|
|
2166
|
+
}
|
|
2167
|
+
output.time.completed = Date.now();
|
|
2168
|
+
output.stopReason = options?.signal?.aborted ? "aborted" : "error";
|
|
2169
|
+
output.errorMessage = error instanceof Error ? error.message : JSON.stringify(error);
|
|
2170
|
+
const rawMetadata = error?.error?.metadata?.raw;
|
|
2171
|
+
if (rawMetadata) output.errorMessage += `\n${rawMetadata}`;
|
|
2172
|
+
stream.push({
|
|
2173
|
+
type: "error",
|
|
2174
|
+
reason: output.stopReason,
|
|
2175
|
+
error: output
|
|
2176
|
+
});
|
|
2177
|
+
stream.end();
|
|
2178
|
+
}
|
|
2179
|
+
})();
|
|
2180
|
+
return stream;
|
|
2181
|
+
};
|
|
2182
|
+
const streamSimpleOpenAICompletions = (model, context, options) => {
|
|
2183
|
+
const apiKey = options?.apiKey || /* @__PURE__ */ getEnvApiKey(model.provider);
|
|
2184
|
+
if (!apiKey) throw new Error(`No API key for provider: ${model.provider.id}`);
|
|
2185
|
+
const base = Stream.buildBaseOptions(model, options, apiKey);
|
|
2186
|
+
const simpleReasoning = Model.supportsXhigh(model) ? options?.reasoning : Stream.clampReasoning(options?.reasoning);
|
|
2187
|
+
const reasoningEffort = simpleReasoning && simpleReasoning !== Stream.ThinkingLevelEnum.off ? simpleReasoning : void 0;
|
|
2188
|
+
const toolChoice = options?.toolChoice;
|
|
2189
|
+
return streamOpenAICompletions(model, context, {
|
|
2190
|
+
...base,
|
|
2191
|
+
reasoningEffort,
|
|
2192
|
+
toolChoice
|
|
2193
|
+
});
|
|
2194
|
+
};
|
|
2195
|
+
function buildParams(model, context, options) {
|
|
2196
|
+
const compat = getCompat(model);
|
|
2197
|
+
const messages = convertMessages(model, context, compat);
|
|
2198
|
+
maybeAddOpenRouterAnthropicCacheControl(model, messages);
|
|
2199
|
+
const baseUrl = resolveBaseUrl(model);
|
|
2200
|
+
const params = {
|
|
2201
|
+
model: model.id,
|
|
2202
|
+
messages,
|
|
2203
|
+
stream: true
|
|
2204
|
+
};
|
|
2205
|
+
if (compat.supportsUsageInStreaming !== false) params.stream_options = { include_usage: true };
|
|
2206
|
+
if (compat.supportsStore) params.store = false;
|
|
2207
|
+
if (options?.maxTokens) if (compat.maxTokensField === "max_tokens") params.max_tokens = options.maxTokens;
|
|
2208
|
+
else params.max_completion_tokens = options.maxTokens;
|
|
2209
|
+
if (options?.temperature !== void 0) params.temperature = options.temperature;
|
|
2210
|
+
if (context.tools) {
|
|
2211
|
+
params.tools = convertTools(context.tools, compat);
|
|
2212
|
+
if (compat.zaiToolStream) params.tool_stream = true;
|
|
2213
|
+
} else if (hasToolHistory(context.messages)) params.tools = [];
|
|
2214
|
+
if (options?.toolChoice) params.tool_choice = options.toolChoice;
|
|
2215
|
+
if ((compat.thinkingFormat === "zai" || compat.thinkingFormat === "qwen") && model.reasoning) params.enable_thinking = !!options?.reasoningEffort;
|
|
2216
|
+
else if (compat.thinkingFormat === "qwen-chat-template" && model.reasoning) params.chat_template_kwargs = { enable_thinking: !!options?.reasoningEffort };
|
|
2217
|
+
else if (compat.thinkingFormat === "openrouter" && model.reasoning) {
|
|
2218
|
+
const openRouterParams = params;
|
|
2219
|
+
if (options?.reasoningEffort) openRouterParams.reasoning = { effort: mapReasoningEffort(options.reasoningEffort, compat.reasoningEffortMap) };
|
|
2220
|
+
else openRouterParams.reasoning = { effort: "none" };
|
|
2221
|
+
} else if (options?.reasoningEffort && model.reasoning && compat.supportsReasoningEffort) params.reasoning_effort = mapReasoningEffort(options.reasoningEffort, compat.reasoningEffortMap);
|
|
2222
|
+
if (baseUrl.includes("openrouter.ai") && model.compat?.openRouterRouting) params.provider = model.compat.openRouterRouting;
|
|
2223
|
+
if (baseUrl.includes("ai-gateway.vercel.sh") && model.compat?.vercelGatewayRouting) {
|
|
2224
|
+
const routing = model.compat.vercelGatewayRouting;
|
|
2225
|
+
if (routing.only || routing.order) {
|
|
2226
|
+
const gatewayOptions = {};
|
|
2227
|
+
if (routing.only) gatewayOptions.only = routing.only;
|
|
2228
|
+
if (routing.order) gatewayOptions.order = routing.order;
|
|
2229
|
+
params.providerOptions = { gateway: gatewayOptions };
|
|
2230
|
+
}
|
|
2231
|
+
}
|
|
2232
|
+
return params;
|
|
2233
|
+
}
|
|
2234
|
+
function createClient(model, context, apiKey, optsHeaders) {
|
|
2235
|
+
const baseUrl = resolveBaseUrl(model);
|
|
2236
|
+
if (!apiKey) {
|
|
2237
|
+
if (!process.env.OPENAI_API_KEY) throw new Error("OpenAI API key is required. set OPENAI_API_KEY environment variable or pass it as an argument");
|
|
2238
|
+
apiKey = process.env.OPENAI_API_KEY;
|
|
2239
|
+
}
|
|
2240
|
+
const headers = { ...model.headers };
|
|
2241
|
+
if (model.provider.id === Provider.KnownProviderEnum.githubCopilot) {
|
|
2242
|
+
const hasImages = hasCopilotVisionInput(context.messages);
|
|
2243
|
+
const copilotHeaders = buildCopilotDynamicHeaders({
|
|
2244
|
+
messages: context.messages,
|
|
2245
|
+
hasImages
|
|
2246
|
+
});
|
|
2247
|
+
Object.assign(headers, copilotHeaders);
|
|
2248
|
+
}
|
|
2249
|
+
if (optsHeaders) Object.assign(headers, optsHeaders);
|
|
2250
|
+
return new OpenAI({
|
|
2251
|
+
apiKey,
|
|
2252
|
+
baseURL: baseUrl,
|
|
2253
|
+
dangerouslyAllowBrowser: true,
|
|
2254
|
+
defaultHeaders: headers
|
|
2255
|
+
});
|
|
2256
|
+
}
|
|
2257
|
+
function mapReasoningEffort(effort, reasoningEffortMap) {
|
|
2258
|
+
return reasoningEffortMap[effort] ?? effort;
|
|
2259
|
+
}
|
|
2260
|
+
function maybeAddOpenRouterAnthropicCacheControl(model, messages) {
|
|
2261
|
+
if (model.provider.id !== Provider.KnownProviderEnum.openrouter || !model.id.startsWith("anthropic/")) return;
|
|
2262
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
2263
|
+
const msg = messages[i];
|
|
2264
|
+
if (!msg) continue;
|
|
2265
|
+
if (msg.role !== "user" && msg.role !== "assistant") continue;
|
|
2266
|
+
const content = msg.content;
|
|
2267
|
+
if (typeof content === "string") {
|
|
2268
|
+
msg.content = [Object.assign({
|
|
2269
|
+
type: "text",
|
|
2270
|
+
text: content
|
|
2271
|
+
}, { cache_control: { type: "ephemeral" } })];
|
|
2272
|
+
return;
|
|
2273
|
+
}
|
|
2274
|
+
if (!Array.isArray(content)) continue;
|
|
2275
|
+
for (let j = content.length - 1; j >= 0; j--) {
|
|
2276
|
+
const part = content[j];
|
|
2277
|
+
if (part?.type === "text") {
|
|
2278
|
+
Object.assign(part, { cache_control: { type: "ephemeral" } });
|
|
2279
|
+
return;
|
|
2280
|
+
}
|
|
2281
|
+
}
|
|
2282
|
+
}
|
|
2283
|
+
}
|
|
2284
|
+
function convertMessages(model, context, compat) {
|
|
2285
|
+
const params = [];
|
|
2286
|
+
const normalizeToolCallId = (id) => {
|
|
2287
|
+
if (id.includes("|")) return (id.split("|")[0] ?? id).replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 40);
|
|
2288
|
+
if (model.provider.id === Provider.KnownProviderEnum.openai) return id.length > 40 ? id.slice(0, 40) : id;
|
|
2289
|
+
return id;
|
|
2290
|
+
};
|
|
2291
|
+
const transformedMessages = Message.transformMessages(context.messages, model, normalizeToolCallId);
|
|
2292
|
+
if (context.systemPrompt) {
|
|
2293
|
+
const role = model.reasoning && compat.supportsDeveloperRole ? "developer" : "system";
|
|
2294
|
+
params.push({
|
|
2295
|
+
role,
|
|
2296
|
+
content: sanitizeSurrogates(context.systemPrompt)
|
|
2297
|
+
});
|
|
2298
|
+
}
|
|
2299
|
+
let lastRole = null;
|
|
2300
|
+
for (const msg of transformedMessages) {
|
|
2301
|
+
if (compat.requiresAssistantAfterToolResult && lastRole === "tool" && msg.role === "user") {
|
|
2302
|
+
params.push({
|
|
2303
|
+
role: "assistant",
|
|
2304
|
+
content: "I have processed the tool results."
|
|
2305
|
+
});
|
|
2306
|
+
lastRole = "assistant";
|
|
2307
|
+
}
|
|
2308
|
+
if (msg.role === "user") {
|
|
2309
|
+
const content = msg.parts.flatMap((item) => {
|
|
2310
|
+
if (item.type === "text") {
|
|
2311
|
+
const text = sanitizeSurrogates(item.text);
|
|
2312
|
+
if (text.trim().length === 0) return [];
|
|
2313
|
+
return [{
|
|
2314
|
+
type: "text",
|
|
2315
|
+
text
|
|
2316
|
+
}];
|
|
2317
|
+
}
|
|
2318
|
+
if (!model.input.includes("image")) return [];
|
|
2319
|
+
return [{
|
|
2320
|
+
type: "image_url",
|
|
2321
|
+
image_url: { url: `data:${item.mimeType};base64,${item.data}` }
|
|
2322
|
+
}];
|
|
2323
|
+
});
|
|
2324
|
+
if (content.length === 0) continue;
|
|
2325
|
+
params.push({
|
|
2326
|
+
role: "user",
|
|
2327
|
+
content
|
|
2328
|
+
});
|
|
2329
|
+
lastRole = "user";
|
|
2330
|
+
continue;
|
|
2331
|
+
}
|
|
2332
|
+
const assistantMsg = {
|
|
2333
|
+
role: "assistant",
|
|
2334
|
+
content: compat.requiresAssistantAfterToolResult ? "" : null
|
|
2335
|
+
};
|
|
2336
|
+
const textSegments = msg.parts.filter((block) => block.type === "text").map((block) => sanitizeSurrogates(block.text)).filter((text) => text.trim().length > 0);
|
|
2337
|
+
const nonEmptyThinkingBlocks = msg.parts.filter((block) => block.type === "thinking").filter((block) => block.thinking.trim().length > 0);
|
|
2338
|
+
if (compat.requiresThinkingAsText && nonEmptyThinkingBlocks.length > 0) textSegments.unshift(...nonEmptyThinkingBlocks.map((block) => sanitizeSurrogates(block.thinking)));
|
|
2339
|
+
else if (nonEmptyThinkingBlocks.length > 0) {
|
|
2340
|
+
const signature = nonEmptyThinkingBlocks[0]?.thinkingSignature;
|
|
2341
|
+
if (signature && signature.length > 0) assistantMsg[signature] = nonEmptyThinkingBlocks.map((block) => sanitizeSurrogates(block.thinking)).join("\n");
|
|
2342
|
+
}
|
|
2343
|
+
if (textSegments.length > 0) assistantMsg.content = textSegments.join("");
|
|
2344
|
+
const toolCalls = msg.parts.filter((block) => block.type === "toolCall");
|
|
2345
|
+
if (toolCalls.length > 0) {
|
|
2346
|
+
assistantMsg.tool_calls = toolCalls.map((toolCall) => ({
|
|
2347
|
+
id: toolCall.callID,
|
|
2348
|
+
type: "function",
|
|
2349
|
+
function: {
|
|
2350
|
+
name: toolCall.name,
|
|
2351
|
+
arguments: JSON.stringify(toolCall.arguments)
|
|
2352
|
+
}
|
|
2353
|
+
}));
|
|
2354
|
+
const reasoningDetails = toolCalls.filter((toolCall) => toolCall.thoughtSignature).map((toolCall) => {
|
|
2355
|
+
try {
|
|
2356
|
+
return JSON.parse(toolCall.thoughtSignature);
|
|
2357
|
+
} catch {
|
|
2358
|
+
return null;
|
|
2359
|
+
}
|
|
2360
|
+
}).filter(Boolean);
|
|
2361
|
+
if (reasoningDetails.length > 0) assistantMsg.reasoning_details = reasoningDetails;
|
|
2362
|
+
}
|
|
2363
|
+
const assistantContent = assistantMsg.content;
|
|
2364
|
+
if (!(assistantContent !== null && assistantContent !== void 0 && (typeof assistantContent === "string" ? assistantContent.length > 0 : assistantContent.length > 0)) && !assistantMsg.tool_calls) continue;
|
|
2365
|
+
params.push(assistantMsg);
|
|
2366
|
+
lastRole = "assistant";
|
|
2367
|
+
const imageBlocks = [];
|
|
2368
|
+
for (const toolCall of toolCalls) {
|
|
2369
|
+
if (toolCall.status === "pending" || toolCall.status === "running") {
|
|
2370
|
+
params.push(buildSyntheticToolResult(toolCall, compat));
|
|
2371
|
+
lastRole = "tool";
|
|
2372
|
+
continue;
|
|
2373
|
+
}
|
|
2374
|
+
const textResult = toolCall.result.content.filter((content) => content.type === "text").map((content) => content.text).join("\n");
|
|
2375
|
+
const hasImages = toolCall.result.content.some((content) => content.type === "image");
|
|
2376
|
+
const toolResultMsg = {
|
|
2377
|
+
role: "tool",
|
|
2378
|
+
content: sanitizeSurrogates(textResult.length > 0 ? textResult : "(see attached image)"),
|
|
2379
|
+
tool_call_id: toolCall.callID
|
|
2380
|
+
};
|
|
2381
|
+
if (compat.requiresToolResultName) toolResultMsg.name = toolCall.name;
|
|
2382
|
+
params.push(toolResultMsg);
|
|
2383
|
+
lastRole = "tool";
|
|
2384
|
+
if (hasImages && model.input.includes("image")) {
|
|
2385
|
+
for (const block of toolCall.result.content) if (block.type === "image") imageBlocks.push({
|
|
2386
|
+
type: "image_url",
|
|
2387
|
+
image_url: { url: `data:${block.mimeType};base64,${block.data}` }
|
|
2388
|
+
});
|
|
2389
|
+
}
|
|
2390
|
+
}
|
|
2391
|
+
if (imageBlocks.length > 0) {
|
|
2392
|
+
if (compat.requiresAssistantAfterToolResult) params.push({
|
|
2393
|
+
role: "assistant",
|
|
2394
|
+
content: "I have processed the tool results."
|
|
2395
|
+
});
|
|
2396
|
+
params.push({
|
|
2397
|
+
role: "user",
|
|
2398
|
+
content: [{
|
|
2399
|
+
type: "text",
|
|
2400
|
+
text: "Attached image(s) from tool result:"
|
|
2401
|
+
}, ...imageBlocks]
|
|
2402
|
+
});
|
|
2403
|
+
lastRole = "user";
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2406
|
+
return params;
|
|
2407
|
+
}
|
|
2408
|
+
function convertTools(tools, compat) {
|
|
2409
|
+
return tools.map((tool) => ({
|
|
2410
|
+
type: "function",
|
|
2411
|
+
function: {
|
|
2412
|
+
name: tool.name,
|
|
2413
|
+
description: tool.description,
|
|
2414
|
+
parameters: tool.parameters,
|
|
2415
|
+
...compat.supportsStrictMode !== false && { strict: false }
|
|
2416
|
+
}
|
|
2417
|
+
}));
|
|
2418
|
+
}
|
|
2419
|
+
function parseChunkUsage(rawUsage, model) {
|
|
2420
|
+
const promptTokens = rawUsage.prompt_tokens || 0;
|
|
2421
|
+
const reportedCachedTokens = rawUsage.prompt_tokens_details?.cached_tokens || 0;
|
|
2422
|
+
const cacheWriteTokens = rawUsage.prompt_tokens_details?.cache_write_tokens || 0;
|
|
2423
|
+
const reasoningTokens = rawUsage.completion_tokens_details?.reasoning_tokens || 0;
|
|
2424
|
+
const cacheReadTokens = cacheWriteTokens > 0 ? Math.max(0, reportedCachedTokens - cacheWriteTokens) : reportedCachedTokens;
|
|
2425
|
+
const input = Math.max(0, promptTokens - cacheReadTokens - cacheWriteTokens);
|
|
2426
|
+
const outputTokens = (rawUsage.completion_tokens || 0) + reasoningTokens;
|
|
2427
|
+
const usage = {
|
|
2428
|
+
input,
|
|
2429
|
+
output: outputTokens,
|
|
2430
|
+
cacheRead: cacheReadTokens,
|
|
2431
|
+
cacheWrite: cacheWriteTokens,
|
|
2432
|
+
totalTokens: input + outputTokens + cacheReadTokens + cacheWriteTokens,
|
|
2433
|
+
cost: {
|
|
2434
|
+
input: 0,
|
|
2435
|
+
output: 0,
|
|
2436
|
+
cacheRead: 0,
|
|
2437
|
+
cacheWrite: 0,
|
|
2438
|
+
total: 0
|
|
2439
|
+
}
|
|
2440
|
+
};
|
|
2441
|
+
Model.calculateCost(model, usage);
|
|
2442
|
+
return usage;
|
|
2443
|
+
}
|
|
2444
|
+
function mapStopReason(reason) {
|
|
2445
|
+
if (reason === null) return { stopReason: "stop" };
|
|
2446
|
+
switch (reason) {
|
|
2447
|
+
case "stop":
|
|
2448
|
+
case "end": return { stopReason: "stop" };
|
|
2449
|
+
case "length": return { stopReason: "length" };
|
|
2450
|
+
case "function_call":
|
|
2451
|
+
case "tool_calls": return { stopReason: "toolUse" };
|
|
2452
|
+
case "content_filter": return {
|
|
2453
|
+
stopReason: "error",
|
|
2454
|
+
errorMessage: "Provider finish_reason: content_filter"
|
|
2455
|
+
};
|
|
2456
|
+
case "network_error": return {
|
|
2457
|
+
stopReason: "error",
|
|
2458
|
+
errorMessage: "Provider finish_reason: network_error"
|
|
2459
|
+
};
|
|
2460
|
+
default: return {
|
|
2461
|
+
stopReason: "error",
|
|
2462
|
+
errorMessage: `Provider finish_reason: ${reason}`
|
|
2463
|
+
};
|
|
2464
|
+
}
|
|
2465
|
+
}
|
|
2466
|
+
function detectCompat(model) {
|
|
2467
|
+
const provider = model.provider.id;
|
|
2468
|
+
const baseUrl = resolveBaseUrl(model);
|
|
2469
|
+
const isZai = provider === "zai" || baseUrl.includes("api.z.ai");
|
|
2470
|
+
const isNonStandard = provider === "cerebras" || baseUrl.includes("cerebras.ai") || provider === "xai" || baseUrl.includes("api.x.ai") || baseUrl.includes("chutes.ai") || baseUrl.includes("deepseek.com") || isZai || provider === "opencode" || baseUrl.includes("opencode.ai");
|
|
2471
|
+
const useMaxTokens = baseUrl.includes("chutes.ai");
|
|
2472
|
+
const isGrok = provider === "xai" || baseUrl.includes("api.x.ai");
|
|
2473
|
+
const reasoningEffortMap = (provider === "groq" || baseUrl.includes("groq.com")) && model.id === "qwen/qwen3-32b" ? {
|
|
2474
|
+
minimal: "default",
|
|
2475
|
+
low: "default",
|
|
2476
|
+
medium: "default",
|
|
2477
|
+
high: "default",
|
|
2478
|
+
xhigh: "default"
|
|
2479
|
+
} : {};
|
|
2480
|
+
return {
|
|
2481
|
+
supportsStore: !isNonStandard,
|
|
2482
|
+
supportsDeveloperRole: !isNonStandard,
|
|
2483
|
+
supportsReasoningEffort: !isGrok && !isZai,
|
|
2484
|
+
reasoningEffortMap,
|
|
2485
|
+
supportsUsageInStreaming: true,
|
|
2486
|
+
maxTokensField: useMaxTokens ? "max_tokens" : "max_completion_tokens",
|
|
2487
|
+
requiresToolResultName: false,
|
|
2488
|
+
requiresAssistantAfterToolResult: false,
|
|
2489
|
+
requiresThinkingAsText: false,
|
|
2490
|
+
thinkingFormat: isZai ? "zai" : provider === "openrouter" || baseUrl.includes("openrouter.ai") ? "openrouter" : "openai",
|
|
2491
|
+
openRouterRouting: {},
|
|
2492
|
+
vercelGatewayRouting: {},
|
|
2493
|
+
zaiToolStream: false,
|
|
2494
|
+
supportsStrictMode: true
|
|
2495
|
+
};
|
|
2496
|
+
}
|
|
2497
|
+
/**
|
|
2498
|
+
* Get resolved compatibility settings for a model.
|
|
2499
|
+
* Uses explicit model.compat if provided, otherwise auto-detects from provider/URL.
|
|
2500
|
+
*/
|
|
2501
|
+
function getCompat(model) {
|
|
2502
|
+
const detected = detectCompat(model);
|
|
2503
|
+
if (!model.compat) return detected;
|
|
2504
|
+
return {
|
|
2505
|
+
supportsStore: model.compat.supportsStore ?? detected.supportsStore,
|
|
2506
|
+
supportsDeveloperRole: model.compat.supportsDeveloperRole ?? detected.supportsDeveloperRole,
|
|
2507
|
+
supportsReasoningEffort: model.compat.supportsReasoningEffort ?? detected.supportsReasoningEffort,
|
|
2508
|
+
reasoningEffortMap: model.compat.reasoningEffortMap ?? detected.reasoningEffortMap,
|
|
2509
|
+
supportsUsageInStreaming: model.compat.supportsUsageInStreaming ?? detected.supportsUsageInStreaming,
|
|
2510
|
+
maxTokensField: model.compat.maxTokensField ?? detected.maxTokensField,
|
|
2511
|
+
requiresToolResultName: model.compat.requiresToolResultName ?? detected.requiresToolResultName,
|
|
2512
|
+
requiresAssistantAfterToolResult: model.compat.requiresAssistantAfterToolResult ?? detected.requiresAssistantAfterToolResult,
|
|
2513
|
+
requiresThinkingAsText: model.compat.requiresThinkingAsText ?? detected.requiresThinkingAsText,
|
|
2514
|
+
thinkingFormat: model.compat.thinkingFormat ?? detected.thinkingFormat,
|
|
2515
|
+
openRouterRouting: model.compat.openRouterRouting ?? {},
|
|
2516
|
+
vercelGatewayRouting: model.compat.vercelGatewayRouting ?? detected.vercelGatewayRouting,
|
|
2517
|
+
zaiToolStream: model.compat.zaiToolStream ?? detected.zaiToolStream,
|
|
2518
|
+
supportsStrictMode: model.compat.supportsStrictMode ?? detected.supportsStrictMode
|
|
2519
|
+
};
|
|
2520
|
+
}
|
|
2521
|
+
const openAICompletionsProtocolProvider = {
|
|
2522
|
+
protocol: Model.KnownProtocolEnum.openaiCompletions,
|
|
2523
|
+
stream: streamOpenAICompletions,
|
|
2524
|
+
streamSimple: streamSimpleOpenAICompletions
|
|
2525
|
+
};
|
|
2526
|
+
//#endregion
|
|
2527
|
+
//#region src/provider/register.ts
|
|
2528
|
+
Stream.registerProtocolProvider(anthropicProtocolProvider, "providers/anthropic/index.ts");
|
|
2529
|
+
Stream.registerProtocolProvider(openAICompletionsProtocolProvider, "providers/openai/completions.ts");
|
|
1843
2530
|
//#endregion
|
|
1844
2531
|
//#region src/agent/agent.ts
|
|
1845
2532
|
let Agent;
|
|
@@ -2656,7 +3343,7 @@ let CodeMode;
|
|
|
2656
3343
|
})(CodeMode || (CodeMode = {}));
|
|
2657
3344
|
//#endregion
|
|
2658
3345
|
//#region src/llm.ts
|
|
2659
|
-
const llmImpl = async (provider, model) => Model.getModel(provider, model);
|
|
3346
|
+
const llmImpl = async (provider, model, overrides) => Model.getModel(provider, model, overrides);
|
|
2660
3347
|
const llm = Object.assign(llmImpl, {
|
|
2661
3348
|
model: Model.getModel,
|
|
2662
3349
|
models: Model.getModels,
|