@codeworksh/aikit 0.2.1 → 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 +7 -0
- package/agent/codemode/drivers/drivers.mjs +122 -0
- package/agent/codemode/drivers/drivers.mjs.map +1 -0
- package/codemode-SiETGR3z.d.mts +8410 -0
- package/index.d.mts +4 -8846
- package/index.mjs +2513 -1474
- package/index.mjs.map +1 -1
- package/package.json +12 -6
package/index.mjs
CHANGED
|
@@ -1,19 +1,38 @@
|
|
|
1
1
|
import { Filesystem, NamedError, lazy } from "@codeworksh/utils";
|
|
2
|
-
import { Type } from "@sinclair/typebox";
|
|
2
|
+
import { Type, TypeGuard } from "@sinclair/typebox";
|
|
3
|
+
import { capitalize, keys, mapValues, pick, pipe } from "remeda";
|
|
4
|
+
import Ajv from "ajv";
|
|
5
|
+
import addFormats from "ajv-formats";
|
|
3
6
|
import { uuidv7 } from "uuidv7";
|
|
4
|
-
import { keys, mapValues, pick, pipe } from "remeda";
|
|
5
7
|
import Anthropic from "@anthropic-ai/sdk";
|
|
6
8
|
import { parse } from "partial-json";
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
+
import OpenAI from "openai";
|
|
10
|
+
import { transform } from "esbuild";
|
|
9
11
|
//#region src/provider/provider.ts
|
|
10
12
|
let Provider;
|
|
11
13
|
(function(_Provider) {
|
|
12
14
|
const KnownProviderEnum = _Provider.KnownProviderEnum = {
|
|
13
15
|
anthropic: "anthropic",
|
|
14
|
-
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"
|
|
15
24
|
};
|
|
16
|
-
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
|
+
]);
|
|
17
36
|
_Provider.Info = Type.Object({
|
|
18
37
|
id: KnownProviderSchema,
|
|
19
38
|
name: Type.String(),
|
|
@@ -75,6 +94,46 @@ let ModelCatalog;
|
|
|
75
94
|
}, 60 * 1e3 * 60).unref();
|
|
76
95
|
})(ModelCatalog || (ModelCatalog = {}));
|
|
77
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
|
|
78
137
|
//#region src/model/model.ts
|
|
79
138
|
let Model;
|
|
80
139
|
(function(_Model) {
|
|
@@ -96,6 +155,11 @@ let Model;
|
|
|
96
155
|
cacheWrite: Type.Number()
|
|
97
156
|
});
|
|
98
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
|
+
});
|
|
99
163
|
const BaseSchema = _Model.BaseSchema = Type.Object({
|
|
100
164
|
id: Type.String(),
|
|
101
165
|
name: Type.String(),
|
|
@@ -106,7 +170,8 @@ let Model;
|
|
|
106
170
|
cost: CostSchema,
|
|
107
171
|
contextWindow: Type.Number(),
|
|
108
172
|
maxTokens: Type.Number(),
|
|
109
|
-
headers: HeadersSchema
|
|
173
|
+
headers: HeadersSchema,
|
|
174
|
+
supportedProtocols: Type.Optional(Type.Partial(SupportedProtocolsSchema))
|
|
110
175
|
});
|
|
111
176
|
const OpenAICompletionsCompatSchema = _Model.OpenAICompletionsCompatSchema = Type.Object({
|
|
112
177
|
supportsStore: Type.Optional(Type.Boolean()),
|
|
@@ -127,6 +192,7 @@ let Model;
|
|
|
127
192
|
])),
|
|
128
193
|
openRouterRouting: Type.Optional(Provider.OpenRouterRoutingSchema),
|
|
129
194
|
vercelGatewayRouting: Type.Optional(Provider.VercelGatewayRoutingSchema),
|
|
195
|
+
zaiToolStream: Type.Optional(Type.Boolean()),
|
|
130
196
|
supportsStrictMode: Type.Optional(Type.Boolean())
|
|
131
197
|
});
|
|
132
198
|
const OpenAIResponsesCompatSchema = _Model.OpenAIResponsesCompatSchema = Type.Object({});
|
|
@@ -140,7 +206,7 @@ let Model;
|
|
|
140
206
|
protocol: Type.Literal(KnownProtocolEnum.openaiResponses),
|
|
141
207
|
compat: Type.Optional(OpenAIResponsesCompatSchema)
|
|
142
208
|
})]);
|
|
143
|
-
_Model.
|
|
209
|
+
_Model.Info = Type.Union([
|
|
144
210
|
AnthropicSchema,
|
|
145
211
|
OpenAICompletionsSchema,
|
|
146
212
|
OpenAIResponsesSchema
|
|
@@ -154,18 +220,13 @@ let Model;
|
|
|
154
220
|
usage.cost.total = usage.cost.input + usage.cost.output + usage.cost.cacheRead + usage.cost.cacheWrite;
|
|
155
221
|
}
|
|
156
222
|
_Model.calculateCost = calculateCost;
|
|
157
|
-
function resolveProtocol(providerId) {
|
|
158
|
-
switch (providerId) {
|
|
159
|
-
case Provider.KnownProviderEnum.anthropic: return KnownProtocolEnum.anthropicMessages;
|
|
160
|
-
case Provider.KnownProviderEnum.openai: return KnownProtocolEnum.openaiResponses;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
223
|
function normalizeInput(input) {
|
|
164
224
|
const normalized = /* @__PURE__ */ new Set();
|
|
165
225
|
for (const modality of input ?? ["text"]) if (modality === "text" || modality === "image") normalized.add(modality);
|
|
166
226
|
if (normalized.size === 0) normalized.add("text");
|
|
167
227
|
return [...normalized];
|
|
168
228
|
}
|
|
229
|
+
_Model.normalizeInput = normalizeInput;
|
|
169
230
|
function toProviderInfo(providerId, provider) {
|
|
170
231
|
return {
|
|
171
232
|
id: providerId,
|
|
@@ -175,26 +236,9 @@ let Model;
|
|
|
175
236
|
options: provider.headers
|
|
176
237
|
};
|
|
177
238
|
}
|
|
239
|
+
_Model.toProviderInfo = toProviderInfo;
|
|
178
240
|
function toModelValue(providerId, provider, model) {
|
|
179
|
-
|
|
180
|
-
return {
|
|
181
|
-
id: model.id,
|
|
182
|
-
name: model.name,
|
|
183
|
-
provider: toProviderInfo(providerId, provider),
|
|
184
|
-
baseUrl,
|
|
185
|
-
reasoning: Boolean(model.reasoning),
|
|
186
|
-
input: normalizeInput(model.modalities.input),
|
|
187
|
-
cost: {
|
|
188
|
-
input: model.cost?.input ?? 0,
|
|
189
|
-
output: model.cost?.output ?? 0,
|
|
190
|
-
cacheRead: model.cost?.cache_read ?? 0,
|
|
191
|
-
cacheWrite: model.cost?.cache_write ?? 0
|
|
192
|
-
},
|
|
193
|
-
contextWindow: model.limit?.context ?? 0,
|
|
194
|
-
maxTokens: model.limit?.output ?? 0,
|
|
195
|
-
headers: model.headers ?? provider.headers,
|
|
196
|
-
protocol: resolveProtocol(providerId)
|
|
197
|
-
};
|
|
241
|
+
return applyModification(providerId, provider, model);
|
|
198
242
|
}
|
|
199
243
|
async function getBuiltInModels() {
|
|
200
244
|
return pipe(await ModelCatalog.get(), pick(BUILTINS), mapValues((provider, providerId) => mapValues(provider.models, (model) => toModelValue(providerId, provider, model))));
|
|
@@ -210,8 +254,13 @@ let Model;
|
|
|
210
254
|
}
|
|
211
255
|
return registry;
|
|
212
256
|
});
|
|
213
|
-
async function getModel(provider, model) {
|
|
214
|
-
|
|
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;
|
|
215
264
|
}
|
|
216
265
|
_Model.getModel = getModel;
|
|
217
266
|
async function getProviders() {
|
|
@@ -240,237 +289,171 @@ let Model;
|
|
|
240
289
|
_Model.modelsAreEqual = modelsAreEqual;
|
|
241
290
|
})(Model || (Model = {}));
|
|
242
291
|
//#endregion
|
|
243
|
-
//#region src/
|
|
244
|
-
let
|
|
245
|
-
(function(
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
});
|
|
251
|
-
const
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
thinking: Type.String(),
|
|
259
|
-
thinkingSignature: Type.Optional(Type.String()),
|
|
260
|
-
redacted: Type.Optional(Type.Boolean())
|
|
261
|
-
});
|
|
262
|
-
const ToolCallBaseSchema = _Message.ToolCallBaseSchema = Type.Object({
|
|
263
|
-
type: Type.Literal("toolCall"),
|
|
264
|
-
callID: Type.String(),
|
|
265
|
-
name: Type.String(),
|
|
266
|
-
arguments: Type.Record(Type.String(), Type.Any()),
|
|
267
|
-
thoughtSignature: Type.Optional(Type.String()),
|
|
268
|
-
time: Type.Object({
|
|
269
|
-
start: Type.Number(),
|
|
270
|
-
end: Type.Number()
|
|
271
|
-
})
|
|
272
|
-
});
|
|
273
|
-
const ToolSuccessResult = _Message.ToolSuccessResult = Type.Object({
|
|
274
|
-
content: Type.Array(Type.Union([TextContentSchema, ImageContentSchema])),
|
|
275
|
-
details: Type.Optional(Type.Any()),
|
|
276
|
-
isError: Type.Literal(false)
|
|
277
|
-
});
|
|
278
|
-
const ToolErrorResult = _Message.ToolErrorResult = Type.Object({
|
|
279
|
-
content: Type.Array(Type.Union([TextContentSchema, ImageContentSchema])),
|
|
280
|
-
details: Type.Optional(Type.Any()),
|
|
281
|
-
isError: Type.Literal(true)
|
|
282
|
-
});
|
|
283
|
-
const ToolRunningPartial = _Message.ToolRunningPartial = Type.Object({
|
|
284
|
-
content: Type.Optional(Type.Array(Type.Union([TextContentSchema, ImageContentSchema]))),
|
|
285
|
-
details: Type.Optional(Type.Any())
|
|
286
|
-
});
|
|
287
|
-
const ToolStatusEnum = _Message.ToolStatusEnum = {
|
|
288
|
-
pending: "pending",
|
|
289
|
-
running: "running",
|
|
290
|
-
completed: "completed",
|
|
291
|
-
error: "error",
|
|
292
|
-
skipped: "skipped",
|
|
293
|
-
aborted: "aborted"
|
|
292
|
+
//#region src/provider/stream.ts
|
|
293
|
+
let Stream;
|
|
294
|
+
(function(_Stream) {
|
|
295
|
+
const ProtocolProviderNotFoundError = _Stream.ProtocolProviderNotFoundError = NamedError.create("ProtocolProviderNotFoundError", Type.Object({ protocol: Model.KnownProtocolSchema }));
|
|
296
|
+
const ProtocolMismatchError = _Stream.ProtocolMismatchError = NamedError.create("ProtocolMismatchError", Type.Object({
|
|
297
|
+
actual: Model.KnownProtocolSchema,
|
|
298
|
+
expected: Model.KnownProtocolSchema
|
|
299
|
+
}));
|
|
300
|
+
const ThinkingLevelEnum = _Stream.ThinkingLevelEnum = {
|
|
301
|
+
off: "off",
|
|
302
|
+
minimal: "minimal",
|
|
303
|
+
low: "low",
|
|
304
|
+
medium: "medium",
|
|
305
|
+
high: "high",
|
|
306
|
+
xhigh: "xhigh"
|
|
294
307
|
};
|
|
295
|
-
const
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
});
|
|
303
|
-
const ToolErrorSchema = _Message.ToolErrorSchema = Type.Object({
|
|
304
|
-
status: Type.Literal(ToolStatusEnum.error),
|
|
305
|
-
result: ToolErrorResult
|
|
306
|
-
});
|
|
307
|
-
const ToolSkippedSchema = _Message.ToolSkippedSchema = Type.Object({
|
|
308
|
-
status: Type.Literal(ToolStatusEnum.skipped),
|
|
309
|
-
result: ToolErrorResult
|
|
310
|
-
});
|
|
311
|
-
const ToolAbortedSchema = _Message.ToolAbortedSchema = Type.Object({
|
|
312
|
-
status: Type.Literal(ToolStatusEnum.aborted),
|
|
313
|
-
result: ToolErrorResult
|
|
314
|
-
});
|
|
315
|
-
const ToolCallPendingPart = Type.Intersect([ToolCallBaseSchema, Type.Object({ status: Type.Literal(ToolStatusEnum.pending) })]);
|
|
316
|
-
const ToolCallRunningPart = Type.Composite([ToolCallBaseSchema, ToolRunningSchema]);
|
|
317
|
-
const ToolCallCompletedPart = Type.Composite([ToolCallBaseSchema, ToolCompletedSchema]);
|
|
318
|
-
const ToolCallErrorPart = Type.Composite([ToolCallBaseSchema, ToolErrorSchema]);
|
|
319
|
-
const ToolCallSkippedPart = Type.Composite([ToolCallBaseSchema, ToolSkippedSchema]);
|
|
320
|
-
const ToolCallAbortedPart = Type.Composite([ToolCallBaseSchema, ToolAbortedSchema]);
|
|
321
|
-
const ToolCallSchema = _Message.ToolCallSchema = Type.Union([
|
|
322
|
-
ToolCallPendingPart,
|
|
323
|
-
ToolCallRunningPart,
|
|
324
|
-
ToolCallCompletedPart,
|
|
325
|
-
ToolCallErrorPart,
|
|
326
|
-
ToolCallSkippedPart,
|
|
327
|
-
ToolCallAbortedPart
|
|
308
|
+
const ThinkingLevelSchema = _Stream.ThinkingLevelSchema = Type.Union([
|
|
309
|
+
Type.Literal(ThinkingLevelEnum.off),
|
|
310
|
+
Type.Literal(ThinkingLevelEnum.minimal),
|
|
311
|
+
Type.Literal(ThinkingLevelEnum.low),
|
|
312
|
+
Type.Literal(ThinkingLevelEnum.medium),
|
|
313
|
+
Type.Literal(ThinkingLevelEnum.high),
|
|
314
|
+
Type.Literal(ThinkingLevelEnum.xhigh)
|
|
328
315
|
]);
|
|
329
|
-
const
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
cost: Type.Object({
|
|
336
|
-
input: Type.Number(),
|
|
337
|
-
output: Type.Number(),
|
|
338
|
-
cacheRead: Type.Number(),
|
|
339
|
-
cacheWrite: Type.Number(),
|
|
340
|
-
total: Type.Number()
|
|
341
|
-
})
|
|
316
|
+
const ThinkingBudgetsSchema = _Stream.ThinkingBudgetsSchema = Type.Object({
|
|
317
|
+
off: Type.Optional(Type.Number()),
|
|
318
|
+
minimal: Type.Optional(Type.Number()),
|
|
319
|
+
low: Type.Optional(Type.Number()),
|
|
320
|
+
medium: Type.Optional(Type.Number()),
|
|
321
|
+
high: Type.Optional(Type.Number())
|
|
342
322
|
});
|
|
343
|
-
const
|
|
344
|
-
Type.Literal("
|
|
345
|
-
Type.Literal("
|
|
346
|
-
Type.Literal("
|
|
347
|
-
Type.Literal("error"),
|
|
348
|
-
Type.Literal("aborted")
|
|
323
|
+
const CacheRetentionSchema = _Stream.CacheRetentionSchema = Type.Union([
|
|
324
|
+
Type.Literal("none"),
|
|
325
|
+
Type.Literal("short"),
|
|
326
|
+
Type.Literal("long")
|
|
349
327
|
]);
|
|
350
|
-
const
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
}),
|
|
368
|
-
parts: Type.Array(Type.Union([
|
|
369
|
-
TextContentSchema,
|
|
370
|
-
ImageContentSchema,
|
|
371
|
-
ThinkingContentSchema,
|
|
372
|
-
ToolCallSchema
|
|
373
|
-
])),
|
|
374
|
-
responseId: Type.Optional(Type.String()),
|
|
375
|
-
messageId: Type.String()
|
|
328
|
+
const TransportSchema = _Stream.TransportSchema = Type.Union([
|
|
329
|
+
Type.Literal("sse"),
|
|
330
|
+
Type.Literal("websocket"),
|
|
331
|
+
Type.Literal("auto")
|
|
332
|
+
]);
|
|
333
|
+
const OptionsSchema = _Stream.OptionsSchema = Type.Object({
|
|
334
|
+
temperature: Type.Optional(Type.Number()),
|
|
335
|
+
maxTokens: Type.Optional(Type.Number()),
|
|
336
|
+
signal: Type.Optional(Type.Unsafe()),
|
|
337
|
+
apiKey: Type.Optional(Type.String()),
|
|
338
|
+
transport: Type.Optional(TransportSchema),
|
|
339
|
+
cacheRetention: Type.Optional(CacheRetentionSchema),
|
|
340
|
+
sessionId: Type.Optional(Type.String()),
|
|
341
|
+
onPayload: Type.Optional(Type.Unsafe()),
|
|
342
|
+
headers: Type.Optional(Type.Record(Type.String(), Type.String())),
|
|
343
|
+
maxRetryDelayMs: Type.Optional(Type.Number()),
|
|
344
|
+
metadata: Type.Optional(Type.Record(Type.String(), Type.Unknown()))
|
|
376
345
|
});
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
function createUserMessage(message) {
|
|
383
|
-
const { messageId = createMessageId(), ...rest } = message;
|
|
346
|
+
_Stream.SimpleOptionsSchema = Type.Composite([OptionsSchema, Type.Object({
|
|
347
|
+
reasoning: Type.Optional(ThinkingLevelSchema),
|
|
348
|
+
thinkingBudgets: Type.Optional(ThinkingBudgetsSchema)
|
|
349
|
+
})]);
|
|
350
|
+
function buildBaseOptions(model, options, apiKey) {
|
|
384
351
|
return {
|
|
385
|
-
|
|
386
|
-
|
|
352
|
+
temperature: options?.temperature,
|
|
353
|
+
maxTokens: options?.maxTokens || Math.min(model.maxTokens, 32e3),
|
|
354
|
+
signal: options?.signal,
|
|
355
|
+
apiKey: apiKey || options?.apiKey,
|
|
356
|
+
transport: options?.transport,
|
|
357
|
+
cacheRetention: options?.cacheRetention,
|
|
358
|
+
sessionId: options?.sessionId,
|
|
359
|
+
headers: options?.headers,
|
|
360
|
+
onPayload: options?.onPayload,
|
|
361
|
+
maxRetryDelayMs: options?.maxRetryDelayMs,
|
|
362
|
+
metadata: options?.metadata
|
|
387
363
|
};
|
|
388
364
|
}
|
|
389
|
-
|
|
390
|
-
function
|
|
391
|
-
|
|
365
|
+
_Stream.buildBaseOptions = buildBaseOptions;
|
|
366
|
+
function clampReasoning(effort) {
|
|
367
|
+
return effort === ThinkingLevelEnum.xhigh ? ThinkingLevelEnum.high : effort;
|
|
368
|
+
}
|
|
369
|
+
_Stream.clampReasoning = clampReasoning;
|
|
370
|
+
function adjustMaxTokensForThinking(baseMaxTokens, modelMaxTokens, reasoningLevel, customBudgets) {
|
|
371
|
+
const budgets = {
|
|
372
|
+
minimal: 1024,
|
|
373
|
+
low: 2048,
|
|
374
|
+
medium: 8192,
|
|
375
|
+
high: 16384,
|
|
376
|
+
...customBudgets
|
|
377
|
+
};
|
|
378
|
+
const minOutputTokens = 1024;
|
|
379
|
+
let thinkingBudget = budgets[clampReasoning(reasoningLevel)];
|
|
380
|
+
const maxTokens = Math.min(baseMaxTokens + thinkingBudget, modelMaxTokens);
|
|
381
|
+
if (maxTokens <= thinkingBudget) thinkingBudget = Math.max(0, maxTokens - minOutputTokens);
|
|
392
382
|
return {
|
|
393
|
-
|
|
394
|
-
|
|
383
|
+
maxTokens,
|
|
384
|
+
thinkingBudget
|
|
395
385
|
};
|
|
396
386
|
}
|
|
397
|
-
|
|
398
|
-
const
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
return tool;
|
|
405
|
-
}
|
|
406
|
-
_Message.defineTool = defineTool;
|
|
407
|
-
_Message.ContextSchema = Type.Object({
|
|
408
|
-
systemPrompt: Type.Optional(Type.String()),
|
|
409
|
-
messages: Type.Array(MessageSchema),
|
|
410
|
-
tools: Type.Optional(Type.Array(ToolSchema))
|
|
411
|
-
});
|
|
412
|
-
function transformMessages(messages, model, normalizeToolCallId) {
|
|
413
|
-
const toolCallIDMap = /* @__PURE__ */ new Map();
|
|
414
|
-
const transformed = [];
|
|
415
|
-
for (const msg of messages) {
|
|
416
|
-
if (msg.role === "user") {
|
|
417
|
-
transformed.push(msg);
|
|
418
|
-
continue;
|
|
419
|
-
}
|
|
420
|
-
const assistantMsg = msg;
|
|
421
|
-
const isSameModel = assistantMsg.provider.id === model.provider.id && assistantMsg.protocol === model.protocol && assistantMsg.model === model.id;
|
|
422
|
-
const parts = assistantMsg.parts.flatMap((block) => {
|
|
423
|
-
if (block.type === "thinking") {
|
|
424
|
-
if (isSameModel && block.thinkingSignature) return block;
|
|
425
|
-
if (!block.thinking || block.thinking.trim() === "") return [];
|
|
426
|
-
if (isSameModel) return block;
|
|
427
|
-
return {
|
|
428
|
-
type: "text",
|
|
429
|
-
text: block.thinking
|
|
430
|
-
};
|
|
431
|
-
}
|
|
432
|
-
if (block.type === "text") {
|
|
433
|
-
if (isSameModel) return block;
|
|
434
|
-
return {
|
|
435
|
-
type: "text",
|
|
436
|
-
text: block.text
|
|
437
|
-
};
|
|
438
|
-
}
|
|
439
|
-
if (block.type === "toolCall") {
|
|
440
|
-
const toolCall = block;
|
|
441
|
-
let normalizedToolCall = toolCall;
|
|
442
|
-
if (!isSameModel && toolCall.thoughtSignature) {
|
|
443
|
-
normalizedToolCall = { ...toolCall };
|
|
444
|
-
delete normalizedToolCall.thoughtSignature;
|
|
445
|
-
}
|
|
446
|
-
const normalizedID = toolCallIDMap.get(block.callID);
|
|
447
|
-
if (normalizedID && normalizedID !== block.callID) normalizedToolCall = {
|
|
448
|
-
...normalizedToolCall,
|
|
449
|
-
callID: normalizedID
|
|
450
|
-
};
|
|
451
|
-
else if (!isSameModel && normalizeToolCallId) {
|
|
452
|
-
const nextID = normalizeToolCallId(toolCall.callID, model, assistantMsg);
|
|
453
|
-
if (nextID !== toolCall.callID) {
|
|
454
|
-
toolCallIDMap.set(toolCall.callID, nextID);
|
|
455
|
-
normalizedToolCall = {
|
|
456
|
-
...normalizedToolCall,
|
|
457
|
-
callID: nextID
|
|
458
|
-
};
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
return normalizedToolCall;
|
|
462
|
-
}
|
|
463
|
-
return block;
|
|
387
|
+
_Stream.adjustMaxTokensForThinking = adjustMaxTokensForThinking;
|
|
388
|
+
const protocolProviderRegistry = /* @__PURE__ */ new Map();
|
|
389
|
+
function wrapStream(protocol, stream) {
|
|
390
|
+
return (model, context, options) => {
|
|
391
|
+
if (model.protocol !== protocol) throw new ProtocolMismatchError({
|
|
392
|
+
actual: model.protocol,
|
|
393
|
+
expected: protocol
|
|
464
394
|
});
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
395
|
+
return stream(model, context, options);
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
function wrapStreamSimple(protocol, streamSimple) {
|
|
399
|
+
return (model, context, options) => {
|
|
400
|
+
if (model.protocol !== protocol) throw new ProtocolMismatchError({
|
|
401
|
+
actual: model.protocol,
|
|
402
|
+
expected: protocol
|
|
468
403
|
});
|
|
469
|
-
|
|
470
|
-
|
|
404
|
+
return streamSimple(model, context, options);
|
|
405
|
+
};
|
|
471
406
|
}
|
|
472
|
-
|
|
473
|
-
|
|
407
|
+
function registerProtocolProvider(provider, sourceId) {
|
|
408
|
+
protocolProviderRegistry.set(provider.protocol, {
|
|
409
|
+
provider: {
|
|
410
|
+
protocol: provider.protocol,
|
|
411
|
+
stream: wrapStream(provider.protocol, provider.stream),
|
|
412
|
+
streamSimple: wrapStreamSimple(provider.protocol, provider.streamSimple)
|
|
413
|
+
},
|
|
414
|
+
sourceId
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
_Stream.registerProtocolProvider = registerProtocolProvider;
|
|
418
|
+
function getProtocolProvider(protocol) {
|
|
419
|
+
return protocolProviderRegistry.get(protocol)?.provider;
|
|
420
|
+
}
|
|
421
|
+
_Stream.getProtocolProvider = getProtocolProvider;
|
|
422
|
+
function resolveProtocolProvider(model) {
|
|
423
|
+
const provider = getProtocolProvider(model.protocol);
|
|
424
|
+
if (!provider) throw new ProtocolProviderNotFoundError({ protocol: model.protocol });
|
|
425
|
+
return provider;
|
|
426
|
+
}
|
|
427
|
+
_Stream.resolveProtocolProvider = resolveProtocolProvider;
|
|
428
|
+
function stream(model, context, options) {
|
|
429
|
+
return resolveProtocolProvider(model).stream(model, context, options);
|
|
430
|
+
}
|
|
431
|
+
_Stream.stream = stream;
|
|
432
|
+
async function complete(model, context, options) {
|
|
433
|
+
return stream(model, context, options).result();
|
|
434
|
+
}
|
|
435
|
+
_Stream.complete = complete;
|
|
436
|
+
function streamSimple(model, context, options) {
|
|
437
|
+
return resolveProtocolProvider(model).streamSimple(model, context, options);
|
|
438
|
+
}
|
|
439
|
+
_Stream.streamSimple = streamSimple;
|
|
440
|
+
async function completeSimple(model, context, options) {
|
|
441
|
+
return streamSimple(model, context, options).result();
|
|
442
|
+
}
|
|
443
|
+
_Stream.completeSimple = completeSimple;
|
|
444
|
+
function getApiProviders() {
|
|
445
|
+
return Array.from(protocolProviderRegistry.values(), (entry) => entry.provider);
|
|
446
|
+
}
|
|
447
|
+
_Stream.getApiProviders = getApiProviders;
|
|
448
|
+
function unregisterProtocolProviders(sourceId) {
|
|
449
|
+
for (const [api, entry] of protocolProviderRegistry.entries()) if (entry.sourceId === sourceId) protocolProviderRegistry.delete(api);
|
|
450
|
+
}
|
|
451
|
+
_Stream.unregisterProtocolProviders = unregisterProtocolProviders;
|
|
452
|
+
function clearProtocolProviders() {
|
|
453
|
+
protocolProviderRegistry.clear();
|
|
454
|
+
}
|
|
455
|
+
_Stream.clearProtocolProviders = clearProtocolProviders;
|
|
456
|
+
})(Stream || (Stream = {}));
|
|
474
457
|
//#endregion
|
|
475
458
|
//#region src/utils/eventstream.ts
|
|
476
459
|
var EventStream = class {
|
|
@@ -530,1306 +513,2020 @@ var AssistantMessageEventStream = class extends EventStream {
|
|
|
530
513
|
}
|
|
531
514
|
};
|
|
532
515
|
//#endregion
|
|
533
|
-
//#region src/utils/
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
516
|
+
//#region src/utils/validation.ts
|
|
517
|
+
const ajv = new Ajv({
|
|
518
|
+
allErrors: true,
|
|
519
|
+
strict: false,
|
|
520
|
+
coerceTypes: true
|
|
521
|
+
});
|
|
522
|
+
addFormats(ajv);
|
|
537
523
|
/**
|
|
538
|
-
*
|
|
539
|
-
* Always returns a valid object, even if the JSON is incomplete.
|
|
540
|
-
*
|
|
541
|
-
* @param partialJson The partial JSON string from streaming
|
|
542
|
-
* @returns Parsed object or empty object if parsing fails
|
|
524
|
+
* Validates an arbitrary value against a TypeBox schema and returns the coerced value.
|
|
543
525
|
*/
|
|
544
|
-
function
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
}
|
|
554
|
-
|
|
526
|
+
function validateSchema(schema, value, label) {
|
|
527
|
+
const validate = ajv.compile(schema);
|
|
528
|
+
const input = structuredClone(value);
|
|
529
|
+
if (validate(input)) return input;
|
|
530
|
+
const errors = validate.errors?.map((err) => {
|
|
531
|
+
return ` - ${err.instancePath ? err.instancePath.substring(1) : err.params?.missingProperty || "root"}: ${err.message}`;
|
|
532
|
+
}).join("\n") || "Unknown Validation Error";
|
|
533
|
+
throw new Error([
|
|
534
|
+
`Validation Failed For ${label}`,
|
|
535
|
+
`${errors}\n`,
|
|
536
|
+
"Received Value:",
|
|
537
|
+
`${JSON.stringify(value, null, 2)}\n`
|
|
538
|
+
].join("\n"));
|
|
555
539
|
}
|
|
556
|
-
//#endregion
|
|
557
|
-
//#region src/utils/sanitize.ts
|
|
558
540
|
/**
|
|
559
|
-
*
|
|
560
|
-
*
|
|
561
|
-
* Unpaired surrogates (high surrogates 0xD800-0xDBFF without matching low surrogates 0xDC00-0xDFFF,
|
|
562
|
-
* or vice versa) cause JSON serialization errors in many API providers.
|
|
563
|
-
*
|
|
564
|
-
* Valid emoji and other characters outside the Basic Multilingual Plane use properly paired
|
|
565
|
-
* surrogates and will NOT be affected by this function.
|
|
566
|
-
*
|
|
567
|
-
* @param text - The text to sanitize
|
|
568
|
-
* @returns The sanitized text with unpaired surrogates removed
|
|
569
|
-
*
|
|
570
|
-
* @example
|
|
571
|
-
* // Valid emoji (properly paired surrogates) are preserved
|
|
572
|
-
* sanitizeSurrogates("Hello 🙈 World") // => "Hello 🙈 World"
|
|
573
|
-
*
|
|
574
|
-
* // Unpaired high surrogate is removed
|
|
575
|
-
* const unpaired = String.fromCharCode(0xD83D); // high surrogate without low
|
|
576
|
-
* sanitizeSurrogates(`Text ${unpaired} here`) // => "Text here"
|
|
541
|
+
* Validates tool call arguments against the tool's TypeBox schema
|
|
577
542
|
*/
|
|
578
|
-
function
|
|
579
|
-
return
|
|
543
|
+
function validateToolArguments(tool, toolExecution) {
|
|
544
|
+
return validateSchema(tool.parameters, toolExecution.rawArgs, `Tool "${toolExecution.name}"`);
|
|
580
545
|
}
|
|
581
546
|
//#endregion
|
|
582
|
-
//#region src/
|
|
583
|
-
let
|
|
584
|
-
(function(
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
actual: Model.KnownProtocolSchema,
|
|
588
|
-
expected: Model.KnownProtocolSchema
|
|
589
|
-
}));
|
|
590
|
-
const ThinkingLevelEnum = _Stream.ThinkingLevelEnum = {
|
|
591
|
-
off: "off",
|
|
592
|
-
minimal: "minimal",
|
|
593
|
-
low: "low",
|
|
594
|
-
medium: "medium",
|
|
595
|
-
high: "high",
|
|
596
|
-
xhigh: "xhigh"
|
|
597
|
-
};
|
|
598
|
-
const ThinkingLevelSchema = _Stream.ThinkingLevelSchema = Type.Union([
|
|
599
|
-
Type.Literal(ThinkingLevelEnum.off),
|
|
600
|
-
Type.Literal(ThinkingLevelEnum.minimal),
|
|
601
|
-
Type.Literal(ThinkingLevelEnum.low),
|
|
602
|
-
Type.Literal(ThinkingLevelEnum.medium),
|
|
603
|
-
Type.Literal(ThinkingLevelEnum.high),
|
|
604
|
-
Type.Literal(ThinkingLevelEnum.xhigh)
|
|
605
|
-
]);
|
|
606
|
-
const ThinkingBudgetsSchema = _Stream.ThinkingBudgetsSchema = Type.Object({
|
|
607
|
-
off: Type.Optional(Type.Number()),
|
|
608
|
-
minimal: Type.Optional(Type.Number()),
|
|
609
|
-
low: Type.Optional(Type.Number()),
|
|
610
|
-
medium: Type.Optional(Type.Number()),
|
|
611
|
-
high: Type.Optional(Type.Number())
|
|
612
|
-
});
|
|
613
|
-
const CacheRetentionSchema = _Stream.CacheRetentionSchema = Type.Union([
|
|
614
|
-
Type.Literal("none"),
|
|
615
|
-
Type.Literal("short"),
|
|
616
|
-
Type.Literal("long")
|
|
617
|
-
]);
|
|
618
|
-
const TransportSchema = _Stream.TransportSchema = Type.Union([
|
|
619
|
-
Type.Literal("sse"),
|
|
620
|
-
Type.Literal("websocket"),
|
|
621
|
-
Type.Literal("auto")
|
|
622
|
-
]);
|
|
623
|
-
const OptionsSchema = _Stream.OptionsSchema = Type.Object({
|
|
624
|
-
temperature: Type.Optional(Type.Number()),
|
|
625
|
-
maxTokens: Type.Optional(Type.Number()),
|
|
626
|
-
signal: Type.Optional(Type.Unsafe()),
|
|
627
|
-
apiKey: Type.Optional(Type.String()),
|
|
628
|
-
transport: Type.Optional(TransportSchema),
|
|
629
|
-
cacheRetention: Type.Optional(CacheRetentionSchema),
|
|
630
|
-
sessionId: Type.Optional(Type.String()),
|
|
631
|
-
onPayload: Type.Optional(Type.Unsafe()),
|
|
632
|
-
headers: Type.Optional(Type.Record(Type.String(), Type.String())),
|
|
633
|
-
maxRetryDelayMs: Type.Optional(Type.Number()),
|
|
634
|
-
metadata: Type.Optional(Type.Record(Type.String(), Type.Unknown()))
|
|
635
|
-
});
|
|
636
|
-
_Stream.SimpleOptionsSchema = Type.Composite([OptionsSchema, Type.Object({
|
|
637
|
-
reasoning: Type.Optional(ThinkingLevelSchema),
|
|
638
|
-
thinkingBudgets: Type.Optional(ThinkingBudgetsSchema)
|
|
639
|
-
})]);
|
|
640
|
-
function buildBaseOptions(model, options, apiKey) {
|
|
641
|
-
return {
|
|
642
|
-
temperature: options?.temperature,
|
|
643
|
-
maxTokens: options?.maxTokens || Math.min(model.maxTokens, 32e3),
|
|
644
|
-
signal: options?.signal,
|
|
645
|
-
apiKey: apiKey || options?.apiKey,
|
|
646
|
-
transport: options?.transport,
|
|
647
|
-
cacheRetention: options?.cacheRetention,
|
|
648
|
-
sessionId: options?.sessionId,
|
|
649
|
-
headers: options?.headers,
|
|
650
|
-
onPayload: options?.onPayload,
|
|
651
|
-
maxRetryDelayMs: options?.maxRetryDelayMs,
|
|
652
|
-
metadata: options?.metadata
|
|
653
|
-
};
|
|
547
|
+
//#region src/agent/loop.ts
|
|
548
|
+
let Loop;
|
|
549
|
+
(function(_Loop) {
|
|
550
|
+
function createAgentStream() {
|
|
551
|
+
return new EventStream((event) => event.type === "agent.end", (event) => event.type === "agent.end" ? event.messages : []);
|
|
654
552
|
}
|
|
655
|
-
|
|
656
|
-
function
|
|
657
|
-
return
|
|
553
|
+
const AgentContextError = _Loop.AgentContextError = NamedError.create("AgentContextError", Type.Object({ message: Type.String() }));
|
|
554
|
+
function snapshotAssistantMessage(message) {
|
|
555
|
+
return structuredClone(message);
|
|
658
556
|
}
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
const
|
|
662
|
-
|
|
663
|
-
low: 2048,
|
|
664
|
-
medium: 8192,
|
|
665
|
-
high: 16384,
|
|
666
|
-
...customBudgets
|
|
667
|
-
};
|
|
668
|
-
const minOutputTokens = 1024;
|
|
669
|
-
let thinkingBudget = budgets[clampReasoning(reasoningLevel)];
|
|
670
|
-
const maxTokens = Math.min(baseMaxTokens + thinkingBudget, modelMaxTokens);
|
|
671
|
-
if (maxTokens <= thinkingBudget) thinkingBudget = Math.max(0, maxTokens - minOutputTokens);
|
|
557
|
+
function snapshotAssistantPartEvent(message, partIndex) {
|
|
558
|
+
const snapshot = snapshotAssistantMessage(message);
|
|
559
|
+
const part = snapshot.parts[partIndex];
|
|
560
|
+
if (!part) throw new Error(`Assistant message part at index ${partIndex} was not found`);
|
|
672
561
|
return {
|
|
673
|
-
|
|
674
|
-
|
|
562
|
+
message: snapshot,
|
|
563
|
+
part
|
|
675
564
|
};
|
|
676
565
|
}
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
566
|
+
async function emitEvent(emit, event) {
|
|
567
|
+
await emit(event);
|
|
568
|
+
}
|
|
569
|
+
async function streamAssistantResponse(config, context, emit, streamFn, signal) {
|
|
570
|
+
let messages = context.messages;
|
|
571
|
+
if (config.transformContext) messages = await config.transformContext(messages, signal);
|
|
572
|
+
const llmMessages = await config.convertToLlm(messages);
|
|
573
|
+
const llmContext = {
|
|
574
|
+
systemPrompt: context.systemPrompt,
|
|
575
|
+
messages: llmMessages,
|
|
576
|
+
tools: context.tools
|
|
577
|
+
};
|
|
578
|
+
const streamFunction = streamFn || Stream.streamSimple;
|
|
579
|
+
const resolvedApiKey = (config.getApiKey ? await config.getApiKey(config.model.provider) : void 0) || config.apiKey;
|
|
580
|
+
const events = await streamFunction(config.model, llmContext, {
|
|
581
|
+
...config,
|
|
582
|
+
apiKey: resolvedApiKey,
|
|
583
|
+
signal
|
|
584
|
+
});
|
|
585
|
+
let partialMessage = null;
|
|
586
|
+
let started = false;
|
|
587
|
+
const ensureMessageStarted = async (message) => {
|
|
588
|
+
if (started) return;
|
|
589
|
+
started = true;
|
|
590
|
+
await emitEvent(emit, {
|
|
591
|
+
type: "message.start",
|
|
592
|
+
message: snapshotAssistantMessage(message)
|
|
684
593
|
});
|
|
685
|
-
return stream(model, context, options);
|
|
686
594
|
};
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
actual: model.protocol,
|
|
692
|
-
expected: protocol
|
|
595
|
+
const emitMessageUpdate = async (message) => {
|
|
596
|
+
await emitEvent(emit, {
|
|
597
|
+
type: "message.update",
|
|
598
|
+
message: snapshotAssistantMessage(message)
|
|
693
599
|
});
|
|
694
|
-
return streamSimple(model, context, options);
|
|
695
600
|
};
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
601
|
+
const emitPartStart = async (message, partIndex) => {
|
|
602
|
+
await emitEvent(emit, {
|
|
603
|
+
type: "message.part.start",
|
|
604
|
+
partIndex,
|
|
605
|
+
...snapshotAssistantPartEvent(message, partIndex)
|
|
606
|
+
});
|
|
607
|
+
};
|
|
608
|
+
const emitPartUpdate = async (message, partIndex) => {
|
|
609
|
+
await emitEvent(emit, {
|
|
610
|
+
type: "message.part.update",
|
|
611
|
+
partIndex,
|
|
612
|
+
...snapshotAssistantPartEvent(message, partIndex),
|
|
613
|
+
source: "llm"
|
|
614
|
+
});
|
|
615
|
+
};
|
|
616
|
+
const emitPartEnd = async (message, partIndex) => {
|
|
617
|
+
await emitEvent(emit, {
|
|
618
|
+
type: "message.part.end",
|
|
619
|
+
partIndex,
|
|
620
|
+
...snapshotAssistantPartEvent(message, partIndex)
|
|
621
|
+
});
|
|
622
|
+
};
|
|
623
|
+
for await (const event of events) switch (event.type) {
|
|
624
|
+
case "start":
|
|
625
|
+
partialMessage = event.partial;
|
|
626
|
+
await ensureMessageStarted(partialMessage);
|
|
627
|
+
break;
|
|
628
|
+
case "text.start":
|
|
629
|
+
case "thinking.start":
|
|
630
|
+
case "toolcall.start":
|
|
631
|
+
partialMessage = event.partial;
|
|
632
|
+
await ensureMessageStarted(partialMessage);
|
|
633
|
+
await emitPartStart(partialMessage, event.partIndex);
|
|
634
|
+
break;
|
|
635
|
+
case "text.delta":
|
|
636
|
+
case "thinking.delta":
|
|
637
|
+
case "toolcall.delta":
|
|
638
|
+
partialMessage = event.partial;
|
|
639
|
+
await ensureMessageStarted(partialMessage);
|
|
640
|
+
await emitPartUpdate(partialMessage, event.partIndex);
|
|
641
|
+
break;
|
|
642
|
+
case "text.end":
|
|
643
|
+
case "thinking.end":
|
|
644
|
+
case "toolcall.end":
|
|
645
|
+
partialMessage = event.partial;
|
|
646
|
+
await ensureMessageStarted(partialMessage);
|
|
647
|
+
await emitPartEnd(partialMessage, event.partIndex);
|
|
648
|
+
await emitMessageUpdate(partialMessage);
|
|
649
|
+
break;
|
|
650
|
+
case "done":
|
|
651
|
+
await ensureMessageStarted(event.message);
|
|
652
|
+
await emitEvent(emit, {
|
|
653
|
+
type: "message.end",
|
|
654
|
+
message: snapshotAssistantMessage(event.message)
|
|
655
|
+
});
|
|
656
|
+
return event.message;
|
|
657
|
+
case "error":
|
|
658
|
+
await ensureMessageStarted(event.error);
|
|
659
|
+
await emitEvent(emit, {
|
|
660
|
+
type: "message.end",
|
|
661
|
+
message: snapshotAssistantMessage(event.error)
|
|
662
|
+
});
|
|
663
|
+
return event.error;
|
|
664
|
+
}
|
|
665
|
+
const finalMessage = await events.result();
|
|
666
|
+
await ensureMessageStarted(finalMessage);
|
|
667
|
+
await emitEvent(emit, {
|
|
668
|
+
type: "message.end",
|
|
669
|
+
message: snapshotAssistantMessage(finalMessage)
|
|
705
670
|
});
|
|
671
|
+
return finalMessage;
|
|
706
672
|
}
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
return
|
|
710
|
-
}
|
|
711
|
-
_Stream.getProtocolProvider = getProtocolProvider;
|
|
712
|
-
function resolveProtocolProvider(model) {
|
|
713
|
-
const provider = getProtocolProvider(model.protocol);
|
|
714
|
-
if (!provider) throw new ProtocolProviderNotFoundError({ protocol: model.protocol });
|
|
715
|
-
return provider;
|
|
716
|
-
}
|
|
717
|
-
_Stream.resolveProtocolProvider = resolveProtocolProvider;
|
|
718
|
-
function stream(model, context, options) {
|
|
719
|
-
return resolveProtocolProvider(model).stream(model, context, options);
|
|
720
|
-
}
|
|
721
|
-
_Stream.stream = stream;
|
|
722
|
-
async function complete(model, context, options) {
|
|
723
|
-
return stream(model, context, options).result();
|
|
724
|
-
}
|
|
725
|
-
_Stream.complete = complete;
|
|
726
|
-
function streamSimple(model, context, options) {
|
|
727
|
-
return resolveProtocolProvider(model).streamSimple(model, context, options);
|
|
728
|
-
}
|
|
729
|
-
_Stream.streamSimple = streamSimple;
|
|
730
|
-
async function completeSimple(model, context, options) {
|
|
731
|
-
return streamSimple(model, context, options).result();
|
|
732
|
-
}
|
|
733
|
-
_Stream.completeSimple = completeSimple;
|
|
734
|
-
function getApiProviders() {
|
|
735
|
-
return Array.from(protocolProviderRegistry.values(), (entry) => entry.provider);
|
|
736
|
-
}
|
|
737
|
-
_Stream.getApiProviders = getApiProviders;
|
|
738
|
-
function unregisterProtocolProviders(sourceId) {
|
|
739
|
-
for (const [api, entry] of protocolProviderRegistry.entries()) if (entry.sourceId === sourceId) protocolProviderRegistry.delete(api);
|
|
673
|
+
async function executeToolCalls(config, currentContext, message, toolCalls, emit, signal) {
|
|
674
|
+
if (config.toolExecution === "sequential") return executeToolCallsSequential(config, currentContext, message, toolCalls, emit, signal);
|
|
675
|
+
return executeToolCallsParallel(config, currentContext, message, toolCalls, emit, signal);
|
|
740
676
|
}
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
677
|
+
async function executeToolCallsSequential(config, currentContext, message, toolCalls, emit, signal) {
|
|
678
|
+
for (const toolCall of toolCalls) {
|
|
679
|
+
const toolCallInFlight = {
|
|
680
|
+
callID: toolCall.callID,
|
|
681
|
+
name: toolCall.name,
|
|
682
|
+
rawArgs: toolCall.arguments
|
|
683
|
+
};
|
|
684
|
+
await emitEvent(emit, {
|
|
685
|
+
type: "tool.execution.start",
|
|
686
|
+
...toolCallInFlight
|
|
687
|
+
});
|
|
688
|
+
const prepared = await prepareToolCall(config, currentContext, message, toolCallInFlight, signal);
|
|
689
|
+
if (prepared.error) {
|
|
690
|
+
const error = {
|
|
691
|
+
status: "error",
|
|
692
|
+
result: {
|
|
693
|
+
content: [{
|
|
694
|
+
type: "text",
|
|
695
|
+
text: prepared.error.message
|
|
696
|
+
}],
|
|
697
|
+
details: prepared.error.details,
|
|
698
|
+
isError: true
|
|
699
|
+
}
|
|
700
|
+
};
|
|
701
|
+
await emitToolCallTerminalResult(toolCall, prepared.runnable, message, emit, error);
|
|
702
|
+
} else {
|
|
703
|
+
const runnable = {
|
|
704
|
+
toolCall,
|
|
705
|
+
runnable: prepared.runnable,
|
|
706
|
+
tool: prepared.tool
|
|
707
|
+
};
|
|
708
|
+
const executed = await executeRunnableToolCall(runnable, message, emit, signal);
|
|
709
|
+
if (executed.error) {
|
|
710
|
+
const error = {
|
|
711
|
+
status: "error",
|
|
712
|
+
result: {
|
|
713
|
+
content: [{
|
|
714
|
+
type: "text",
|
|
715
|
+
text: executed.error.message
|
|
716
|
+
}],
|
|
717
|
+
details: executed.error.details,
|
|
718
|
+
isError: true
|
|
719
|
+
}
|
|
720
|
+
};
|
|
721
|
+
await emitToolCallTerminalResult(runnable.toolCall, runnable.runnable, message, emit, error);
|
|
722
|
+
} else await finalizeExecutedToolCall(config, currentContext, runnable.toolCall, runnable.runnable, message, emit, executed.result);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
744
725
|
}
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
726
|
+
async function executeToolCallsParallel(config, currentContext, message, toolCalls, emit, signal) {
|
|
727
|
+
const runnables = [];
|
|
728
|
+
for (const toolCall of toolCalls) {
|
|
729
|
+
const toolCallInFlight = {
|
|
730
|
+
callID: toolCall.callID,
|
|
731
|
+
name: toolCall.name,
|
|
732
|
+
rawArgs: toolCall.arguments
|
|
733
|
+
};
|
|
734
|
+
await emitEvent(emit, {
|
|
735
|
+
type: "tool.execution.start",
|
|
736
|
+
...toolCallInFlight
|
|
737
|
+
});
|
|
738
|
+
const prepared = await prepareToolCall(config, currentContext, message, toolCallInFlight, signal);
|
|
739
|
+
if (prepared.error) {
|
|
740
|
+
const error = {
|
|
741
|
+
status: "error",
|
|
742
|
+
result: {
|
|
743
|
+
content: [{
|
|
744
|
+
type: "text",
|
|
745
|
+
text: prepared.error.message
|
|
746
|
+
}],
|
|
747
|
+
details: prepared.error.details,
|
|
748
|
+
isError: true
|
|
749
|
+
}
|
|
750
|
+
};
|
|
751
|
+
await emitToolCallTerminalResult(toolCall, prepared.runnable, message, emit, error);
|
|
752
|
+
} else runnables.push({
|
|
753
|
+
toolCall,
|
|
754
|
+
runnable: prepared.runnable,
|
|
755
|
+
tool: prepared.tool
|
|
756
|
+
});
|
|
757
|
+
}
|
|
758
|
+
const runningExecutions = runnables.map((runnable) => ({
|
|
759
|
+
runnable,
|
|
760
|
+
execution: executeRunnableToolCall(runnable, message, emit, signal)
|
|
761
|
+
}));
|
|
762
|
+
for (const running of runningExecutions) {
|
|
763
|
+
const executed = await running.execution;
|
|
764
|
+
const { runnable } = running;
|
|
765
|
+
if (executed.error) {
|
|
766
|
+
const error = {
|
|
767
|
+
status: "error",
|
|
768
|
+
result: {
|
|
769
|
+
content: [{
|
|
770
|
+
type: "text",
|
|
771
|
+
text: executed.error.message
|
|
772
|
+
}],
|
|
773
|
+
details: executed.error.details,
|
|
774
|
+
isError: true
|
|
775
|
+
}
|
|
776
|
+
};
|
|
777
|
+
await emitToolCallTerminalResult(runnable.toolCall, runnable.runnable, message, emit, error);
|
|
778
|
+
} else await finalizeExecutedToolCall(config, currentContext, runnable.toolCall, runnable.runnable, message, emit, executed.result);
|
|
764
779
|
}
|
|
765
|
-
};
|
|
766
|
-
}
|
|
767
|
-
const claudeCodeVersion = "2.1.2";
|
|
768
|
-
const ccToolLookup = new Map([
|
|
769
|
-
"Read",
|
|
770
|
-
"Write",
|
|
771
|
-
"Edit",
|
|
772
|
-
"Bash",
|
|
773
|
-
"Grep",
|
|
774
|
-
"Glob",
|
|
775
|
-
"AskUserQuestion",
|
|
776
|
-
"EnterPlanMode",
|
|
777
|
-
"ExitPlanMode",
|
|
778
|
-
"KillShell",
|
|
779
|
-
"NotebookEdit",
|
|
780
|
-
"Skill",
|
|
781
|
-
"Task",
|
|
782
|
-
"TaskOutput",
|
|
783
|
-
"TodoWrite",
|
|
784
|
-
"WebFetch",
|
|
785
|
-
"WebSearch"
|
|
786
|
-
].map((t) => [t.toLowerCase(), t]));
|
|
787
|
-
const toClaudeCodeName = (name) => ccToolLookup.get(name.toLowerCase()) ?? name;
|
|
788
|
-
const fromClaudeCodeName = (name, tools) => {
|
|
789
|
-
if (tools && tools.length > 0) {
|
|
790
|
-
const lowerName = name.toLowerCase();
|
|
791
|
-
const matchedTool = tools.find((tool) => tool.name.toLowerCase() === lowerName);
|
|
792
|
-
if (matchedTool) return matchedTool.name;
|
|
793
780
|
}
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
781
|
+
async function prepareToolCall(config, currentContext, assistantMessage, toolCallInFlight, signal) {
|
|
782
|
+
const tool = currentContext.tools?.find((t) => t.name === toolCallInFlight.name);
|
|
783
|
+
if (!tool) return {
|
|
784
|
+
error: {
|
|
785
|
+
kind: "error",
|
|
786
|
+
message: `Tool ${toolCallInFlight.name} Not Found`
|
|
787
|
+
},
|
|
788
|
+
runnable: toolCallInFlight
|
|
802
789
|
};
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
790
|
+
try {
|
|
791
|
+
toolCallInFlight.args = validateToolArguments(tool, toolCallInFlight);
|
|
792
|
+
if (config.beforeToolExecution) {
|
|
793
|
+
const beforeResult = await config.beforeToolExecution({
|
|
794
|
+
context: currentContext,
|
|
795
|
+
assistantMessage,
|
|
796
|
+
toolCall: toolCallInFlight
|
|
797
|
+
}, signal);
|
|
798
|
+
if (beforeResult?.block) return {
|
|
799
|
+
error: {
|
|
800
|
+
kind: "blocked",
|
|
801
|
+
message: beforeResult?.reason || "Tool Execution was Blocked",
|
|
802
|
+
details: beforeResult?.details
|
|
803
|
+
},
|
|
804
|
+
runnable: toolCallInFlight
|
|
805
|
+
};
|
|
809
806
|
}
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
}
|
|
823
|
-
function isOAuthToken(apiKey) {
|
|
824
|
-
return apiKey.includes("sk-ant-oat");
|
|
825
|
-
}
|
|
826
|
-
function createClient(model, apiKey, interleavedThinking, optionsHeaders) {
|
|
827
|
-
const betaFeatures = ["fine-grained-tool-streaming-2025-05-14"];
|
|
828
|
-
if (interleavedThinking) betaFeatures.push("interleaved-thinking-2025-05-14");
|
|
829
|
-
if (isOAuthToken(apiKey)) {
|
|
830
|
-
const defaultHeaders = mergeHeaders({
|
|
831
|
-
accept: "application/json",
|
|
832
|
-
"anthropic-dangerous-direct-browser-access": "true",
|
|
833
|
-
"anthropic-beta": `claude-code-20250219,oauth-2025-04-20,${betaFeatures.join(",")}`,
|
|
834
|
-
"user-agent": `claude-cli/${claudeCodeVersion} (external, cli)`,
|
|
835
|
-
"x-app": "cli"
|
|
836
|
-
}, model.headers, optionsHeaders);
|
|
837
|
-
return {
|
|
838
|
-
client: new Anthropic({
|
|
839
|
-
apiKey: null,
|
|
840
|
-
authToken: apiKey,
|
|
841
|
-
baseURL: model.baseUrl,
|
|
842
|
-
defaultHeaders,
|
|
843
|
-
dangerouslyAllowBrowser: true
|
|
844
|
-
}),
|
|
845
|
-
isOAuthToken: true
|
|
846
|
-
};
|
|
847
|
-
}
|
|
848
|
-
const defaultHeaders = mergeHeaders({
|
|
849
|
-
accept: "application/json",
|
|
850
|
-
"anthropic-dangerous-direct-browser-access": "true",
|
|
851
|
-
"anthropic-beta": betaFeatures.join(",")
|
|
852
|
-
}, model.headers, optionsHeaders);
|
|
853
|
-
return {
|
|
854
|
-
client: new Anthropic({
|
|
855
|
-
apiKey,
|
|
856
|
-
baseURL: model.baseUrl,
|
|
857
|
-
dangerouslyAllowBrowser: true,
|
|
858
|
-
defaultHeaders
|
|
859
|
-
}),
|
|
860
|
-
isOAuthToken: false
|
|
861
|
-
};
|
|
862
|
-
}
|
|
863
|
-
function normalizeToolCallId(id) {
|
|
864
|
-
return id.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 64);
|
|
865
|
-
}
|
|
866
|
-
function assertReplayableAssistant(message) {
|
|
867
|
-
let sawToolCall = false;
|
|
868
|
-
let sawNonTerminalToolCall = false;
|
|
869
|
-
let sawTerminalToolCall = false;
|
|
870
|
-
for (const part of message.parts) {
|
|
871
|
-
if (part.type === "image") throw new Error("Anthropic replay does not support assistant image parts");
|
|
872
|
-
if (part.type === "toolCall") {
|
|
873
|
-
sawToolCall = true;
|
|
874
|
-
if (part.status === "pending" || part.status === "running") sawNonTerminalToolCall = true;
|
|
875
|
-
else sawTerminalToolCall = true;
|
|
876
|
-
continue;
|
|
807
|
+
return {
|
|
808
|
+
runnable: toolCallInFlight,
|
|
809
|
+
tool
|
|
810
|
+
};
|
|
811
|
+
} catch (err) {
|
|
812
|
+
return {
|
|
813
|
+
error: {
|
|
814
|
+
kind: "error",
|
|
815
|
+
message: err instanceof Error ? err.message : String(err)
|
|
816
|
+
},
|
|
817
|
+
runnable: toolCallInFlight
|
|
818
|
+
};
|
|
877
819
|
}
|
|
878
|
-
if (sawToolCall) throw new Error("Assistant parts after a toolCall are not replayable for Anthropic");
|
|
879
820
|
}
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
content: convertContentBlocks([{
|
|
889
|
-
type: "text",
|
|
890
|
-
text: [
|
|
891
|
-
"<result>",
|
|
892
|
-
"No Result Provided",
|
|
893
|
-
"</result>"
|
|
894
|
-
].join("\n")
|
|
895
|
-
}]),
|
|
896
|
-
is_error: true
|
|
897
|
-
};
|
|
898
|
-
}
|
|
899
|
-
function convertMessages(messages, model, isOAuthToken, cacheControl) {
|
|
900
|
-
const params = [];
|
|
901
|
-
const transformedMessages = Message.transformMessages(messages, model, normalizeToolCallId);
|
|
902
|
-
for (const msg of transformedMessages) {
|
|
903
|
-
if (msg.role === "user") {
|
|
904
|
-
const blocks = msg.parts.map((item) => {
|
|
905
|
-
if (item.type === "text") return {
|
|
906
|
-
type: "text",
|
|
907
|
-
text: sanitizeSurrogates(item.text)
|
|
908
|
-
};
|
|
909
|
-
return {
|
|
910
|
-
type: "image",
|
|
911
|
-
source: {
|
|
912
|
-
type: "base64",
|
|
913
|
-
media_type: item.mimeType,
|
|
914
|
-
data: item.data
|
|
915
|
-
}
|
|
821
|
+
async function executeRunnableToolCall(toolCallRunnable, message, emit, signal) {
|
|
822
|
+
const { toolCall, runnable, tool } = toolCallRunnable;
|
|
823
|
+
const { partIndex, ...pendingPart } = toolCall;
|
|
824
|
+
try {
|
|
825
|
+
return { result: await tool.execute(runnable.callID, runnable.args, signal, async (result) => {
|
|
826
|
+
const runningResult = {
|
|
827
|
+
status: "running",
|
|
828
|
+
partial: result.partial
|
|
916
829
|
};
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
}
|
|
937
|
-
} else if (block.type === "thinking") {
|
|
938
|
-
if (block.thinking.trim().length === 0) continue;
|
|
939
|
-
if (!block.thinkingSignature || block.thinkingSignature.trim().length === 0) assistantBlocks.push({
|
|
940
|
-
type: "text",
|
|
941
|
-
text: sanitizeSurrogates(block.thinking)
|
|
942
|
-
});
|
|
943
|
-
else assistantBlocks.push({
|
|
944
|
-
type: "thinking",
|
|
945
|
-
thinking: sanitizeSurrogates(block.thinking),
|
|
946
|
-
signature: block.thinkingSignature
|
|
947
|
-
});
|
|
948
|
-
} else if (block.type === "toolCall") {
|
|
949
|
-
assistantBlocks.push({
|
|
950
|
-
type: "tool_use",
|
|
951
|
-
id: block.callID,
|
|
952
|
-
name: isOAuthToken ? toClaudeCodeName(block.name) : block.name,
|
|
953
|
-
input: block.arguments ?? {}
|
|
954
|
-
});
|
|
955
|
-
if (block.status === "pending" || block.status === "running") toolResults.push(buildSyntheticToolResult(block));
|
|
956
|
-
else toolResults.push({
|
|
957
|
-
type: "tool_result",
|
|
958
|
-
tool_use_id: block.callID,
|
|
959
|
-
content: convertContentBlocks(block.result.content),
|
|
960
|
-
is_error: block.result.isError
|
|
961
|
-
});
|
|
830
|
+
await emitEvent(emit, {
|
|
831
|
+
type: "tool.execution.update",
|
|
832
|
+
...runnable,
|
|
833
|
+
...runningResult
|
|
834
|
+
});
|
|
835
|
+
const runningPart = {
|
|
836
|
+
...pendingPart,
|
|
837
|
+
...runningResult
|
|
838
|
+
};
|
|
839
|
+
message.parts[partIndex] = runningPart;
|
|
840
|
+
await emitEvent(emit, {
|
|
841
|
+
type: "message.part.update",
|
|
842
|
+
message,
|
|
843
|
+
partIndex,
|
|
844
|
+
part: runningPart,
|
|
845
|
+
source: "tool"
|
|
846
|
+
});
|
|
847
|
+
}) };
|
|
848
|
+
} catch (err) {
|
|
849
|
+
return { error: { message: err instanceof Error ? err.message : String(err) } };
|
|
962
850
|
}
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
851
|
+
}
|
|
852
|
+
async function emitToolCallTerminalResult(toolCall, runnable, message, emit, result) {
|
|
853
|
+
const { partIndex, ...pendingPart } = toolCall;
|
|
854
|
+
const { time } = pendingPart;
|
|
855
|
+
await emitEvent(emit, {
|
|
856
|
+
type: "tool.execution.end",
|
|
857
|
+
...runnable,
|
|
858
|
+
...result
|
|
966
859
|
});
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
860
|
+
const part = {
|
|
861
|
+
...pendingPart,
|
|
862
|
+
...result,
|
|
863
|
+
time: {
|
|
864
|
+
start: time.start,
|
|
865
|
+
end: Date.now()
|
|
866
|
+
}
|
|
867
|
+
};
|
|
868
|
+
message.parts[partIndex] = part;
|
|
869
|
+
await emitEvent(emit, {
|
|
870
|
+
type: "message.part.start",
|
|
871
|
+
message,
|
|
872
|
+
partIndex,
|
|
873
|
+
part
|
|
874
|
+
});
|
|
875
|
+
await emitEvent(emit, {
|
|
876
|
+
type: "message.part.end",
|
|
877
|
+
message,
|
|
878
|
+
partIndex,
|
|
879
|
+
part
|
|
880
|
+
});
|
|
881
|
+
await emitEvent(emit, {
|
|
882
|
+
type: "message.update",
|
|
883
|
+
message
|
|
970
884
|
});
|
|
971
885
|
}
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
886
|
+
async function finalizeExecutedToolCall(config, currentContext, toolCall, runnable, message, emit, result, signal) {
|
|
887
|
+
if (config.afterToolExecution) {
|
|
888
|
+
const afterResult = await config.afterToolExecution({
|
|
889
|
+
context: currentContext,
|
|
890
|
+
assistantMessage: message,
|
|
891
|
+
toolCall: runnable,
|
|
892
|
+
result
|
|
893
|
+
}, signal);
|
|
894
|
+
if (afterResult) {
|
|
895
|
+
await emitToolCallTerminalResult(toolCall, runnable, message, emit, afterResult);
|
|
896
|
+
return;
|
|
897
|
+
}
|
|
983
898
|
}
|
|
899
|
+
await emitToolCallTerminalResult(toolCall, runnable, message, emit, result);
|
|
984
900
|
}
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
901
|
+
/**
|
|
902
|
+
* Main core loop shared by run and runContinue.
|
|
903
|
+
*/
|
|
904
|
+
async function runLoop(config, currentContext, newMessages, emit, streamFn, signal) {
|
|
905
|
+
let firstTurn = true;
|
|
906
|
+
let pendingMessages = await config.getSteeringMessages?.() || [];
|
|
907
|
+
while (true) {
|
|
908
|
+
let hasMoreToolCalls = true;
|
|
909
|
+
while (hasMoreToolCalls || pendingMessages.length > 0) {
|
|
910
|
+
if (!firstTurn) await emitEvent(emit, { type: "turn.start" });
|
|
911
|
+
else firstTurn = false;
|
|
912
|
+
if (pendingMessages.length > 0) {
|
|
913
|
+
for (const message of pendingMessages) {
|
|
914
|
+
await emitEvent(emit, {
|
|
915
|
+
type: "message.start",
|
|
916
|
+
message
|
|
917
|
+
});
|
|
918
|
+
for (const [partIndex, part] of message.parts.entries()) {
|
|
919
|
+
await emitEvent(emit, {
|
|
920
|
+
type: "message.part.start",
|
|
921
|
+
message,
|
|
922
|
+
partIndex,
|
|
923
|
+
part
|
|
924
|
+
});
|
|
925
|
+
await emitEvent(emit, {
|
|
926
|
+
type: "message.part.end",
|
|
927
|
+
message,
|
|
928
|
+
partIndex,
|
|
929
|
+
part
|
|
930
|
+
});
|
|
931
|
+
await emitEvent(emit, {
|
|
932
|
+
type: "message.update",
|
|
933
|
+
message
|
|
934
|
+
});
|
|
935
|
+
}
|
|
936
|
+
await emitEvent(emit, {
|
|
937
|
+
type: "message.end",
|
|
938
|
+
message
|
|
939
|
+
});
|
|
940
|
+
currentContext.messages.push(message);
|
|
941
|
+
newMessages.push(message);
|
|
942
|
+
}
|
|
943
|
+
pendingMessages = [];
|
|
944
|
+
}
|
|
945
|
+
const message = await streamAssistantResponse(config, currentContext, emit, streamFn, signal);
|
|
946
|
+
if (message.stopReason === "error" || message.stopReason === "aborted") {
|
|
947
|
+
currentContext.messages.push(message);
|
|
948
|
+
newMessages.push(message);
|
|
949
|
+
await emitEvent(emit, {
|
|
950
|
+
type: "turn.end",
|
|
951
|
+
message
|
|
952
|
+
});
|
|
953
|
+
await emitEvent(emit, {
|
|
954
|
+
type: "agent.end",
|
|
955
|
+
messages: newMessages
|
|
956
|
+
});
|
|
957
|
+
return;
|
|
958
|
+
}
|
|
959
|
+
const toolCalls = message.parts.map((part, index) => ({
|
|
960
|
+
...part,
|
|
961
|
+
partIndex: index
|
|
962
|
+
})).filter((part) => part.type === "toolCall" && part.status === "pending");
|
|
963
|
+
hasMoreToolCalls = toolCalls.length > 0;
|
|
964
|
+
if (hasMoreToolCalls) await executeToolCalls(config, currentContext, message, toolCalls, emit, signal);
|
|
965
|
+
currentContext.messages.push(message);
|
|
966
|
+
newMessages.push(message);
|
|
967
|
+
await emitEvent(emit, {
|
|
968
|
+
type: "turn.end",
|
|
969
|
+
message
|
|
970
|
+
});
|
|
971
|
+
pendingMessages = await config.getSteeringMessages?.() || [];
|
|
998
972
|
}
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
case "refusal":
|
|
1010
|
-
case "sensitive": return "error";
|
|
1011
|
-
default: throw new Error("Unhandled Anthropic stop reason");
|
|
1012
|
-
}
|
|
1013
|
-
}
|
|
1014
|
-
function buildParams(model, context, isOAuthToken, options) {
|
|
1015
|
-
const { cacheControl } = getCacheControl(model.baseUrl, options?.cacheRetention);
|
|
1016
|
-
const params = {
|
|
1017
|
-
model: model.id,
|
|
1018
|
-
messages: convertMessages(context.messages, model, isOAuthToken, cacheControl),
|
|
1019
|
-
max_tokens: options?.maxTokens || model.maxTokens / 3 | 0,
|
|
1020
|
-
stream: true
|
|
1021
|
-
};
|
|
1022
|
-
if (isOAuthToken) {
|
|
1023
|
-
params.system = [{
|
|
1024
|
-
type: "text",
|
|
1025
|
-
text: "You are Claude Code, Anthropic's official CLI for Claude.",
|
|
1026
|
-
...cacheControl ? { cache_control: cacheControl } : {}
|
|
1027
|
-
}];
|
|
1028
|
-
if (context.systemPrompt) params.system.push({
|
|
1029
|
-
type: "text",
|
|
1030
|
-
text: sanitizeSurrogates(context.systemPrompt),
|
|
1031
|
-
...cacheControl ? { cache_control: cacheControl } : {}
|
|
973
|
+
const followUpMessages = await config.getFollowUpMessages?.() || [];
|
|
974
|
+
if (followUpMessages.length > 0) {
|
|
975
|
+
pendingMessages = followUpMessages;
|
|
976
|
+
continue;
|
|
977
|
+
}
|
|
978
|
+
break;
|
|
979
|
+
}
|
|
980
|
+
await emitEvent(emit, {
|
|
981
|
+
type: "agent.end",
|
|
982
|
+
messages: newMessages
|
|
1032
983
|
});
|
|
1033
|
-
} else if (context.systemPrompt) params.system = [{
|
|
1034
|
-
type: "text",
|
|
1035
|
-
text: sanitizeSurrogates(context.systemPrompt),
|
|
1036
|
-
...cacheControl ? { cache_control: cacheControl } : {}
|
|
1037
|
-
}];
|
|
1038
|
-
if (options?.temperature !== void 0 && !options?.thinkingEnabled) params.temperature = options.temperature;
|
|
1039
|
-
if (context.tools) params.tools = convertTools(context.tools, isOAuthToken);
|
|
1040
|
-
if (options?.thinkingEnabled && model.reasoning) params.thinking = {
|
|
1041
|
-
type: "enabled",
|
|
1042
|
-
budget_tokens: options.thinkingBudgetTokens || 1024
|
|
1043
|
-
};
|
|
1044
|
-
if (options?.metadata) {
|
|
1045
|
-
const userId = options.metadata.user_id;
|
|
1046
|
-
if (typeof userId === "string") params.metadata = { user_id: userId };
|
|
1047
984
|
}
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
cost: {
|
|
1067
|
-
input: 0,
|
|
1068
|
-
output: 0,
|
|
1069
|
-
cacheRead: 0,
|
|
1070
|
-
cacheWrite: 0,
|
|
1071
|
-
total: 0
|
|
1072
|
-
}
|
|
1073
|
-
},
|
|
1074
|
-
stopReason: "stop",
|
|
1075
|
-
time: {
|
|
1076
|
-
created: Date.now(),
|
|
1077
|
-
completed: Date.now()
|
|
1078
|
-
}
|
|
1079
|
-
});
|
|
1080
|
-
try {
|
|
1081
|
-
let client;
|
|
1082
|
-
let isOAuthToken;
|
|
1083
|
-
if (options?.client) {
|
|
1084
|
-
client = options.client;
|
|
1085
|
-
isOAuthToken = false;
|
|
1086
|
-
} else {
|
|
1087
|
-
const apiKey = options?.apiKey ?? getEnvApiKey(model.provider);
|
|
1088
|
-
if (!apiKey) throw new Error(`No API key for provider: ${model.provider.id}`);
|
|
1089
|
-
const created = createClient(model, apiKey, options?.interleavedThinking ?? true, options?.headers);
|
|
1090
|
-
client = created.client;
|
|
1091
|
-
isOAuthToken = created.isOAuthToken;
|
|
1092
|
-
}
|
|
1093
|
-
let params = buildParams(model, context, isOAuthToken, options);
|
|
1094
|
-
const nextParams = await options?.onPayload?.(params, model);
|
|
1095
|
-
if (nextParams !== void 0) params = nextParams;
|
|
1096
|
-
const anthropicStream = client.messages.stream({
|
|
1097
|
-
...params,
|
|
1098
|
-
stream: true
|
|
1099
|
-
}, { signal: options?.signal });
|
|
1100
|
-
stream.push({
|
|
1101
|
-
type: "start",
|
|
1102
|
-
partial: output
|
|
985
|
+
async function runAgentLoop(config, context, prompts, emit, streamFn, signal) {
|
|
986
|
+
/**
|
|
987
|
+
* maintain 2 arrays to represent state, `context` the actual full state sent to the LLM,
|
|
988
|
+
* `newMessages` for intermediate current loop state.
|
|
989
|
+
* think of newMessages as subset of context.messages for the running current loop.
|
|
990
|
+
* they do move at the same speed staring with initial user prompts.
|
|
991
|
+
* */
|
|
992
|
+
const newMessages = [...prompts];
|
|
993
|
+
const currentContext = {
|
|
994
|
+
...context,
|
|
995
|
+
messages: [...context.messages, ...prompts]
|
|
996
|
+
};
|
|
997
|
+
await emitEvent(emit, { type: "agent.start" });
|
|
998
|
+
await emitEvent(emit, { type: "turn.start" });
|
|
999
|
+
for (const prompt of prompts) {
|
|
1000
|
+
await emitEvent(emit, {
|
|
1001
|
+
type: "message.start",
|
|
1002
|
+
message: prompt
|
|
1103
1003
|
});
|
|
1104
|
-
const
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
text: "",
|
|
1118
|
-
index: event.index
|
|
1119
|
-
};
|
|
1120
|
-
output.parts.push(block);
|
|
1121
|
-
stream.push({
|
|
1122
|
-
type: "text.start",
|
|
1123
|
-
partIndex: output.parts.length - 1,
|
|
1124
|
-
partial: output
|
|
1125
|
-
});
|
|
1126
|
-
} else if (event.content_block.type === "thinking") {
|
|
1127
|
-
const block = {
|
|
1128
|
-
type: "thinking",
|
|
1129
|
-
thinking: "",
|
|
1130
|
-
thinkingSignature: "",
|
|
1131
|
-
index: event.index
|
|
1132
|
-
};
|
|
1133
|
-
output.parts.push(block);
|
|
1134
|
-
stream.push({
|
|
1135
|
-
type: "thinking.start",
|
|
1136
|
-
partIndex: output.parts.length - 1,
|
|
1137
|
-
partial: output
|
|
1138
|
-
});
|
|
1139
|
-
} else if (event.content_block.type === "redacted_thinking") {
|
|
1140
|
-
const block = {
|
|
1141
|
-
type: "thinking",
|
|
1142
|
-
thinking: "[Reasoning redacted]",
|
|
1143
|
-
thinkingSignature: event.content_block.data,
|
|
1144
|
-
redacted: true,
|
|
1145
|
-
index: event.index
|
|
1146
|
-
};
|
|
1147
|
-
output.parts.push(block);
|
|
1148
|
-
stream.push({
|
|
1149
|
-
type: "thinking.start",
|
|
1150
|
-
partIndex: output.parts.length - 1,
|
|
1151
|
-
partial: output
|
|
1152
|
-
});
|
|
1153
|
-
} else if (event.content_block.type === "tool_use") {
|
|
1154
|
-
const block = {
|
|
1155
|
-
type: "toolCall",
|
|
1156
|
-
callID: event.content_block.id,
|
|
1157
|
-
name: isOAuthToken ? fromClaudeCodeName(event.content_block.name, context.tools) : event.content_block.name,
|
|
1158
|
-
arguments: event.content_block.input ?? {},
|
|
1159
|
-
partialJson: "",
|
|
1160
|
-
status: "pending",
|
|
1161
|
-
time: {
|
|
1162
|
-
start: Date.now(),
|
|
1163
|
-
end: Date.now()
|
|
1164
|
-
},
|
|
1165
|
-
index: event.index
|
|
1166
|
-
};
|
|
1167
|
-
output.parts.push(block);
|
|
1168
|
-
stream.push({
|
|
1169
|
-
type: "toolcall.start",
|
|
1170
|
-
partIndex: output.parts.length - 1,
|
|
1171
|
-
partial: output
|
|
1172
|
-
});
|
|
1173
|
-
}
|
|
1174
|
-
} else if (event.type === "content_block_delta") {
|
|
1175
|
-
const partIndex = blocks.findIndex((b) => b.index === event.index);
|
|
1176
|
-
const block = blocks[partIndex];
|
|
1177
|
-
if (!block) continue;
|
|
1178
|
-
if (event.delta.type === "text_delta" && block.type === "text") {
|
|
1179
|
-
block.text += event.delta.text;
|
|
1180
|
-
stream.push({
|
|
1181
|
-
type: "text.delta",
|
|
1182
|
-
partIndex,
|
|
1183
|
-
delta: event.delta.text,
|
|
1184
|
-
partial: output
|
|
1185
|
-
});
|
|
1186
|
-
} else if (event.delta.type === "thinking_delta" && block.type === "thinking") {
|
|
1187
|
-
block.thinking += event.delta.thinking;
|
|
1188
|
-
stream.push({
|
|
1189
|
-
type: "thinking.delta",
|
|
1190
|
-
partIndex,
|
|
1191
|
-
delta: event.delta.thinking,
|
|
1192
|
-
partial: output
|
|
1193
|
-
});
|
|
1194
|
-
} else if (event.delta.type === "input_json_delta" && block.type === "toolCall") {
|
|
1195
|
-
block.partialJson += event.delta.partial_json;
|
|
1196
|
-
block.arguments = parseStreamingJson(block.partialJson);
|
|
1197
|
-
stream.push({
|
|
1198
|
-
type: "toolcall.delta",
|
|
1199
|
-
partIndex,
|
|
1200
|
-
delta: event.delta.partial_json,
|
|
1201
|
-
partial: output
|
|
1202
|
-
});
|
|
1203
|
-
} else if (event.delta.type === "signature_delta" && block.type === "thinking") {
|
|
1204
|
-
block.thinkingSignature = block.thinkingSignature || "";
|
|
1205
|
-
block.thinkingSignature += event.delta.signature;
|
|
1206
|
-
}
|
|
1207
|
-
} else if (event.type === "content_block_stop") {
|
|
1208
|
-
const partIndex = blocks.findIndex((b) => b.index === event.index);
|
|
1209
|
-
const block = blocks[partIndex];
|
|
1210
|
-
if (block) {
|
|
1211
|
-
delete block.index;
|
|
1212
|
-
if (block.type === "text") stream.push({
|
|
1213
|
-
type: "text.end",
|
|
1214
|
-
partIndex,
|
|
1215
|
-
content: block.text,
|
|
1216
|
-
partial: output
|
|
1217
|
-
});
|
|
1218
|
-
else if (block.type === "thinking") stream.push({
|
|
1219
|
-
type: "thinking.end",
|
|
1220
|
-
partIndex,
|
|
1221
|
-
content: block.thinking,
|
|
1222
|
-
partial: output
|
|
1223
|
-
});
|
|
1224
|
-
else if (block.type === "toolCall") {
|
|
1225
|
-
block.arguments = parseStreamingJson(block.partialJson);
|
|
1226
|
-
delete block.partialJson;
|
|
1227
|
-
stream.push({
|
|
1228
|
-
type: "toolcall.end",
|
|
1229
|
-
partIndex,
|
|
1230
|
-
toolCall: block,
|
|
1231
|
-
partial: output
|
|
1232
|
-
});
|
|
1233
|
-
}
|
|
1234
|
-
}
|
|
1235
|
-
} else if (event.type === "message_delta") {
|
|
1236
|
-
if (event.delta.stop_reason) output.stopReason = mapStopReason(event.delta.stop_reason);
|
|
1237
|
-
if (event.usage.input_tokens != null) output.usage.input = event.usage.input_tokens;
|
|
1238
|
-
if (event.usage.output_tokens != null) output.usage.output = event.usage.output_tokens;
|
|
1239
|
-
if (event.usage.cache_read_input_tokens != null) output.usage.cacheRead = event.usage.cache_read_input_tokens;
|
|
1240
|
-
if (event.usage.cache_creation_input_tokens != null) output.usage.cacheWrite = event.usage.cache_creation_input_tokens;
|
|
1241
|
-
output.usage.totalTokens = output.usage.input + output.usage.output + output.usage.cacheRead + output.usage.cacheWrite;
|
|
1242
|
-
Model.calculateCost(model, output.usage);
|
|
1004
|
+
for (const [partIndex, part] of prompt.parts.entries()) {
|
|
1005
|
+
await emitEvent(emit, {
|
|
1006
|
+
type: "message.part.start",
|
|
1007
|
+
message: prompt,
|
|
1008
|
+
partIndex,
|
|
1009
|
+
part
|
|
1010
|
+
});
|
|
1011
|
+
await emitEvent(emit, {
|
|
1012
|
+
type: "message.part.end",
|
|
1013
|
+
message: prompt,
|
|
1014
|
+
partIndex,
|
|
1015
|
+
part
|
|
1016
|
+
});
|
|
1243
1017
|
}
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
stream.push({
|
|
1248
|
-
type: "done",
|
|
1249
|
-
reason: output.stopReason,
|
|
1250
|
-
message: output
|
|
1251
|
-
});
|
|
1252
|
-
stream.end();
|
|
1253
|
-
} catch (error) {
|
|
1254
|
-
for (const block of output.parts) delete block.index;
|
|
1255
|
-
output.time.completed = Date.now();
|
|
1256
|
-
output.stopReason = options?.signal?.aborted ? "aborted" : "error";
|
|
1257
|
-
output.errorMessage = error instanceof Error ? error.message : JSON.stringify(error);
|
|
1258
|
-
stream.push({
|
|
1259
|
-
type: "error",
|
|
1260
|
-
reason: output.stopReason,
|
|
1261
|
-
error: output
|
|
1018
|
+
await emitEvent(emit, {
|
|
1019
|
+
type: "message.end",
|
|
1020
|
+
message: prompt
|
|
1262
1021
|
});
|
|
1263
|
-
stream.end();
|
|
1264
1022
|
}
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
}
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
...
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1023
|
+
await runLoop(config, currentContext, newMessages, emit, streamFn, signal);
|
|
1024
|
+
return newMessages;
|
|
1025
|
+
}
|
|
1026
|
+
_Loop.runAgentLoop = runAgentLoop;
|
|
1027
|
+
async function runAgentLoopContinue(config, context, emit, streamFn, signal) {
|
|
1028
|
+
if (context.messages.length === 0) throw new AgentContextError({ message: "connot continue: no messages in context" });
|
|
1029
|
+
if (context.messages[context.messages.length - 1]?.role === "assistant") throw new AgentContextError({ message: "cannot continue from message role: assistant" });
|
|
1030
|
+
const newMessages = [];
|
|
1031
|
+
const currentContext = { ...context };
|
|
1032
|
+
await emitEvent(emit, { type: "agent.start" });
|
|
1033
|
+
await emitEvent(emit, { type: "turn.start" });
|
|
1034
|
+
await runLoop(config, currentContext, newMessages, emit, streamFn, signal);
|
|
1035
|
+
return newMessages;
|
|
1036
|
+
}
|
|
1037
|
+
_Loop.runAgentLoopContinue = runAgentLoopContinue;
|
|
1038
|
+
function run(config, context, prompts, streamFn, signal) {
|
|
1039
|
+
const stream = createAgentStream();
|
|
1040
|
+
runAgentLoop(config, context, prompts, async (event) => {
|
|
1041
|
+
stream.push(event);
|
|
1042
|
+
}, streamFn, signal).then((messages) => {
|
|
1043
|
+
stream.end(messages);
|
|
1044
|
+
});
|
|
1045
|
+
return stream;
|
|
1046
|
+
}
|
|
1047
|
+
_Loop.run = run;
|
|
1048
|
+
function runContinue(config, context, streamFn, signal) {
|
|
1049
|
+
const stream = createAgentStream();
|
|
1050
|
+
runAgentLoopContinue(config, context, async (event) => {
|
|
1051
|
+
stream.push(event);
|
|
1052
|
+
}, streamFn, signal).then((messages) => {
|
|
1053
|
+
stream.end(messages);
|
|
1054
|
+
});
|
|
1055
|
+
return stream;
|
|
1056
|
+
}
|
|
1057
|
+
_Loop.runContinue = runContinue;
|
|
1058
|
+
})(Loop || (Loop = {}));
|
|
1292
1059
|
//#endregion
|
|
1293
|
-
//#region src/
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
allErrors: true,
|
|
1299
|
-
strict: false,
|
|
1300
|
-
coerceTypes: true
|
|
1301
|
-
});
|
|
1302
|
-
addFormats(ajv);
|
|
1303
|
-
/**
|
|
1304
|
-
* Validates tool call arguments against the tool's TypeBox schema
|
|
1305
|
-
*/
|
|
1306
|
-
function validateToolArguments(tool, toolExecution) {
|
|
1307
|
-
const validate = ajv.compile(tool.parameters);
|
|
1308
|
-
const args = structuredClone(toolExecution.rawArgs);
|
|
1309
|
-
if (validate(args)) return args;
|
|
1310
|
-
const errors = validate.errors?.map((err) => {
|
|
1311
|
-
return ` - ${err.instancePath ? err.instancePath.substring(1) : err.params?.missingProperty || "root"}: ${err.message}`;
|
|
1312
|
-
}).join("\n") || "Unknown Validation Error";
|
|
1313
|
-
throw new Error([
|
|
1314
|
-
`Validation Failed For Tool "${toolExecution.name}"`,
|
|
1315
|
-
`${errors}\n`,
|
|
1316
|
-
"Received Arguments:",
|
|
1317
|
-
`${JSON.stringify(toolExecution.rawArgs, null, 2)}\n`
|
|
1318
|
-
].join("\n"));
|
|
1319
|
-
}
|
|
1060
|
+
//#region src/agent.ts
|
|
1061
|
+
const agent = {
|
|
1062
|
+
loop: Loop.run,
|
|
1063
|
+
loopContinue: Loop.runContinue
|
|
1064
|
+
};
|
|
1320
1065
|
//#endregion
|
|
1321
|
-
//#region src/
|
|
1322
|
-
let
|
|
1323
|
-
(function(
|
|
1324
|
-
|
|
1325
|
-
|
|
1066
|
+
//#region src/message/message.ts
|
|
1067
|
+
let Message;
|
|
1068
|
+
(function(_Message) {
|
|
1069
|
+
const TextContentSchema = _Message.TextContentSchema = Type.Object({
|
|
1070
|
+
type: Type.Literal("text"),
|
|
1071
|
+
text: Type.String(),
|
|
1072
|
+
textSignature: Type.Optional(Type.String())
|
|
1073
|
+
});
|
|
1074
|
+
const ImageContentSchema = _Message.ImageContentSchema = Type.Object({
|
|
1075
|
+
type: Type.Literal("image"),
|
|
1076
|
+
data: Type.String(),
|
|
1077
|
+
mimeType: Type.String()
|
|
1078
|
+
});
|
|
1079
|
+
const ThinkingContentSchema = _Message.ThinkingContentSchema = Type.Object({
|
|
1080
|
+
type: Type.Literal("thinking"),
|
|
1081
|
+
thinking: Type.String(),
|
|
1082
|
+
thinkingSignature: Type.Optional(Type.String()),
|
|
1083
|
+
redacted: Type.Optional(Type.Boolean())
|
|
1084
|
+
});
|
|
1085
|
+
const ToolCallBaseSchema = _Message.ToolCallBaseSchema = Type.Object({
|
|
1086
|
+
type: Type.Literal("toolCall"),
|
|
1087
|
+
callID: Type.String(),
|
|
1088
|
+
name: Type.String(),
|
|
1089
|
+
arguments: Type.Record(Type.String(), Type.Any()),
|
|
1090
|
+
thoughtSignature: Type.Optional(Type.String()),
|
|
1091
|
+
time: Type.Object({
|
|
1092
|
+
start: Type.Number(),
|
|
1093
|
+
end: Type.Number()
|
|
1094
|
+
})
|
|
1095
|
+
});
|
|
1096
|
+
const ToolSuccessResult = _Message.ToolSuccessResult = Type.Object({
|
|
1097
|
+
content: Type.Array(Type.Union([TextContentSchema, ImageContentSchema])),
|
|
1098
|
+
details: Type.Optional(Type.Any()),
|
|
1099
|
+
isError: Type.Literal(false)
|
|
1100
|
+
});
|
|
1101
|
+
const ToolErrorResult = _Message.ToolErrorResult = Type.Object({
|
|
1102
|
+
content: Type.Array(Type.Union([TextContentSchema, ImageContentSchema])),
|
|
1103
|
+
details: Type.Optional(Type.Any()),
|
|
1104
|
+
isError: Type.Literal(true)
|
|
1105
|
+
});
|
|
1106
|
+
const ToolRunningPartial = _Message.ToolRunningPartial = Type.Object({
|
|
1107
|
+
content: Type.Optional(Type.Array(Type.Union([TextContentSchema, ImageContentSchema]))),
|
|
1108
|
+
details: Type.Optional(Type.Any())
|
|
1109
|
+
});
|
|
1110
|
+
const ToolStatusEnum = _Message.ToolStatusEnum = {
|
|
1111
|
+
pending: "pending",
|
|
1112
|
+
running: "running",
|
|
1113
|
+
completed: "completed",
|
|
1114
|
+
error: "error",
|
|
1115
|
+
skipped: "skipped",
|
|
1116
|
+
aborted: "aborted"
|
|
1117
|
+
};
|
|
1118
|
+
const ToolRunningSchema = _Message.ToolRunningSchema = Type.Object({
|
|
1119
|
+
status: Type.Literal(ToolStatusEnum.running),
|
|
1120
|
+
partial: Type.Optional(ToolRunningPartial)
|
|
1121
|
+
});
|
|
1122
|
+
const ToolCompletedSchema = _Message.ToolCompletedSchema = Type.Object({
|
|
1123
|
+
status: Type.Literal(ToolStatusEnum.completed),
|
|
1124
|
+
result: ToolSuccessResult
|
|
1125
|
+
});
|
|
1126
|
+
const ToolErrorSchema = _Message.ToolErrorSchema = Type.Object({
|
|
1127
|
+
status: Type.Literal(ToolStatusEnum.error),
|
|
1128
|
+
result: ToolErrorResult
|
|
1129
|
+
});
|
|
1130
|
+
const ToolSkippedSchema = _Message.ToolSkippedSchema = Type.Object({
|
|
1131
|
+
status: Type.Literal(ToolStatusEnum.skipped),
|
|
1132
|
+
result: ToolErrorResult
|
|
1133
|
+
});
|
|
1134
|
+
const ToolAbortedSchema = _Message.ToolAbortedSchema = Type.Object({
|
|
1135
|
+
status: Type.Literal(ToolStatusEnum.aborted),
|
|
1136
|
+
result: ToolErrorResult
|
|
1137
|
+
});
|
|
1138
|
+
const ToolCallPendingPart = Type.Intersect([ToolCallBaseSchema, Type.Object({ status: Type.Literal(ToolStatusEnum.pending) })]);
|
|
1139
|
+
const ToolCallRunningPart = Type.Composite([ToolCallBaseSchema, ToolRunningSchema]);
|
|
1140
|
+
const ToolCallCompletedPart = Type.Composite([ToolCallBaseSchema, ToolCompletedSchema]);
|
|
1141
|
+
const ToolCallErrorPart = Type.Composite([ToolCallBaseSchema, ToolErrorSchema]);
|
|
1142
|
+
const ToolCallSkippedPart = Type.Composite([ToolCallBaseSchema, ToolSkippedSchema]);
|
|
1143
|
+
const ToolCallAbortedPart = Type.Composite([ToolCallBaseSchema, ToolAbortedSchema]);
|
|
1144
|
+
const ToolCallSchema = _Message.ToolCallSchema = Type.Union([
|
|
1145
|
+
ToolCallPendingPart,
|
|
1146
|
+
ToolCallRunningPart,
|
|
1147
|
+
ToolCallCompletedPart,
|
|
1148
|
+
ToolCallErrorPart,
|
|
1149
|
+
ToolCallSkippedPart,
|
|
1150
|
+
ToolCallAbortedPart
|
|
1151
|
+
]);
|
|
1152
|
+
const UsageSchema = _Message.UsageSchema = Type.Object({
|
|
1153
|
+
input: Type.Number(),
|
|
1154
|
+
output: Type.Number(),
|
|
1155
|
+
cacheRead: Type.Number(),
|
|
1156
|
+
cacheWrite: Type.Number(),
|
|
1157
|
+
totalTokens: Type.Number(),
|
|
1158
|
+
cost: Type.Object({
|
|
1159
|
+
input: Type.Number(),
|
|
1160
|
+
output: Type.Number(),
|
|
1161
|
+
cacheRead: Type.Number(),
|
|
1162
|
+
cacheWrite: Type.Number(),
|
|
1163
|
+
total: Type.Number()
|
|
1164
|
+
})
|
|
1165
|
+
});
|
|
1166
|
+
const StopReasonSchema = _Message.StopReasonSchema = Type.Union([
|
|
1167
|
+
Type.Literal("stop"),
|
|
1168
|
+
Type.Literal("length"),
|
|
1169
|
+
Type.Literal("toolUse"),
|
|
1170
|
+
Type.Literal("error"),
|
|
1171
|
+
Type.Literal("aborted")
|
|
1172
|
+
]);
|
|
1173
|
+
const UserMessageSchema = _Message.UserMessageSchema = Type.Object({
|
|
1174
|
+
messageId: Type.String(),
|
|
1175
|
+
role: Type.Literal("user"),
|
|
1176
|
+
time: Type.Object({ created: Type.Number() }),
|
|
1177
|
+
parts: Type.Array(Type.Union([TextContentSchema, ImageContentSchema]))
|
|
1178
|
+
});
|
|
1179
|
+
const AssistantMessageSchema = _Message.AssistantMessageSchema = Type.Object({
|
|
1180
|
+
role: Type.Literal("assistant"),
|
|
1181
|
+
protocol: Model.KnownProtocolSchema,
|
|
1182
|
+
provider: Provider.Info,
|
|
1183
|
+
model: Type.String(),
|
|
1184
|
+
usage: UsageSchema,
|
|
1185
|
+
stopReason: StopReasonSchema,
|
|
1186
|
+
errorMessage: Type.Optional(Type.String()),
|
|
1187
|
+
time: Type.Object({
|
|
1188
|
+
created: Type.Number(),
|
|
1189
|
+
completed: Type.Number()
|
|
1190
|
+
}),
|
|
1191
|
+
parts: Type.Array(Type.Union([
|
|
1192
|
+
TextContentSchema,
|
|
1193
|
+
ImageContentSchema,
|
|
1194
|
+
ThinkingContentSchema,
|
|
1195
|
+
ToolCallSchema
|
|
1196
|
+
])),
|
|
1197
|
+
responseId: Type.Optional(Type.String()),
|
|
1198
|
+
messageId: Type.String()
|
|
1199
|
+
});
|
|
1200
|
+
const MessageSchema = _Message.MessageSchema = Type.Union([UserMessageSchema, AssistantMessageSchema]);
|
|
1201
|
+
function createMessageId() {
|
|
1202
|
+
return uuidv7();
|
|
1326
1203
|
}
|
|
1327
|
-
|
|
1328
|
-
function
|
|
1329
|
-
|
|
1204
|
+
_Message.createMessageId = createMessageId;
|
|
1205
|
+
function createUserMessage(message) {
|
|
1206
|
+
const { messageId = createMessageId(), ...rest } = message;
|
|
1207
|
+
return {
|
|
1208
|
+
messageId,
|
|
1209
|
+
...rest
|
|
1210
|
+
};
|
|
1330
1211
|
}
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
const
|
|
1334
|
-
if (!part) throw new Error(`Assistant message part at index ${partIndex} was not found`);
|
|
1212
|
+
_Message.createUserMessage = createUserMessage;
|
|
1213
|
+
function createAssistantMessage(message) {
|
|
1214
|
+
const { messageId = createMessageId(), ...rest } = message;
|
|
1335
1215
|
return {
|
|
1336
|
-
|
|
1337
|
-
|
|
1216
|
+
messageId,
|
|
1217
|
+
...rest
|
|
1338
1218
|
};
|
|
1339
1219
|
}
|
|
1340
|
-
|
|
1341
|
-
|
|
1220
|
+
_Message.createAssistantMessage = createAssistantMessage;
|
|
1221
|
+
const ToolSchema = _Message.ToolSchema = Type.Object({
|
|
1222
|
+
name: Type.String(),
|
|
1223
|
+
description: Type.String(),
|
|
1224
|
+
parameters: Type.Unsafe()
|
|
1225
|
+
});
|
|
1226
|
+
function defineTool(tool) {
|
|
1227
|
+
return tool;
|
|
1342
1228
|
}
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
const
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1229
|
+
_Message.defineTool = defineTool;
|
|
1230
|
+
_Message.ContextSchema = Type.Object({
|
|
1231
|
+
systemPrompt: Type.Optional(Type.String()),
|
|
1232
|
+
messages: Type.Array(MessageSchema),
|
|
1233
|
+
tools: Type.Optional(Type.Array(ToolSchema))
|
|
1234
|
+
});
|
|
1235
|
+
function transformMessages(messages, model, normalizeToolCallId) {
|
|
1236
|
+
const toolCallIDMap = /* @__PURE__ */ new Map();
|
|
1237
|
+
const transformed = [];
|
|
1238
|
+
for (const msg of messages) {
|
|
1239
|
+
if (msg.role === "user") {
|
|
1240
|
+
transformed.push(msg);
|
|
1241
|
+
continue;
|
|
1242
|
+
}
|
|
1243
|
+
const assistantMsg = msg;
|
|
1244
|
+
const isSameModel = assistantMsg.provider.id === model.provider.id && assistantMsg.protocol === model.protocol && assistantMsg.model === model.id;
|
|
1245
|
+
const parts = assistantMsg.parts.flatMap((block) => {
|
|
1246
|
+
if (block.type === "thinking") {
|
|
1247
|
+
if (isSameModel && block.thinkingSignature) return block;
|
|
1248
|
+
if (!block.thinking || block.thinking.trim() === "") return [];
|
|
1249
|
+
if (isSameModel) return block;
|
|
1250
|
+
return {
|
|
1251
|
+
type: "text",
|
|
1252
|
+
text: block.thinking
|
|
1253
|
+
};
|
|
1254
|
+
}
|
|
1255
|
+
if (block.type === "text") {
|
|
1256
|
+
if (isSameModel) return block;
|
|
1257
|
+
return {
|
|
1258
|
+
type: "text",
|
|
1259
|
+
text: block.text
|
|
1260
|
+
};
|
|
1261
|
+
}
|
|
1262
|
+
if (block.type === "toolCall") {
|
|
1263
|
+
const toolCall = block;
|
|
1264
|
+
let normalizedToolCall = toolCall;
|
|
1265
|
+
if (!isSameModel && toolCall.thoughtSignature) {
|
|
1266
|
+
normalizedToolCall = { ...toolCall };
|
|
1267
|
+
delete normalizedToolCall.thoughtSignature;
|
|
1268
|
+
}
|
|
1269
|
+
const normalizedID = toolCallIDMap.get(block.callID);
|
|
1270
|
+
if (normalizedID && normalizedID !== block.callID) normalizedToolCall = {
|
|
1271
|
+
...normalizedToolCall,
|
|
1272
|
+
callID: normalizedID
|
|
1273
|
+
};
|
|
1274
|
+
else if (!isSameModel && normalizeToolCallId) {
|
|
1275
|
+
const nextID = normalizeToolCallId(toolCall.callID, model, assistantMsg);
|
|
1276
|
+
if (nextID !== toolCall.callID) {
|
|
1277
|
+
toolCallIDMap.set(toolCall.callID, nextID);
|
|
1278
|
+
normalizedToolCall = {
|
|
1279
|
+
...normalizedToolCall,
|
|
1280
|
+
callID: nextID
|
|
1281
|
+
};
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
return normalizedToolCall;
|
|
1285
|
+
}
|
|
1286
|
+
return block;
|
|
1388
1287
|
});
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
type: "message.part.end",
|
|
1393
|
-
partIndex,
|
|
1394
|
-
...snapshotAssistantPartEvent(message, partIndex)
|
|
1288
|
+
transformed.push({
|
|
1289
|
+
...assistantMsg,
|
|
1290
|
+
parts
|
|
1395
1291
|
});
|
|
1396
|
-
};
|
|
1397
|
-
for await (const event of events) switch (event.type) {
|
|
1398
|
-
case "start":
|
|
1399
|
-
partialMessage = event.partial;
|
|
1400
|
-
await ensureMessageStarted(partialMessage);
|
|
1401
|
-
break;
|
|
1402
|
-
case "text.start":
|
|
1403
|
-
case "thinking.start":
|
|
1404
|
-
case "toolcall.start":
|
|
1405
|
-
partialMessage = event.partial;
|
|
1406
|
-
await ensureMessageStarted(partialMessage);
|
|
1407
|
-
await emitPartStart(partialMessage, event.partIndex);
|
|
1408
|
-
break;
|
|
1409
|
-
case "text.delta":
|
|
1410
|
-
case "thinking.delta":
|
|
1411
|
-
case "toolcall.delta":
|
|
1412
|
-
partialMessage = event.partial;
|
|
1413
|
-
await ensureMessageStarted(partialMessage);
|
|
1414
|
-
await emitPartUpdate(partialMessage, event.partIndex);
|
|
1415
|
-
break;
|
|
1416
|
-
case "text.end":
|
|
1417
|
-
case "thinking.end":
|
|
1418
|
-
case "toolcall.end":
|
|
1419
|
-
partialMessage = event.partial;
|
|
1420
|
-
await ensureMessageStarted(partialMessage);
|
|
1421
|
-
await emitPartEnd(partialMessage, event.partIndex);
|
|
1422
|
-
await emitMessageUpdate(partialMessage);
|
|
1423
|
-
break;
|
|
1424
|
-
case "done":
|
|
1425
|
-
await ensureMessageStarted(event.message);
|
|
1426
|
-
await emitEvent(emit, {
|
|
1427
|
-
type: "message.end",
|
|
1428
|
-
message: snapshotAssistantMessage(event.message)
|
|
1429
|
-
});
|
|
1430
|
-
return event.message;
|
|
1431
|
-
case "error":
|
|
1432
|
-
await ensureMessageStarted(event.error);
|
|
1433
|
-
await emitEvent(emit, {
|
|
1434
|
-
type: "message.end",
|
|
1435
|
-
message: snapshotAssistantMessage(event.error)
|
|
1436
|
-
});
|
|
1437
|
-
return event.error;
|
|
1438
1292
|
}
|
|
1439
|
-
|
|
1440
|
-
await ensureMessageStarted(finalMessage);
|
|
1441
|
-
await emitEvent(emit, {
|
|
1442
|
-
type: "message.end",
|
|
1443
|
-
message: snapshotAssistantMessage(finalMessage)
|
|
1444
|
-
});
|
|
1445
|
-
return finalMessage;
|
|
1446
|
-
}
|
|
1447
|
-
async function executeToolCalls(config, currentContext, message, toolCalls, emit, signal) {
|
|
1448
|
-
if (config.toolExecution === "sequential") return executeToolCallsSequential(config, currentContext, message, toolCalls, emit, signal);
|
|
1449
|
-
return executeToolCallsParallel(config, currentContext, message, toolCalls, emit, signal);
|
|
1293
|
+
return transformed;
|
|
1450
1294
|
}
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
};
|
|
1475
|
-
await emitToolCallTerminalResult(toolCall, prepared.runnable, message, emit, error);
|
|
1476
|
-
} else {
|
|
1477
|
-
const runnable = {
|
|
1478
|
-
toolCall,
|
|
1479
|
-
runnable: prepared.runnable,
|
|
1480
|
-
tool: prepared.tool
|
|
1481
|
-
};
|
|
1482
|
-
const executed = await executeRunnableToolCall(runnable, message, emit, signal);
|
|
1483
|
-
if (executed.error) {
|
|
1484
|
-
const error = {
|
|
1485
|
-
status: "error",
|
|
1486
|
-
result: {
|
|
1487
|
-
content: [{
|
|
1488
|
-
type: "text",
|
|
1489
|
-
text: executed.error.message
|
|
1490
|
-
}],
|
|
1491
|
-
details: executed.error.details,
|
|
1492
|
-
isError: true
|
|
1493
|
-
}
|
|
1494
|
-
};
|
|
1495
|
-
await emitToolCallTerminalResult(runnable.toolCall, runnable.runnable, message, emit, error);
|
|
1496
|
-
} else await finalizeExecutedToolCall(config, currentContext, runnable.toolCall, runnable.runnable, message, emit, executed.result);
|
|
1497
|
-
}
|
|
1295
|
+
_Message.transformMessages = transformMessages;
|
|
1296
|
+
})(Message || (Message = {}));
|
|
1297
|
+
//#endregion
|
|
1298
|
+
//#region src/utils/jsonparse.ts
|
|
1299
|
+
/**
|
|
1300
|
+
* @description Helper function to parse partial jsons during LLM streaming.
|
|
1301
|
+
*/
|
|
1302
|
+
/**
|
|
1303
|
+
* Attempts to parse potentially incomplete JSON during streaming.
|
|
1304
|
+
* Always returns a valid object, even if the JSON is incomplete.
|
|
1305
|
+
*
|
|
1306
|
+
* @param partialJson The partial JSON string from streaming
|
|
1307
|
+
* @returns Parsed object or empty object if parsing fails
|
|
1308
|
+
*/
|
|
1309
|
+
function parseStreamingJson(partialJson) {
|
|
1310
|
+
if (!partialJson || partialJson.trim() === "") return {};
|
|
1311
|
+
try {
|
|
1312
|
+
return JSON.parse(partialJson);
|
|
1313
|
+
} catch {
|
|
1314
|
+
try {
|
|
1315
|
+
return parse(partialJson) ?? {};
|
|
1316
|
+
} catch {
|
|
1317
|
+
return {};
|
|
1498
1318
|
}
|
|
1499
1319
|
}
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
text: executed.error.message
|
|
1546
|
-
}],
|
|
1547
|
-
details: executed.error.details,
|
|
1548
|
-
isError: true
|
|
1549
|
-
}
|
|
1550
|
-
};
|
|
1551
|
-
await emitToolCallTerminalResult(runnable.toolCall, runnable.runnable, message, emit, error);
|
|
1552
|
-
} else await finalizeExecutedToolCall(config, currentContext, runnable.toolCall, runnable.runnable, message, emit, executed.result);
|
|
1320
|
+
}
|
|
1321
|
+
//#endregion
|
|
1322
|
+
//#region src/utils/sanitize.ts
|
|
1323
|
+
/**
|
|
1324
|
+
* Removes unpaired Unicode surrogate characters from a string.
|
|
1325
|
+
*
|
|
1326
|
+
* Unpaired surrogates (high surrogates 0xD800-0xDBFF without matching low surrogates 0xDC00-0xDFFF,
|
|
1327
|
+
* or vice versa) cause JSON serialization errors in many API providers.
|
|
1328
|
+
*
|
|
1329
|
+
* Valid emoji and other characters outside the Basic Multilingual Plane use properly paired
|
|
1330
|
+
* surrogates and will NOT be affected by this function.
|
|
1331
|
+
*
|
|
1332
|
+
* @param text - The text to sanitize
|
|
1333
|
+
* @returns The sanitized text with unpaired surrogates removed
|
|
1334
|
+
*
|
|
1335
|
+
* @example
|
|
1336
|
+
* // Valid emoji (properly paired surrogates) are preserved
|
|
1337
|
+
* sanitizeSurrogates("Hello 🙈 World") // => "Hello 🙈 World"
|
|
1338
|
+
*
|
|
1339
|
+
* // Unpaired high surrogate is removed
|
|
1340
|
+
* const unpaired = String.fromCharCode(0xD83D); // high surrogate without low
|
|
1341
|
+
* sanitizeSurrogates(`Text ${unpaired} here`) // => "Text here"
|
|
1342
|
+
*/
|
|
1343
|
+
function sanitizeSurrogates(text) {
|
|
1344
|
+
return text.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "");
|
|
1345
|
+
}
|
|
1346
|
+
//#endregion
|
|
1347
|
+
//#region src/provider/providers/utils.ts
|
|
1348
|
+
function getEnvApiKey(_provider) {}
|
|
1349
|
+
//#endregion
|
|
1350
|
+
//#region src/provider/providers/anthropic/index.ts
|
|
1351
|
+
function resolveCacheRetention(cacheRetention) {
|
|
1352
|
+
if (cacheRetention) return cacheRetention;
|
|
1353
|
+
if (typeof process !== "undefined" && process.env.CODEWORK_CACHE_RETENTION === "long") return "long";
|
|
1354
|
+
return "short";
|
|
1355
|
+
}
|
|
1356
|
+
function getCacheControl(baseUrl, cacheRetention) {
|
|
1357
|
+
const retention = resolveCacheRetention(cacheRetention);
|
|
1358
|
+
if (retention === "none") return { retention };
|
|
1359
|
+
const ttl = retention === "long" && baseUrl.includes("api.anthropic.com") ? "1h" : void 0;
|
|
1360
|
+
return {
|
|
1361
|
+
retention,
|
|
1362
|
+
cacheControl: {
|
|
1363
|
+
type: "ephemeral",
|
|
1364
|
+
...ttl && { ttl }
|
|
1553
1365
|
}
|
|
1366
|
+
};
|
|
1367
|
+
}
|
|
1368
|
+
const claudeCodeVersion = "2.1.2";
|
|
1369
|
+
const ccToolLookup = new Map([
|
|
1370
|
+
"Read",
|
|
1371
|
+
"Write",
|
|
1372
|
+
"Edit",
|
|
1373
|
+
"Bash",
|
|
1374
|
+
"Grep",
|
|
1375
|
+
"Glob",
|
|
1376
|
+
"AskUserQuestion",
|
|
1377
|
+
"EnterPlanMode",
|
|
1378
|
+
"ExitPlanMode",
|
|
1379
|
+
"KillShell",
|
|
1380
|
+
"NotebookEdit",
|
|
1381
|
+
"Skill",
|
|
1382
|
+
"Task",
|
|
1383
|
+
"TaskOutput",
|
|
1384
|
+
"TodoWrite",
|
|
1385
|
+
"WebFetch",
|
|
1386
|
+
"WebSearch"
|
|
1387
|
+
].map((t) => [t.toLowerCase(), t]));
|
|
1388
|
+
const toClaudeCodeName = (name) => ccToolLookup.get(name.toLowerCase()) ?? name;
|
|
1389
|
+
const fromClaudeCodeName = (name, tools) => {
|
|
1390
|
+
if (tools && tools.length > 0) {
|
|
1391
|
+
const lowerName = name.toLowerCase();
|
|
1392
|
+
const matchedTool = tools.find((tool) => tool.name.toLowerCase() === lowerName);
|
|
1393
|
+
if (matchedTool) return matchedTool.name;
|
|
1554
1394
|
}
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1395
|
+
return name;
|
|
1396
|
+
};
|
|
1397
|
+
function convertContentBlocks(content) {
|
|
1398
|
+
if (!content.some((c) => c.type === "image")) return sanitizeSurrogates(content.map((c) => c.text).join("\n"));
|
|
1399
|
+
const blocks = content.map((block) => {
|
|
1400
|
+
if (block.type === "text") return {
|
|
1401
|
+
type: "text",
|
|
1402
|
+
text: sanitizeSurrogates(block.text)
|
|
1563
1403
|
};
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
toolCall: toolCallInFlight
|
|
1571
|
-
}, signal);
|
|
1572
|
-
if (beforeResult?.block) return {
|
|
1573
|
-
error: {
|
|
1574
|
-
kind: "blocked",
|
|
1575
|
-
message: beforeResult?.reason || "Tool Execution was Blocked",
|
|
1576
|
-
details: beforeResult?.details
|
|
1577
|
-
},
|
|
1578
|
-
runnable: toolCallInFlight
|
|
1579
|
-
};
|
|
1404
|
+
return {
|
|
1405
|
+
type: "image",
|
|
1406
|
+
source: {
|
|
1407
|
+
type: "base64",
|
|
1408
|
+
media_type: block.mimeType,
|
|
1409
|
+
data: block.data
|
|
1580
1410
|
}
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1411
|
+
};
|
|
1412
|
+
});
|
|
1413
|
+
if (!blocks.some((b) => b.type === "text")) blocks.unshift({
|
|
1414
|
+
type: "text",
|
|
1415
|
+
text: "(see attached image)"
|
|
1416
|
+
});
|
|
1417
|
+
return blocks;
|
|
1418
|
+
}
|
|
1419
|
+
function mergeHeaders(...headerSources) {
|
|
1420
|
+
const merged = {};
|
|
1421
|
+
for (const headers of headerSources) if (headers) Object.assign(merged, headers);
|
|
1422
|
+
return merged;
|
|
1423
|
+
}
|
|
1424
|
+
function isOAuthToken(apiKey) {
|
|
1425
|
+
return apiKey.includes("sk-ant-oat");
|
|
1426
|
+
}
|
|
1427
|
+
function createClient$1(model, apiKey, interleavedThinking, optionsHeaders) {
|
|
1428
|
+
const betaFeatures = ["fine-grained-tool-streaming-2025-05-14"];
|
|
1429
|
+
if (interleavedThinking) betaFeatures.push("interleaved-thinking-2025-05-14");
|
|
1430
|
+
if (isOAuthToken(apiKey)) {
|
|
1431
|
+
const defaultHeaders = mergeHeaders({
|
|
1432
|
+
accept: "application/json",
|
|
1433
|
+
"anthropic-dangerous-direct-browser-access": "true",
|
|
1434
|
+
"anthropic-beta": `claude-code-20250219,oauth-2025-04-20,${betaFeatures.join(",")}`,
|
|
1435
|
+
"user-agent": `claude-cli/${claudeCodeVersion} (external, cli)`,
|
|
1436
|
+
"x-app": "cli"
|
|
1437
|
+
}, model.headers, optionsHeaders);
|
|
1438
|
+
return {
|
|
1439
|
+
client: new Anthropic({
|
|
1440
|
+
apiKey: null,
|
|
1441
|
+
authToken: apiKey,
|
|
1442
|
+
baseURL: model.baseUrl,
|
|
1443
|
+
defaultHeaders,
|
|
1444
|
+
dangerouslyAllowBrowser: true
|
|
1445
|
+
}),
|
|
1446
|
+
isOAuthToken: true
|
|
1447
|
+
};
|
|
1448
|
+
}
|
|
1449
|
+
const defaultHeaders = mergeHeaders({
|
|
1450
|
+
accept: "application/json",
|
|
1451
|
+
"anthropic-dangerous-direct-browser-access": "true",
|
|
1452
|
+
"anthropic-beta": betaFeatures.join(",")
|
|
1453
|
+
}, model.headers, optionsHeaders);
|
|
1454
|
+
return {
|
|
1455
|
+
client: new Anthropic({
|
|
1456
|
+
apiKey,
|
|
1457
|
+
baseURL: model.baseUrl,
|
|
1458
|
+
dangerouslyAllowBrowser: true,
|
|
1459
|
+
defaultHeaders
|
|
1460
|
+
}),
|
|
1461
|
+
isOAuthToken: false
|
|
1462
|
+
};
|
|
1463
|
+
}
|
|
1464
|
+
function normalizeToolCallId(id) {
|
|
1465
|
+
return id.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 64);
|
|
1466
|
+
}
|
|
1467
|
+
function assertReplayableAssistant(message) {
|
|
1468
|
+
let sawToolCall = false;
|
|
1469
|
+
let sawNonTerminalToolCall = false;
|
|
1470
|
+
let sawTerminalToolCall = false;
|
|
1471
|
+
for (const part of message.parts) {
|
|
1472
|
+
if (part.type === "image") throw new Error("Anthropic replay does not support assistant image parts");
|
|
1473
|
+
if (part.type === "toolCall") {
|
|
1474
|
+
sawToolCall = true;
|
|
1475
|
+
if (part.status === "pending" || part.status === "running") sawNonTerminalToolCall = true;
|
|
1476
|
+
else sawTerminalToolCall = true;
|
|
1477
|
+
continue;
|
|
1593
1478
|
}
|
|
1479
|
+
if (sawToolCall) throw new Error("Assistant parts after a toolCall are not replayable for Anthropic");
|
|
1594
1480
|
}
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1481
|
+
if (sawToolCall && message.stopReason !== "toolUse") throw new Error("Assistant message with tool calls must have stopReason 'toolUse'");
|
|
1482
|
+
if (!sawToolCall && message.stopReason === "toolUse") throw new Error("Assistant message with stopReason 'toolUse' must contain at least one toolCall");
|
|
1483
|
+
if (sawTerminalToolCall && sawNonTerminalToolCall) throw new Error("Assistant message cannot mix terminal and non-terminal toolCall states");
|
|
1484
|
+
}
|
|
1485
|
+
function buildSyntheticToolResult$1(block) {
|
|
1486
|
+
return {
|
|
1487
|
+
type: "tool_result",
|
|
1488
|
+
tool_use_id: block.callID,
|
|
1489
|
+
content: convertContentBlocks([{
|
|
1490
|
+
type: "text",
|
|
1491
|
+
text: [
|
|
1492
|
+
"<result>",
|
|
1493
|
+
"No Result Provided",
|
|
1494
|
+
"</result>"
|
|
1495
|
+
].join("\n")
|
|
1496
|
+
}]),
|
|
1497
|
+
is_error: true
|
|
1498
|
+
};
|
|
1499
|
+
}
|
|
1500
|
+
function convertMessages$1(messages, model, isOAuthToken, cacheControl) {
|
|
1501
|
+
const params = [];
|
|
1502
|
+
const transformedMessages = Message.transformMessages(messages, model, normalizeToolCallId);
|
|
1503
|
+
for (const msg of transformedMessages) {
|
|
1504
|
+
if (msg.role === "user") {
|
|
1505
|
+
const blocks = msg.parts.map((item) => {
|
|
1506
|
+
if (item.type === "text") return {
|
|
1507
|
+
type: "text",
|
|
1508
|
+
text: sanitizeSurrogates(item.text)
|
|
1612
1509
|
};
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
}
|
|
1621
|
-
})
|
|
1622
|
-
|
|
1623
|
-
|
|
1510
|
+
return {
|
|
1511
|
+
type: "image",
|
|
1512
|
+
source: {
|
|
1513
|
+
type: "base64",
|
|
1514
|
+
media_type: item.mimeType,
|
|
1515
|
+
data: item.data
|
|
1516
|
+
}
|
|
1517
|
+
};
|
|
1518
|
+
});
|
|
1519
|
+
let filteredBlocks = !model.input.includes("image") ? blocks.filter((b) => b.type !== "image") : blocks;
|
|
1520
|
+
filteredBlocks = filteredBlocks.filter((b) => b.type === "text" ? b.text.trim().length > 0 : true);
|
|
1521
|
+
if (filteredBlocks.length === 0) continue;
|
|
1522
|
+
params.push({
|
|
1523
|
+
role: "user",
|
|
1524
|
+
content: filteredBlocks
|
|
1525
|
+
});
|
|
1526
|
+
continue;
|
|
1527
|
+
}
|
|
1528
|
+
if (msg.stopReason === "error" || msg.stopReason === "aborted") continue;
|
|
1529
|
+
assertReplayableAssistant(msg);
|
|
1530
|
+
const assistantBlocks = [];
|
|
1531
|
+
const toolResults = [];
|
|
1532
|
+
for (const block of msg.parts) if (block.type === "text") {
|
|
1533
|
+
if (block.text.trim().length === 0) continue;
|
|
1534
|
+
assistantBlocks.push({
|
|
1535
|
+
type: "text",
|
|
1536
|
+
text: sanitizeSurrogates(block.text)
|
|
1537
|
+
});
|
|
1538
|
+
} else if (block.type === "thinking") {
|
|
1539
|
+
if (block.thinking.trim().length === 0) continue;
|
|
1540
|
+
if (!block.thinkingSignature || block.thinkingSignature.trim().length === 0) assistantBlocks.push({
|
|
1541
|
+
type: "text",
|
|
1542
|
+
text: sanitizeSurrogates(block.thinking)
|
|
1543
|
+
});
|
|
1544
|
+
else assistantBlocks.push({
|
|
1545
|
+
type: "thinking",
|
|
1546
|
+
thinking: sanitizeSurrogates(block.thinking),
|
|
1547
|
+
signature: block.thinkingSignature
|
|
1548
|
+
});
|
|
1549
|
+
} else if (block.type === "toolCall") {
|
|
1550
|
+
assistantBlocks.push({
|
|
1551
|
+
type: "tool_use",
|
|
1552
|
+
id: block.callID,
|
|
1553
|
+
name: isOAuthToken ? toClaudeCodeName(block.name) : block.name,
|
|
1554
|
+
input: block.arguments ?? {}
|
|
1555
|
+
});
|
|
1556
|
+
if (block.status === "pending" || block.status === "running") toolResults.push(buildSyntheticToolResult$1(block));
|
|
1557
|
+
else toolResults.push({
|
|
1558
|
+
type: "tool_result",
|
|
1559
|
+
tool_use_id: block.callID,
|
|
1560
|
+
content: convertContentBlocks(block.result.content),
|
|
1561
|
+
is_error: block.result.isError
|
|
1562
|
+
});
|
|
1563
|
+
}
|
|
1564
|
+
if (assistantBlocks.length > 0) params.push({
|
|
1565
|
+
role: "assistant",
|
|
1566
|
+
content: assistantBlocks
|
|
1567
|
+
});
|
|
1568
|
+
if (toolResults.length > 0) params.push({
|
|
1569
|
+
role: "user",
|
|
1570
|
+
content: toolResults
|
|
1571
|
+
});
|
|
1572
|
+
}
|
|
1573
|
+
if (cacheControl && params.length > 0) {
|
|
1574
|
+
const lastMessage = params[params.length - 1];
|
|
1575
|
+
if (lastMessage.role === "user") {
|
|
1576
|
+
if (Array.isArray(lastMessage.content)) {
|
|
1577
|
+
const lastBlock = lastMessage.content[lastMessage.content.length - 1];
|
|
1578
|
+
if (lastBlock && (lastBlock.type === "text" || lastBlock.type === "image" || lastBlock.type === "tool_result")) lastBlock.cache_control = cacheControl;
|
|
1579
|
+
} else if (typeof lastMessage.content === "string") lastMessage.content = [{
|
|
1580
|
+
type: "text",
|
|
1581
|
+
text: lastMessage.content,
|
|
1582
|
+
cache_control: cacheControl
|
|
1583
|
+
}];
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
return params;
|
|
1587
|
+
}
|
|
1588
|
+
function convertTools$1(tools, isOAuthToken) {
|
|
1589
|
+
if (!tools) return [];
|
|
1590
|
+
return tools.map((tool) => {
|
|
1591
|
+
const jsonSchema = tool.parameters;
|
|
1592
|
+
return {
|
|
1593
|
+
name: isOAuthToken ? toClaudeCodeName(tool.name) : tool.name,
|
|
1594
|
+
description: tool.description,
|
|
1595
|
+
input_schema: {
|
|
1596
|
+
type: "object",
|
|
1597
|
+
properties: jsonSchema.properties || {},
|
|
1598
|
+
required: jsonSchema.required || []
|
|
1599
|
+
}
|
|
1600
|
+
};
|
|
1601
|
+
});
|
|
1602
|
+
}
|
|
1603
|
+
function mapStopReason$1(reason) {
|
|
1604
|
+
switch (reason) {
|
|
1605
|
+
case "end_turn": return "stop";
|
|
1606
|
+
case "max_tokens": return "length";
|
|
1607
|
+
case "tool_use": return "toolUse";
|
|
1608
|
+
case "pause_turn":
|
|
1609
|
+
case "stop_sequence": return "stop";
|
|
1610
|
+
case "refusal":
|
|
1611
|
+
case "sensitive": return "error";
|
|
1612
|
+
default: throw new Error("Unhandled Anthropic stop reason");
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
function buildParams$1(model, context, isOAuthToken, options) {
|
|
1616
|
+
const { cacheControl } = getCacheControl(model.baseUrl, options?.cacheRetention);
|
|
1617
|
+
const params = {
|
|
1618
|
+
model: model.id,
|
|
1619
|
+
messages: convertMessages$1(context.messages, model, isOAuthToken, cacheControl),
|
|
1620
|
+
max_tokens: options?.maxTokens || model.maxTokens / 3 | 0,
|
|
1621
|
+
stream: true
|
|
1622
|
+
};
|
|
1623
|
+
if (isOAuthToken) {
|
|
1624
|
+
params.system = [{
|
|
1625
|
+
type: "text",
|
|
1626
|
+
text: "You are Claude Code, Anthropic's official CLI for Claude.",
|
|
1627
|
+
...cacheControl ? { cache_control: cacheControl } : {}
|
|
1628
|
+
}];
|
|
1629
|
+
if (context.systemPrompt) params.system.push({
|
|
1630
|
+
type: "text",
|
|
1631
|
+
text: sanitizeSurrogates(context.systemPrompt),
|
|
1632
|
+
...cacheControl ? { cache_control: cacheControl } : {}
|
|
1633
|
+
});
|
|
1634
|
+
} else if (context.systemPrompt) params.system = [{
|
|
1635
|
+
type: "text",
|
|
1636
|
+
text: sanitizeSurrogates(context.systemPrompt),
|
|
1637
|
+
...cacheControl ? { cache_control: cacheControl } : {}
|
|
1638
|
+
}];
|
|
1639
|
+
if (options?.temperature !== void 0 && !options?.thinkingEnabled) params.temperature = options.temperature;
|
|
1640
|
+
if (context.tools) params.tools = convertTools$1(context.tools, isOAuthToken);
|
|
1641
|
+
if (options?.thinkingEnabled && model.reasoning) params.thinking = {
|
|
1642
|
+
type: "enabled",
|
|
1643
|
+
budget_tokens: options.thinkingBudgetTokens || 1024
|
|
1644
|
+
};
|
|
1645
|
+
if (options?.metadata) {
|
|
1646
|
+
const userId = options.metadata.user_id;
|
|
1647
|
+
if (typeof userId === "string") params.metadata = { user_id: userId };
|
|
1648
|
+
}
|
|
1649
|
+
if (options?.toolChoice) params.tool_choice = typeof options.toolChoice === "string" ? { type: options.toolChoice } : options.toolChoice;
|
|
1650
|
+
return params;
|
|
1651
|
+
}
|
|
1652
|
+
const streamAnthropic = (model, context, options) => {
|
|
1653
|
+
const stream = new AssistantMessageEventStream();
|
|
1654
|
+
(async () => {
|
|
1655
|
+
const output = Message.createAssistantMessage({
|
|
1656
|
+
role: "assistant",
|
|
1657
|
+
parts: [],
|
|
1658
|
+
protocol: model.protocol,
|
|
1659
|
+
provider: model.provider,
|
|
1660
|
+
model: model.id,
|
|
1661
|
+
usage: {
|
|
1662
|
+
input: 0,
|
|
1663
|
+
output: 0,
|
|
1664
|
+
cacheRead: 0,
|
|
1665
|
+
cacheWrite: 0,
|
|
1666
|
+
totalTokens: 0,
|
|
1667
|
+
cost: {
|
|
1668
|
+
input: 0,
|
|
1669
|
+
output: 0,
|
|
1670
|
+
cacheRead: 0,
|
|
1671
|
+
cacheWrite: 0,
|
|
1672
|
+
total: 0
|
|
1673
|
+
}
|
|
1674
|
+
},
|
|
1675
|
+
stopReason: "stop",
|
|
1676
|
+
time: {
|
|
1677
|
+
created: Date.now(),
|
|
1678
|
+
completed: Date.now()
|
|
1679
|
+
}
|
|
1680
|
+
});
|
|
1681
|
+
try {
|
|
1682
|
+
let client;
|
|
1683
|
+
let isOAuthToken;
|
|
1684
|
+
if (options?.client) {
|
|
1685
|
+
client = options.client;
|
|
1686
|
+
isOAuthToken = false;
|
|
1687
|
+
} else {
|
|
1688
|
+
const apiKey = options?.apiKey ?? /* @__PURE__ */ getEnvApiKey(model.provider);
|
|
1689
|
+
if (!apiKey) throw new Error(`No API key for provider: ${model.provider.id}`);
|
|
1690
|
+
const created = createClient$1(model, apiKey, options?.interleavedThinking ?? true, options?.headers);
|
|
1691
|
+
client = created.client;
|
|
1692
|
+
isOAuthToken = created.isOAuthToken;
|
|
1693
|
+
}
|
|
1694
|
+
let params = buildParams$1(model, context, isOAuthToken, options);
|
|
1695
|
+
const nextParams = await options?.onPayload?.(params, model);
|
|
1696
|
+
if (nextParams !== void 0) params = nextParams;
|
|
1697
|
+
const anthropicStream = client.messages.stream({
|
|
1698
|
+
...params,
|
|
1699
|
+
stream: true
|
|
1700
|
+
}, { signal: options?.signal });
|
|
1701
|
+
stream.push({
|
|
1702
|
+
type: "start",
|
|
1703
|
+
partial: output
|
|
1704
|
+
});
|
|
1705
|
+
const blocks = output.parts;
|
|
1706
|
+
for await (const event of anthropicStream) if (event.type === "message_start") {
|
|
1707
|
+
output.responseId = event.message.id;
|
|
1708
|
+
output.usage.input = event.message.usage.input_tokens || 0;
|
|
1709
|
+
output.usage.output = event.message.usage.output_tokens || 0;
|
|
1710
|
+
output.usage.cacheRead = event.message.usage.cache_read_input_tokens || 0;
|
|
1711
|
+
output.usage.cacheWrite = event.message.usage.cache_creation_input_tokens || 0;
|
|
1712
|
+
output.usage.totalTokens = output.usage.input + output.usage.output + output.usage.cacheRead + output.usage.cacheWrite;
|
|
1713
|
+
Model.calculateCost(model, output.usage);
|
|
1714
|
+
} else if (event.type === "content_block_start") {
|
|
1715
|
+
if (event.content_block.type === "text") {
|
|
1716
|
+
const block = {
|
|
1717
|
+
type: "text",
|
|
1718
|
+
text: "",
|
|
1719
|
+
index: event.index
|
|
1720
|
+
};
|
|
1721
|
+
output.parts.push(block);
|
|
1722
|
+
stream.push({
|
|
1723
|
+
type: "text.start",
|
|
1724
|
+
partIndex: output.parts.length - 1,
|
|
1725
|
+
partial: output
|
|
1726
|
+
});
|
|
1727
|
+
} else if (event.content_block.type === "thinking") {
|
|
1728
|
+
const block = {
|
|
1729
|
+
type: "thinking",
|
|
1730
|
+
thinking: "",
|
|
1731
|
+
thinkingSignature: "",
|
|
1732
|
+
index: event.index
|
|
1733
|
+
};
|
|
1734
|
+
output.parts.push(block);
|
|
1735
|
+
stream.push({
|
|
1736
|
+
type: "thinking.start",
|
|
1737
|
+
partIndex: output.parts.length - 1,
|
|
1738
|
+
partial: output
|
|
1739
|
+
});
|
|
1740
|
+
} else if (event.content_block.type === "redacted_thinking") {
|
|
1741
|
+
const block = {
|
|
1742
|
+
type: "thinking",
|
|
1743
|
+
thinking: "[Reasoning redacted]",
|
|
1744
|
+
thinkingSignature: event.content_block.data,
|
|
1745
|
+
redacted: true,
|
|
1746
|
+
index: event.index
|
|
1747
|
+
};
|
|
1748
|
+
output.parts.push(block);
|
|
1749
|
+
stream.push({
|
|
1750
|
+
type: "thinking.start",
|
|
1751
|
+
partIndex: output.parts.length - 1,
|
|
1752
|
+
partial: output
|
|
1753
|
+
});
|
|
1754
|
+
} else if (event.content_block.type === "tool_use") {
|
|
1755
|
+
const block = {
|
|
1756
|
+
type: "toolCall",
|
|
1757
|
+
callID: event.content_block.id,
|
|
1758
|
+
name: isOAuthToken ? fromClaudeCodeName(event.content_block.name, context.tools) : event.content_block.name,
|
|
1759
|
+
arguments: event.content_block.input ?? {},
|
|
1760
|
+
partialJson: "",
|
|
1761
|
+
status: "pending",
|
|
1762
|
+
time: {
|
|
1763
|
+
start: Date.now(),
|
|
1764
|
+
end: Date.now()
|
|
1765
|
+
},
|
|
1766
|
+
index: event.index
|
|
1767
|
+
};
|
|
1768
|
+
output.parts.push(block);
|
|
1769
|
+
stream.push({
|
|
1770
|
+
type: "toolcall.start",
|
|
1771
|
+
partIndex: output.parts.length - 1,
|
|
1772
|
+
partial: output
|
|
1773
|
+
});
|
|
1774
|
+
}
|
|
1775
|
+
} else if (event.type === "content_block_delta") {
|
|
1776
|
+
const partIndex = blocks.findIndex((b) => b.index === event.index);
|
|
1777
|
+
const block = blocks[partIndex];
|
|
1778
|
+
if (!block) continue;
|
|
1779
|
+
if (event.delta.type === "text_delta" && block.type === "text") {
|
|
1780
|
+
block.text += event.delta.text;
|
|
1781
|
+
stream.push({
|
|
1782
|
+
type: "text.delta",
|
|
1783
|
+
partIndex,
|
|
1784
|
+
delta: event.delta.text,
|
|
1785
|
+
partial: output
|
|
1786
|
+
});
|
|
1787
|
+
} else if (event.delta.type === "thinking_delta" && block.type === "thinking") {
|
|
1788
|
+
block.thinking += event.delta.thinking;
|
|
1789
|
+
stream.push({
|
|
1790
|
+
type: "thinking.delta",
|
|
1791
|
+
partIndex,
|
|
1792
|
+
delta: event.delta.thinking,
|
|
1793
|
+
partial: output
|
|
1794
|
+
});
|
|
1795
|
+
} else if (event.delta.type === "input_json_delta" && block.type === "toolCall") {
|
|
1796
|
+
block.partialJson += event.delta.partial_json;
|
|
1797
|
+
block.arguments = parseStreamingJson(block.partialJson);
|
|
1798
|
+
stream.push({
|
|
1799
|
+
type: "toolcall.delta",
|
|
1800
|
+
partIndex,
|
|
1801
|
+
delta: event.delta.partial_json,
|
|
1802
|
+
partial: output
|
|
1803
|
+
});
|
|
1804
|
+
} else if (event.delta.type === "signature_delta" && block.type === "thinking") {
|
|
1805
|
+
block.thinkingSignature = block.thinkingSignature || "";
|
|
1806
|
+
block.thinkingSignature += event.delta.signature;
|
|
1807
|
+
}
|
|
1808
|
+
} else if (event.type === "content_block_stop") {
|
|
1809
|
+
const partIndex = blocks.findIndex((b) => b.index === event.index);
|
|
1810
|
+
const block = blocks[partIndex];
|
|
1811
|
+
if (block) {
|
|
1812
|
+
delete block.index;
|
|
1813
|
+
if (block.type === "text") stream.push({
|
|
1814
|
+
type: "text.end",
|
|
1815
|
+
partIndex,
|
|
1816
|
+
content: block.text,
|
|
1817
|
+
partial: output
|
|
1818
|
+
});
|
|
1819
|
+
else if (block.type === "thinking") stream.push({
|
|
1820
|
+
type: "thinking.end",
|
|
1821
|
+
partIndex,
|
|
1822
|
+
content: block.thinking,
|
|
1823
|
+
partial: output
|
|
1824
|
+
});
|
|
1825
|
+
else if (block.type === "toolCall") {
|
|
1826
|
+
block.arguments = parseStreamingJson(block.partialJson);
|
|
1827
|
+
delete block.partialJson;
|
|
1828
|
+
stream.push({
|
|
1829
|
+
type: "toolcall.end",
|
|
1830
|
+
partIndex,
|
|
1831
|
+
toolCall: block,
|
|
1832
|
+
partial: output
|
|
1833
|
+
});
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
} else if (event.type === "message_delta") {
|
|
1837
|
+
if (event.delta.stop_reason) output.stopReason = mapStopReason$1(event.delta.stop_reason);
|
|
1838
|
+
if (event.usage.input_tokens != null) output.usage.input = event.usage.input_tokens;
|
|
1839
|
+
if (event.usage.output_tokens != null) output.usage.output = event.usage.output_tokens;
|
|
1840
|
+
if (event.usage.cache_read_input_tokens != null) output.usage.cacheRead = event.usage.cache_read_input_tokens;
|
|
1841
|
+
if (event.usage.cache_creation_input_tokens != null) output.usage.cacheWrite = event.usage.cache_creation_input_tokens;
|
|
1842
|
+
output.usage.totalTokens = output.usage.input + output.usage.output + output.usage.cacheRead + output.usage.cacheWrite;
|
|
1843
|
+
Model.calculateCost(model, output.usage);
|
|
1844
|
+
}
|
|
1845
|
+
if (options?.signal?.aborted) throw new Error("Request was aborted");
|
|
1846
|
+
if (output.stopReason === "aborted" || output.stopReason === "error") throw new Error("An unknown error occurred");
|
|
1847
|
+
output.time.completed = Date.now();
|
|
1848
|
+
stream.push({
|
|
1849
|
+
type: "done",
|
|
1850
|
+
reason: output.stopReason,
|
|
1851
|
+
message: output
|
|
1852
|
+
});
|
|
1853
|
+
stream.end();
|
|
1854
|
+
} catch (error) {
|
|
1855
|
+
for (const block of output.parts) delete block.index;
|
|
1856
|
+
output.time.completed = Date.now();
|
|
1857
|
+
output.stopReason = options?.signal?.aborted ? "aborted" : "error";
|
|
1858
|
+
output.errorMessage = error instanceof Error ? error.message : JSON.stringify(error);
|
|
1859
|
+
stream.push({
|
|
1860
|
+
type: "error",
|
|
1861
|
+
reason: output.stopReason,
|
|
1862
|
+
error: output
|
|
1863
|
+
});
|
|
1864
|
+
stream.end();
|
|
1624
1865
|
}
|
|
1866
|
+
})();
|
|
1867
|
+
return stream;
|
|
1868
|
+
};
|
|
1869
|
+
const streamSimpleAnthropic = (model, context, options) => {
|
|
1870
|
+
const apiKey = options?.apiKey || /* @__PURE__ */ getEnvApiKey(model.provider);
|
|
1871
|
+
if (!apiKey) throw new Error(`No API key for provider: ${model.provider.id}`);
|
|
1872
|
+
const base = Stream.buildBaseOptions(model, options, apiKey);
|
|
1873
|
+
if (!options?.reasoning) return streamAnthropic(model, context, {
|
|
1874
|
+
...base,
|
|
1875
|
+
thinkingEnabled: false
|
|
1876
|
+
});
|
|
1877
|
+
const adjusted = Stream.adjustMaxTokensForThinking(base.maxTokens || 0, model.maxTokens, options.reasoning, options.thinkingBudgets);
|
|
1878
|
+
return streamAnthropic(model, context, {
|
|
1879
|
+
...base,
|
|
1880
|
+
maxTokens: adjusted.maxTokens,
|
|
1881
|
+
thinkingEnabled: true,
|
|
1882
|
+
thinkingBudgetTokens: adjusted.thinkingBudget
|
|
1883
|
+
});
|
|
1884
|
+
};
|
|
1885
|
+
const anthropicProtocolProvider = {
|
|
1886
|
+
protocol: Model.KnownProtocolEnum.anthropicMessages,
|
|
1887
|
+
stream: streamAnthropic,
|
|
1888
|
+
streamSimple: streamSimpleAnthropic
|
|
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;
|
|
1625
1916
|
}
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
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",
|
|
1637
1959
|
time: {
|
|
1638
|
-
|
|
1639
|
-
|
|
1960
|
+
created: Date.now(),
|
|
1961
|
+
completed: Date.now()
|
|
1640
1962
|
}
|
|
1641
|
-
};
|
|
1642
|
-
message.parts[partIndex] = part;
|
|
1643
|
-
await emitEvent(emit, {
|
|
1644
|
-
type: "message.part.start",
|
|
1645
|
-
message,
|
|
1646
|
-
partIndex,
|
|
1647
|
-
part
|
|
1648
|
-
});
|
|
1649
|
-
await emitEvent(emit, {
|
|
1650
|
-
type: "message.part.end",
|
|
1651
|
-
message,
|
|
1652
|
-
partIndex,
|
|
1653
|
-
part
|
|
1654
|
-
});
|
|
1655
|
-
await emitEvent(emit, {
|
|
1656
|
-
type: "message.update",
|
|
1657
|
-
message
|
|
1658
1963
|
});
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
const
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
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
|
|
1691
2003
|
});
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
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
|
|
1704
2089
|
});
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
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
|
|
1708
2122
|
});
|
|
1709
2123
|
}
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
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
|
+
});
|
|
1716
2139
|
}
|
|
1717
|
-
pendingMessages = [];
|
|
1718
2140
|
}
|
|
1719
|
-
const
|
|
1720
|
-
if (
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
message
|
|
1726
|
-
});
|
|
1727
|
-
await emitEvent(emit, {
|
|
1728
|
-
type: "agent.end",
|
|
1729
|
-
messages: newMessages
|
|
1730
|
-
});
|
|
1731
|
-
return;
|
|
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
|
+
}
|
|
1732
2147
|
}
|
|
1733
|
-
const toolCalls = message.parts.map((part, index) => ({
|
|
1734
|
-
...part,
|
|
1735
|
-
partIndex: index
|
|
1736
|
-
})).filter((part) => part.type === "toolCall" && part.status === "pending");
|
|
1737
|
-
hasMoreToolCalls = toolCalls.length > 0;
|
|
1738
|
-
if (hasMoreToolCalls) await executeToolCalls(config, currentContext, message, toolCalls, emit, signal);
|
|
1739
|
-
currentContext.messages.push(message);
|
|
1740
|
-
newMessages.push(message);
|
|
1741
|
-
await emitEvent(emit, {
|
|
1742
|
-
type: "turn.end",
|
|
1743
|
-
message
|
|
1744
|
-
});
|
|
1745
|
-
pendingMessages = await config.getSteeringMessages?.() || [];
|
|
1746
2148
|
}
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
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;
|
|
1751
2166
|
}
|
|
1752
|
-
|
|
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();
|
|
1753
2178
|
}
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
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
|
|
1757
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
|
+
}
|
|
1758
2282
|
}
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
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."
|
|
1777
2305
|
});
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
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}` }
|
|
1790
2388
|
});
|
|
1791
2389
|
}
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
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]
|
|
1795
2402
|
});
|
|
2403
|
+
lastRole = "user";
|
|
1796
2404
|
}
|
|
1797
|
-
await runLoop(config, currentContext, newMessages, emit, streamFn, signal);
|
|
1798
|
-
return newMessages;
|
|
1799
|
-
}
|
|
1800
|
-
_Loop.runAgentLoop = runAgentLoop;
|
|
1801
|
-
async function runAgentLoopContinue(config, context, emit, streamFn, signal) {
|
|
1802
|
-
if (context.messages.length === 0) throw new AgentContextError({ message: "connot continue: no messages in context" });
|
|
1803
|
-
if (context.messages[context.messages.length - 1]?.role === "assistant") throw new AgentContextError({ message: "cannot continue from message role: assistant" });
|
|
1804
|
-
const newMessages = [];
|
|
1805
|
-
const currentContext = { ...context };
|
|
1806
|
-
await emitEvent(emit, { type: "agent.start" });
|
|
1807
|
-
await emitEvent(emit, { type: "turn.start" });
|
|
1808
|
-
await runLoop(config, currentContext, newMessages, emit, streamFn, signal);
|
|
1809
|
-
return newMessages;
|
|
1810
|
-
}
|
|
1811
|
-
_Loop.runAgentLoopContinue = runAgentLoopContinue;
|
|
1812
|
-
function run(config, context, prompts, streamFn, signal) {
|
|
1813
|
-
const stream = createAgentStream();
|
|
1814
|
-
runAgentLoop(config, context, prompts, async (event) => {
|
|
1815
|
-
stream.push(event);
|
|
1816
|
-
}, streamFn, signal).then((messages) => {
|
|
1817
|
-
stream.end(messages);
|
|
1818
|
-
});
|
|
1819
|
-
return stream;
|
|
1820
2405
|
}
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
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
|
+
};
|
|
1830
2464
|
}
|
|
1831
|
-
|
|
1832
|
-
|
|
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");
|
|
1833
2530
|
//#endregion
|
|
1834
2531
|
//#region src/agent/agent.ts
|
|
1835
2532
|
let Agent;
|
|
@@ -2297,14 +2994,356 @@ let Agent;
|
|
|
2297
2994
|
_Agent.create = create;
|
|
2298
2995
|
})(Agent || (Agent = {}));
|
|
2299
2996
|
//#endregion
|
|
2300
|
-
//#region src/agent.ts
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2997
|
+
//#region src/agent/codemode/codemode.ts
|
|
2998
|
+
let CodeMode;
|
|
2999
|
+
(function(_CodeMode) {
|
|
3000
|
+
const ToolDefinitionErr = _CodeMode.ToolDefinitionErr = NamedError.create("ToolDefinitionErr", Type.Object({ message: Type.String() }));
|
|
3001
|
+
const toolFnPrefix = process.env.CODEWORK_CODEMODE_FN_PREFIX ?? "external_";
|
|
3002
|
+
const SandboxExecutionInputSchema = _CodeMode.SandboxExecutionInputSchema = Type.Object({ typescriptCode: Type.String({ description: "TypeScript code to execute in the sandbox." }) });
|
|
3003
|
+
const SandboxExecutionOutputSchema = _CodeMode.SandboxExecutionOutputSchema = Type.Object({
|
|
3004
|
+
result: Type.Optional(Type.Unknown()),
|
|
3005
|
+
logs: Type.Optional(Type.Array(Type.String()))
|
|
3006
|
+
});
|
|
3007
|
+
const SandboxExecutionErrorSchema = _CodeMode.SandboxExecutionErrorSchema = Type.Object({
|
|
3008
|
+
message: Type.String(),
|
|
3009
|
+
name: Type.Optional(Type.String()),
|
|
3010
|
+
stack: Type.Optional(Type.String()),
|
|
3011
|
+
line: Type.Optional(Type.Number()),
|
|
3012
|
+
logs: Type.Optional(Type.Array(Type.String()))
|
|
3013
|
+
});
|
|
3014
|
+
function withMessageInStack(message, stack) {
|
|
3015
|
+
if (!stack) return void 0;
|
|
3016
|
+
return stack.includes(message) ? stack : `${message}\n${stack}`;
|
|
3017
|
+
}
|
|
3018
|
+
function normalizeExecutionError(error) {
|
|
3019
|
+
if (error instanceof Error) return {
|
|
3020
|
+
name: error.name,
|
|
3021
|
+
message: error.message,
|
|
3022
|
+
stack: withMessageInStack(error.message, error.stack),
|
|
3023
|
+
line: parseErrorLine(error.stack)
|
|
3024
|
+
};
|
|
3025
|
+
if (typeof error === "object" && error !== null) {
|
|
3026
|
+
const candidate = error;
|
|
3027
|
+
return {
|
|
3028
|
+
name: typeof candidate.name === "string" ? candidate.name : void 0,
|
|
3029
|
+
message: typeof candidate.message === "string" ? candidate.message : JSON.stringify(candidate) || "Unknown execution error",
|
|
3030
|
+
stack: typeof candidate.stack === "string" ? withMessageInStack(typeof candidate.message === "string" ? candidate.message : JSON.stringify(candidate) || "Unknown execution error", candidate.stack) : void 0,
|
|
3031
|
+
line: typeof candidate.line === "number" ? candidate.line : parseErrorLine(candidate.stack)
|
|
3032
|
+
};
|
|
3033
|
+
}
|
|
3034
|
+
return {
|
|
3035
|
+
name: "Error",
|
|
3036
|
+
message: String(error)
|
|
3037
|
+
};
|
|
3038
|
+
}
|
|
3039
|
+
function parseErrorLine(stack) {
|
|
3040
|
+
if (typeof stack !== "string") return void 0;
|
|
3041
|
+
const lineMatch = stack.match(/:(\d+)(?::\d+)?\)?$/m);
|
|
3042
|
+
return lineMatch ? Number(lineMatch[1]) : void 0;
|
|
3043
|
+
}
|
|
3044
|
+
function serializeForContent(value) {
|
|
3045
|
+
return JSON.stringify(value, (_key, currentValue) => typeof currentValue === "bigint" ? currentValue.toString() : currentValue, 2);
|
|
3046
|
+
}
|
|
3047
|
+
function toolContentToText(result) {
|
|
3048
|
+
return result.content.filter((part) => part.type === "text" && typeof part.text === "string").map((part) => part.text).join("\n").trim();
|
|
3049
|
+
}
|
|
3050
|
+
function unwrapToolResult(tool, result) {
|
|
3051
|
+
if (result.status === "completed") {
|
|
3052
|
+
if (tool.outputSchema) return validateSchema(tool.outputSchema, result.result.details, `Tool "${tool.name}" output`);
|
|
3053
|
+
return result.result.details;
|
|
3054
|
+
}
|
|
3055
|
+
if (tool.errorSchema && result.result.details !== void 0) validateSchema(tool.errorSchema, result.result.details, `Tool "${tool.name}" error`);
|
|
3056
|
+
const message = toolContentToText(result.result) || `Tool "${tool.name}" failed`;
|
|
3057
|
+
const error = new Error(message);
|
|
3058
|
+
if (result.result.details !== void 0) error.cause = result.result.details;
|
|
3059
|
+
throw error;
|
|
3060
|
+
}
|
|
3061
|
+
function toolToBinding(tool, prefix = "") {
|
|
3062
|
+
const inputSchema = TypeGuard.IsSchema(tool.parameters) ? tool.parameters : Type.Object({});
|
|
3063
|
+
const outputSchema = TypeGuard.IsSchema(tool.outputSchema) ? tool.outputSchema : void 0;
|
|
3064
|
+
const errorSchema = TypeGuard.IsSchema(tool.errorSchema) ? tool.errorSchema : void 0;
|
|
3065
|
+
if (!("execute" in tool) || typeof tool.execute !== "function") throw new ToolDefinitionErr({ message: `tool "${tool.name}" does not have an execute function. code mode requires tools with implementation` });
|
|
3066
|
+
return {
|
|
3067
|
+
name: `${prefix}${tool.name}`,
|
|
3068
|
+
description: tool.description,
|
|
3069
|
+
inputSchema,
|
|
3070
|
+
outputSchema,
|
|
3071
|
+
errorSchema,
|
|
3072
|
+
execute: async (callID, params, signal) => {
|
|
3073
|
+
const validatedParams = validateSchema(inputSchema, params, `Tool "${tool.name}" input`);
|
|
3074
|
+
return unwrapToolResult(tool, await tool.execute(callID, validatedParams, signal));
|
|
3075
|
+
}
|
|
3076
|
+
};
|
|
3077
|
+
}
|
|
3078
|
+
function toolsForBinding(tools, prefix) {
|
|
3079
|
+
const bindings = {};
|
|
3080
|
+
for (const tool of tools) {
|
|
3081
|
+
const name = `${prefix}${tool.name}`;
|
|
3082
|
+
bindings[name] = toolToBinding(tool, prefix);
|
|
3083
|
+
}
|
|
3084
|
+
return bindings;
|
|
3085
|
+
}
|
|
3086
|
+
function indent(value, depth = 1) {
|
|
3087
|
+
const prefix = " ".repeat(depth);
|
|
3088
|
+
return value.split("\n").map((line) => `${prefix}${line}`).join("\n");
|
|
3089
|
+
}
|
|
3090
|
+
function toPropertyKey(name) {
|
|
3091
|
+
return /^[$A-Z_a-z][$\w]*$/i.test(name) ? name : JSON.stringify(name);
|
|
3092
|
+
}
|
|
3093
|
+
function schemaToTypeScript(schema) {
|
|
3094
|
+
if (TypeGuard.IsAny(schema)) return "any";
|
|
3095
|
+
if (TypeGuard.IsUnknown(schema)) return "unknown";
|
|
3096
|
+
if (TypeGuard.IsString(schema)) return "string";
|
|
3097
|
+
if (TypeGuard.IsNumber(schema) || TypeGuard.IsInteger(schema)) return "number";
|
|
3098
|
+
if (TypeGuard.IsBoolean(schema)) return "boolean";
|
|
3099
|
+
if (TypeGuard.IsNull(schema)) return "null";
|
|
3100
|
+
if (TypeGuard.IsUndefined(schema) || TypeGuard.IsVoid(schema)) return "undefined";
|
|
3101
|
+
if (TypeGuard.IsLiteral(schema)) return JSON.stringify(schema.const);
|
|
3102
|
+
if (TypeGuard.IsArray(schema)) return `Array<${schemaToTypeScript(schema.items)}>`;
|
|
3103
|
+
if (TypeGuard.IsTuple(schema)) return `[${(schema.items ?? []).map((item) => schemaToTypeScript(item)).join(", ")}]`;
|
|
3104
|
+
if (TypeGuard.IsUnion(schema)) return schema.anyOf.map((item) => schemaToTypeScript(item)).join(" | ");
|
|
3105
|
+
if (TypeGuard.IsIntersect(schema)) return schema.allOf.map((item) => schemaToTypeScript(item)).join(" & ");
|
|
3106
|
+
if (TypeGuard.IsRecord(schema)) return `Record<string, ${schemaToTypeScript(Object.values(schema.patternProperties ?? {}).find(TypeGuard.IsSchema) ?? (schema.additionalProperties && TypeGuard.IsSchema(schema.additionalProperties) ? schema.additionalProperties : Type.Unknown()))}>`;
|
|
3107
|
+
if (TypeGuard.IsObject(schema)) {
|
|
3108
|
+
const required = new Set(schema.required ?? []);
|
|
3109
|
+
const properties = Object.entries(schema.properties ?? {}).map(([key, value]) => {
|
|
3110
|
+
const optional = required.has(key) ? "" : "?";
|
|
3111
|
+
return `${toPropertyKey(key)}${optional}: ${schemaToTypeScript(value)};`;
|
|
3112
|
+
});
|
|
3113
|
+
if (schema.additionalProperties === true) properties.push("[key: string]: unknown;");
|
|
3114
|
+
else if (schema.additionalProperties && TypeGuard.IsSchema(schema.additionalProperties)) properties.push(`[key: string]: ${schemaToTypeScript(schema.additionalProperties)};`);
|
|
3115
|
+
if (properties.length === 0) return "{}";
|
|
3116
|
+
return `{\n${indent(properties.join("\n"))}\n}`;
|
|
3117
|
+
}
|
|
3118
|
+
return "unknown";
|
|
3119
|
+
}
|
|
3120
|
+
function generateTypeStubs(bindings, options = {}) {
|
|
3121
|
+
const { includeDescriptions = true } = options;
|
|
3122
|
+
const declarations = [];
|
|
3123
|
+
for (const [name, binding] of Object.entries(bindings)) {
|
|
3124
|
+
const inputTypeName = `${capitalize(name)}Input`;
|
|
3125
|
+
const outputTypeName = `${capitalize(name)}Output`;
|
|
3126
|
+
declarations.push(`type ${inputTypeName} = ${schemaToTypeScript(binding.inputSchema)};`);
|
|
3127
|
+
let outputTypeRef = "unknown";
|
|
3128
|
+
if (binding.outputSchema !== void 0) {
|
|
3129
|
+
declarations.push(`type ${outputTypeName} = ${schemaToTypeScript(binding.outputSchema)};`);
|
|
3130
|
+
outputTypeRef = outputTypeName;
|
|
3131
|
+
}
|
|
3132
|
+
const description = includeDescriptions && binding.description ? `/** ${binding.description} */\n` : "";
|
|
3133
|
+
declarations.push(`${description}declare function ${name}(input: ${inputTypeName}): Promise<${outputTypeRef}>;`);
|
|
3134
|
+
}
|
|
3135
|
+
return declarations.join("\n\n");
|
|
3136
|
+
}
|
|
3137
|
+
_CodeMode.generateTypeStubs = generateTypeStubs;
|
|
3138
|
+
function buildExampleSnippet() {
|
|
3139
|
+
return [
|
|
3140
|
+
"```typescript",
|
|
3141
|
+
"const numbers = [12, 24, 36];",
|
|
3142
|
+
"const total = numbers.reduce((sum, value) => sum + value, 0);",
|
|
3143
|
+
"",
|
|
3144
|
+
"return {",
|
|
3145
|
+
" count: numbers.length,",
|
|
3146
|
+
" average: total / numbers.length,",
|
|
3147
|
+
"};",
|
|
3148
|
+
"```"
|
|
3149
|
+
];
|
|
3150
|
+
}
|
|
3151
|
+
function generateSystemPrompt(config) {
|
|
3152
|
+
const fnPrefix = config.fnPrefix ?? toolFnPrefix;
|
|
3153
|
+
const bindings = toolsForBinding(config.tools, fnPrefix);
|
|
3154
|
+
const typeStubs = generateTypeStubs(bindings);
|
|
3155
|
+
return [
|
|
3156
|
+
"## Code Execution Tool",
|
|
3157
|
+
"",
|
|
3158
|
+
"You have access to `sandbox_execute_typescript` which runs TypeScript code in a sandboxed environment.",
|
|
3159
|
+
"",
|
|
3160
|
+
"### When to Use",
|
|
3161
|
+
"",
|
|
3162
|
+
"Use `sandbox_execute_typescript` when you need to:",
|
|
3163
|
+
"- Process data with loops, conditionals, or complex logic",
|
|
3164
|
+
"- Make multiple tool calls in parallel with `Promise.all(...)`",
|
|
3165
|
+
"- Transform, filter, or aggregate data",
|
|
3166
|
+
"- Perform calculations or data analysis",
|
|
3167
|
+
"",
|
|
3168
|
+
"For simple operations, prefer calling tools directly.",
|
|
3169
|
+
"",
|
|
3170
|
+
"### Available External APIs",
|
|
3171
|
+
"",
|
|
3172
|
+
"Inside your TypeScript code, you can call these async functions:",
|
|
3173
|
+
"",
|
|
3174
|
+
Object.entries(bindings).map(([name, binding]) => `- \`${name}(input)\`: ${binding.description}`).join("\n") || "- No external APIs available for this run.",
|
|
3175
|
+
"",
|
|
3176
|
+
"### Type Definitions",
|
|
3177
|
+
"",
|
|
3178
|
+
"```typescript",
|
|
3179
|
+
typeStubs,
|
|
3180
|
+
"```",
|
|
3181
|
+
"",
|
|
3182
|
+
"### Example",
|
|
3183
|
+
"",
|
|
3184
|
+
...buildExampleSnippet(),
|
|
3185
|
+
"",
|
|
3186
|
+
"### Important Notes",
|
|
3187
|
+
"",
|
|
3188
|
+
`- All\`${fnPrefix}\`*calls are async, so always use \`await\``,
|
|
3189
|
+
"- Return a value to pass results back to you",
|
|
3190
|
+
"- Use `console.log()` for debugging (logs are captured)",
|
|
3191
|
+
"- The sandbox is isolated - no network access or file system",
|
|
3192
|
+
"- Each execution is isolated and does not share state with previous runs",
|
|
3193
|
+
""
|
|
3194
|
+
].join("\n");
|
|
3195
|
+
}
|
|
3196
|
+
_CodeMode.generateSystemPrompt = generateSystemPrompt;
|
|
3197
|
+
async function transpileTypeScript(typescriptCode) {
|
|
3198
|
+
return (await transform([
|
|
3199
|
+
"(async () => {",
|
|
3200
|
+
" const __codemodeNormalizeError = (error: unknown) => {",
|
|
3201
|
+
" if (error instanceof Error) return { name: error.name, message: error.message, stack: error.stack };",
|
|
3202
|
+
" if (typeof error === 'object' && error !== null) {",
|
|
3203
|
+
" const candidate = error as { name?: unknown; message?: unknown; stack?: unknown };",
|
|
3204
|
+
" return {",
|
|
3205
|
+
" name: typeof candidate.name === 'string' ? candidate.name : 'Error',",
|
|
3206
|
+
" message:",
|
|
3207
|
+
" typeof candidate.message === 'string'",
|
|
3208
|
+
" ? candidate.message",
|
|
3209
|
+
" : JSON.stringify(candidate) || 'Unknown execution error',",
|
|
3210
|
+
" stack: typeof candidate.stack === 'string' ? candidate.stack : undefined,",
|
|
3211
|
+
" };",
|
|
3212
|
+
" }",
|
|
3213
|
+
" return { name: 'Error', message: String(error) };",
|
|
3214
|
+
" };",
|
|
3215
|
+
" try {",
|
|
3216
|
+
typescriptCode,
|
|
3217
|
+
" } catch (error) {",
|
|
3218
|
+
" throw __codemodeNormalizeError(error);",
|
|
3219
|
+
" }",
|
|
3220
|
+
"})()"
|
|
3221
|
+
].join("\n"), {
|
|
3222
|
+
loader: "ts",
|
|
3223
|
+
target: "es2022",
|
|
3224
|
+
sourcefile: "script.ts",
|
|
3225
|
+
minify: false,
|
|
3226
|
+
keepNames: false
|
|
3227
|
+
})).code;
|
|
3228
|
+
}
|
|
3229
|
+
function buildToolDescription(tools, fnPrefix = toolFnPrefix) {
|
|
3230
|
+
return [
|
|
3231
|
+
"Execute TypeScript code in a secure sandbox environment. ",
|
|
3232
|
+
"The code can use these external API functions:",
|
|
3233
|
+
"<functions>",
|
|
3234
|
+
` ${tools.map((tool) => `${fnPrefix}${tool.name}`).join("\n")}`,
|
|
3235
|
+
"</functions>",
|
|
3236
|
+
`All ${fnPrefix}* calls are async and must be awaited. `,
|
|
3237
|
+
"Return a value to pass results back. Use console.log() for debugging."
|
|
3238
|
+
].join("\n");
|
|
3239
|
+
}
|
|
3240
|
+
function createCodeModeTool(config) {
|
|
3241
|
+
if (config.tools.length === 0) throw new ToolDefinitionErr({ message: "at least one tool must be provided to `createCodeModeTool`" });
|
|
3242
|
+
return Agent.defineTool({
|
|
3243
|
+
name: "sandbox_execute_typescript",
|
|
3244
|
+
label: "Sandbox TypeScript",
|
|
3245
|
+
description: buildToolDescription(config.tools, config.fnPrefix),
|
|
3246
|
+
parameters: SandboxExecutionInputSchema,
|
|
3247
|
+
outputSchema: SandboxExecutionOutputSchema,
|
|
3248
|
+
errorSchema: SandboxExecutionErrorSchema,
|
|
3249
|
+
async execute(_callID, params, signal, onUpdate) {
|
|
3250
|
+
const bindings = toolsForBinding(config.tools, config.fnPrefix ?? toolFnPrefix);
|
|
3251
|
+
let context;
|
|
3252
|
+
try {
|
|
3253
|
+
await onUpdate?.({
|
|
3254
|
+
status: "running",
|
|
3255
|
+
partial: { content: [{
|
|
3256
|
+
type: "text",
|
|
3257
|
+
text: "Transpiling TypeScript for sandbox execution"
|
|
3258
|
+
}] }
|
|
3259
|
+
});
|
|
3260
|
+
const javascriptCode = await transpileTypeScript(params.typescriptCode);
|
|
3261
|
+
await onUpdate?.({
|
|
3262
|
+
status: "running",
|
|
3263
|
+
partial: { content: [{
|
|
3264
|
+
type: "text",
|
|
3265
|
+
text: "Executing code in sandbox runtime"
|
|
3266
|
+
}] }
|
|
3267
|
+
});
|
|
3268
|
+
context = await config.driver.createContext({
|
|
3269
|
+
bindings,
|
|
3270
|
+
timeout: config.timeout,
|
|
3271
|
+
memoryLimit: config.memoryLimit,
|
|
3272
|
+
signal
|
|
3273
|
+
});
|
|
3274
|
+
const executionResult = await context.execute(javascriptCode);
|
|
3275
|
+
if (executionResult.success) {
|
|
3276
|
+
const details = {
|
|
3277
|
+
result: executionResult.value,
|
|
3278
|
+
...executionResult.logs && executionResult.logs.length > 0 ? { logs: executionResult.logs } : {}
|
|
3279
|
+
};
|
|
3280
|
+
return {
|
|
3281
|
+
status: "completed",
|
|
3282
|
+
result: {
|
|
3283
|
+
content: [{
|
|
3284
|
+
type: "text",
|
|
3285
|
+
text: serializeForContent(details)
|
|
3286
|
+
}],
|
|
3287
|
+
details,
|
|
3288
|
+
isError: false
|
|
3289
|
+
}
|
|
3290
|
+
};
|
|
3291
|
+
}
|
|
3292
|
+
const details = {
|
|
3293
|
+
message: executionResult.error?.message || "sandbox execution failed",
|
|
3294
|
+
...executionResult.error?.name ? { name: executionResult.error.name } : {},
|
|
3295
|
+
...executionResult.error?.stack ? { stack: executionResult.error.stack } : {},
|
|
3296
|
+
...executionResult.error?.line != null ? { line: executionResult.error.line } : {},
|
|
3297
|
+
...executionResult.logs && executionResult.logs.length > 0 ? { logs: executionResult.logs } : {}
|
|
3298
|
+
};
|
|
3299
|
+
return {
|
|
3300
|
+
status: "error",
|
|
3301
|
+
result: {
|
|
3302
|
+
content: [{
|
|
3303
|
+
type: "text",
|
|
3304
|
+
text: serializeForContent(details)
|
|
3305
|
+
}],
|
|
3306
|
+
details,
|
|
3307
|
+
isError: true
|
|
3308
|
+
}
|
|
3309
|
+
};
|
|
3310
|
+
} catch (error) {
|
|
3311
|
+
const normalizedError = normalizeExecutionError(error);
|
|
3312
|
+
const details = {
|
|
3313
|
+
message: normalizedError.message,
|
|
3314
|
+
...normalizedError.name ? { name: normalizedError.name } : {},
|
|
3315
|
+
...normalizedError.stack ? { stack: normalizedError.stack } : {},
|
|
3316
|
+
...normalizedError.line != null ? { line: normalizedError.line } : {}
|
|
3317
|
+
};
|
|
3318
|
+
return {
|
|
3319
|
+
status: "error",
|
|
3320
|
+
result: {
|
|
3321
|
+
content: [{
|
|
3322
|
+
type: "text",
|
|
3323
|
+
text: serializeForContent(details)
|
|
3324
|
+
}],
|
|
3325
|
+
details,
|
|
3326
|
+
isError: true
|
|
3327
|
+
}
|
|
3328
|
+
};
|
|
3329
|
+
} finally {
|
|
3330
|
+
await context?.dispose();
|
|
3331
|
+
}
|
|
3332
|
+
}
|
|
3333
|
+
});
|
|
3334
|
+
}
|
|
3335
|
+
_CodeMode.createCodeModeTool = createCodeModeTool;
|
|
3336
|
+
async function create(config) {
|
|
3337
|
+
return {
|
|
3338
|
+
tool: createCodeModeTool(config),
|
|
3339
|
+
systemPrompt: generateSystemPrompt(config)
|
|
3340
|
+
};
|
|
3341
|
+
}
|
|
3342
|
+
_CodeMode.create = create;
|
|
3343
|
+
})(CodeMode || (CodeMode = {}));
|
|
2305
3344
|
//#endregion
|
|
2306
3345
|
//#region src/llm.ts
|
|
2307
|
-
const llmImpl = async (provider, model) => Model.getModel(provider, model);
|
|
3346
|
+
const llmImpl = async (provider, model, overrides) => Model.getModel(provider, model, overrides);
|
|
2308
3347
|
const llm = Object.assign(llmImpl, {
|
|
2309
3348
|
model: Model.getModel,
|
|
2310
3349
|
models: Model.getModels,
|
|
@@ -2337,6 +3376,6 @@ Stream.complete;
|
|
|
2337
3376
|
Stream.streamSimple;
|
|
2338
3377
|
Stream.completeSimple;
|
|
2339
3378
|
//#endregion
|
|
2340
|
-
export { Agent, Message, agent, llm, stream };
|
|
3379
|
+
export { Agent, CodeMode, Message, agent, llm, stream };
|
|
2341
3380
|
|
|
2342
3381
|
//# sourceMappingURL=index.mjs.map
|