@cognipeer/agent-sdk 0.4.8 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -5
- package/dist/index.cjs +2541 -174
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +503 -6
- package/dist/index.d.ts +503 -6
- package/dist/index.mjs +2528 -179
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var zod = require('zod');
|
|
4
4
|
var module$1 = require('module');
|
|
5
|
+
var zodToJsonSchema = require('zod-to-json-schema');
|
|
5
6
|
var fs = require('fs');
|
|
6
7
|
var path = require('path');
|
|
7
8
|
var buffer = require('buffer');
|
|
@@ -134,12 +135,377 @@ function createSimpleTool({
|
|
|
134
135
|
};
|
|
135
136
|
}
|
|
136
137
|
|
|
138
|
+
// src/structuredOutput/types.ts
|
|
139
|
+
function getNestedValue(obj, path2) {
|
|
140
|
+
let current = obj;
|
|
141
|
+
for (const key of path2) {
|
|
142
|
+
if (current == null || typeof current !== "object") return void 0;
|
|
143
|
+
current = current[key];
|
|
144
|
+
}
|
|
145
|
+
return current;
|
|
146
|
+
}
|
|
147
|
+
function formatZodError(error, rawContent) {
|
|
148
|
+
const fieldErrors = error.issues.map((issue) => ({
|
|
149
|
+
path: issue.path.join(".") || "root",
|
|
150
|
+
expected: issue.expected ?? issue.code,
|
|
151
|
+
received: issue.received ?? "unknown",
|
|
152
|
+
message: issue.message
|
|
153
|
+
}));
|
|
154
|
+
return {
|
|
155
|
+
type: "validation_error",
|
|
156
|
+
message: `Schema validation failed: ${fieldErrors.map((f) => `${f.path}: ${f.message}`).join("; ")}`,
|
|
157
|
+
fieldErrors,
|
|
158
|
+
rawContent
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// src/structuredOutput/nativeStrategy.ts
|
|
163
|
+
var NativeJsonSchemaStrategy = class {
|
|
164
|
+
constructor() {
|
|
165
|
+
this.kind = "native";
|
|
166
|
+
}
|
|
167
|
+
resolveJsonPointer(document, pointer) {
|
|
168
|
+
if (!pointer.startsWith("#/")) {
|
|
169
|
+
return void 0;
|
|
170
|
+
}
|
|
171
|
+
const segments = pointer.slice(2).split("/").map((segment) => segment.replace(/~1/g, "/").replace(/~0/g, "~"));
|
|
172
|
+
let current = document;
|
|
173
|
+
for (const segment of segments) {
|
|
174
|
+
if (!current || typeof current !== "object" || !(segment in current)) {
|
|
175
|
+
return void 0;
|
|
176
|
+
}
|
|
177
|
+
current = current[segment];
|
|
178
|
+
}
|
|
179
|
+
return current;
|
|
180
|
+
}
|
|
181
|
+
unwrapRootObjectSchema(schema) {
|
|
182
|
+
if (schema.type === "object") {
|
|
183
|
+
return schema;
|
|
184
|
+
}
|
|
185
|
+
const ref = typeof schema.$ref === "string" ? schema.$ref : null;
|
|
186
|
+
if (!ref) {
|
|
187
|
+
return schema;
|
|
188
|
+
}
|
|
189
|
+
const resolved = this.resolveJsonPointer(schema, ref);
|
|
190
|
+
if (!resolved || typeof resolved !== "object" || Array.isArray(resolved)) {
|
|
191
|
+
return schema;
|
|
192
|
+
}
|
|
193
|
+
const resolvedSchema = { ...resolved };
|
|
194
|
+
if (resolvedSchema.type !== "object") {
|
|
195
|
+
return schema;
|
|
196
|
+
}
|
|
197
|
+
if (schema.definitions && resolvedSchema.definitions === void 0) {
|
|
198
|
+
resolvedSchema.definitions = schema.definitions;
|
|
199
|
+
}
|
|
200
|
+
if (schema.$defs && resolvedSchema.$defs === void 0) {
|
|
201
|
+
resolvedSchema.$defs = schema.$defs;
|
|
202
|
+
}
|
|
203
|
+
return resolvedSchema;
|
|
204
|
+
}
|
|
205
|
+
normalizeOpenAIStrictSchema(schema) {
|
|
206
|
+
const visit = (node) => {
|
|
207
|
+
if (!node || typeof node !== "object") return node;
|
|
208
|
+
if (Array.isArray(node)) {
|
|
209
|
+
return node.map(visit);
|
|
210
|
+
}
|
|
211
|
+
const clone2 = { ...node };
|
|
212
|
+
if (clone2.properties && typeof clone2.properties === "object") {
|
|
213
|
+
const normalizedProperties = {};
|
|
214
|
+
for (const [key, value] of Object.entries(clone2.properties)) {
|
|
215
|
+
normalizedProperties[key] = visit(value);
|
|
216
|
+
}
|
|
217
|
+
clone2.properties = normalizedProperties;
|
|
218
|
+
clone2.required = Object.keys(normalizedProperties);
|
|
219
|
+
if (clone2.additionalProperties === void 0) {
|
|
220
|
+
clone2.additionalProperties = false;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (clone2.items) {
|
|
224
|
+
clone2.items = visit(clone2.items);
|
|
225
|
+
}
|
|
226
|
+
if (clone2.definitions && typeof clone2.definitions === "object") {
|
|
227
|
+
const normalizedDefinitions = {};
|
|
228
|
+
for (const [key, value] of Object.entries(clone2.definitions)) {
|
|
229
|
+
normalizedDefinitions[key] = visit(value);
|
|
230
|
+
}
|
|
231
|
+
clone2.definitions = normalizedDefinitions;
|
|
232
|
+
}
|
|
233
|
+
if (clone2.$defs && typeof clone2.$defs === "object") {
|
|
234
|
+
const normalizedDefs = {};
|
|
235
|
+
for (const [key, value] of Object.entries(clone2.$defs)) {
|
|
236
|
+
normalizedDefs[key] = visit(value);
|
|
237
|
+
}
|
|
238
|
+
clone2.$defs = normalizedDefs;
|
|
239
|
+
}
|
|
240
|
+
for (const compositeKey of ["anyOf", "oneOf", "allOf"]) {
|
|
241
|
+
if (Array.isArray(clone2[compositeKey])) {
|
|
242
|
+
clone2[compositeKey] = clone2[compositeKey].map(visit);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return clone2;
|
|
246
|
+
};
|
|
247
|
+
return visit(schema);
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Convert a Zod schema to the JSON Schema object used in response_format.
|
|
251
|
+
*/
|
|
252
|
+
toJsonSchema(schema, name = "structured_response") {
|
|
253
|
+
const result = zodToJsonSchema.zodToJsonSchema(schema, {
|
|
254
|
+
name,
|
|
255
|
+
openaiStrictMode: true,
|
|
256
|
+
nameStrategy: "duplicate-ref",
|
|
257
|
+
$refStrategy: "extract-to-root",
|
|
258
|
+
nullableStrategy: "property"
|
|
259
|
+
});
|
|
260
|
+
const normalized = this.normalizeOpenAIStrictSchema(result);
|
|
261
|
+
return this.unwrapRootObjectSchema(normalized);
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Build response_format configuration for the model call.
|
|
265
|
+
* Returns the object that should be spread into model invocation options.
|
|
266
|
+
*/
|
|
267
|
+
buildResponseFormat(schema, name) {
|
|
268
|
+
const schemaName = name || "structured_response";
|
|
269
|
+
const jsonSchema = this.toJsonSchema(schema, schemaName);
|
|
270
|
+
delete jsonSchema["$schema"];
|
|
271
|
+
return {
|
|
272
|
+
response_format: {
|
|
273
|
+
type: "json_schema",
|
|
274
|
+
json_schema: {
|
|
275
|
+
name: schemaName,
|
|
276
|
+
strict: true,
|
|
277
|
+
schema: jsonSchema
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Parse and validate model response content.
|
|
284
|
+
* With native support, content is guaranteed JSON — but we still validate with Zod
|
|
285
|
+
* for type safety and field-level error reporting.
|
|
286
|
+
*/
|
|
287
|
+
extractOutput(content, schema) {
|
|
288
|
+
try {
|
|
289
|
+
const raw = JSON.parse(content);
|
|
290
|
+
const validated = schema.parse(raw);
|
|
291
|
+
return { data: validated };
|
|
292
|
+
} catch (e) {
|
|
293
|
+
if (e?.issues) {
|
|
294
|
+
return { error: formatZodError(e, content) };
|
|
295
|
+
}
|
|
296
|
+
return {
|
|
297
|
+
error: {
|
|
298
|
+
type: "parse_error",
|
|
299
|
+
message: `Failed to parse native structured output: ${e?.message}`,
|
|
300
|
+
rawContent: content
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
buildCorrectionPrompt(error, previousAttempt) {
|
|
306
|
+
const fieldIssues = error.issues.map((issue) => {
|
|
307
|
+
const pathStr = issue.path.join(".") || "root";
|
|
308
|
+
const received = getNestedValue(previousAttempt, issue.path);
|
|
309
|
+
return `- "${pathStr}": ${issue.message} (received: ${JSON.stringify(received)})`;
|
|
310
|
+
}).join("\n");
|
|
311
|
+
return [
|
|
312
|
+
"Your previous JSON response had validation errors:",
|
|
313
|
+
fieldIssues,
|
|
314
|
+
"",
|
|
315
|
+
"Please output corrected JSON matching the schema."
|
|
316
|
+
].join("\n");
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
// src/tool.ts
|
|
321
|
+
function createTool({
|
|
322
|
+
name,
|
|
323
|
+
description,
|
|
324
|
+
schema,
|
|
325
|
+
func,
|
|
326
|
+
needsApproval,
|
|
327
|
+
approvalPrompt,
|
|
328
|
+
approvalDefaults,
|
|
329
|
+
maxExecutionsPerRun
|
|
330
|
+
}) {
|
|
331
|
+
const execute = async (input) => func(input);
|
|
332
|
+
const toolRecord = {
|
|
333
|
+
name,
|
|
334
|
+
description,
|
|
335
|
+
schema,
|
|
336
|
+
// Our runtime prefers invoke but fallback helpers keep compatibility with LC-style tools.
|
|
337
|
+
invoke: execute,
|
|
338
|
+
call: execute,
|
|
339
|
+
run: execute,
|
|
340
|
+
func: execute
|
|
341
|
+
};
|
|
342
|
+
if (typeof needsApproval === "boolean") {
|
|
343
|
+
toolRecord.needsApproval = needsApproval;
|
|
344
|
+
}
|
|
345
|
+
if (approvalPrompt !== void 0) {
|
|
346
|
+
toolRecord.approvalPrompt = approvalPrompt;
|
|
347
|
+
}
|
|
348
|
+
if (approvalDefaults !== void 0) {
|
|
349
|
+
toolRecord.approvalDefaults = approvalDefaults;
|
|
350
|
+
}
|
|
351
|
+
if (maxExecutionsPerRun !== void 0) {
|
|
352
|
+
toolRecord.maxExecutionsPerRun = maxExecutionsPerRun;
|
|
353
|
+
}
|
|
354
|
+
toolRecord.__source = toolRecord.__source || "smart";
|
|
355
|
+
toolRecord.__impl = func;
|
|
356
|
+
return toolRecord;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// src/structuredOutput/toolStrategy.ts
|
|
360
|
+
var ToolBasedStrategy = class {
|
|
361
|
+
constructor() {
|
|
362
|
+
this.kind = "tool_based";
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Create the `response` tool for a given Zod schema.
|
|
366
|
+
* The tool validates input against the schema and returns a sentinel object
|
|
367
|
+
* that the tools node picks up to finalize the structured output.
|
|
368
|
+
*/
|
|
369
|
+
createResponseTool(schema) {
|
|
370
|
+
return createTool({
|
|
371
|
+
name: "response",
|
|
372
|
+
description: "Finalize the answer by returning the final structured JSON matching the required schema. Call exactly once when you are fully done, then stop.",
|
|
373
|
+
schema,
|
|
374
|
+
func: async (data) => {
|
|
375
|
+
try {
|
|
376
|
+
const validated = schema.parse ? schema.parse(data) : data;
|
|
377
|
+
return { __finalStructuredOutput: true, data: validated };
|
|
378
|
+
} catch (e) {
|
|
379
|
+
const zodError = e;
|
|
380
|
+
if (zodError?.issues) {
|
|
381
|
+
const soError = formatZodError(zodError);
|
|
382
|
+
return {
|
|
383
|
+
error: "Schema validation failed",
|
|
384
|
+
details: soError.message,
|
|
385
|
+
fieldErrors: soError.fieldErrors
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
return { error: "Schema validation failed", details: e?.message };
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Try to extract structured output from raw assistant text content.
|
|
395
|
+
* Used as a last resort when the model writes JSON directly instead of calling the tool.
|
|
396
|
+
*/
|
|
397
|
+
extractFromText(content, schema) {
|
|
398
|
+
let jsonText = null;
|
|
399
|
+
const fenced = content.match(/```(?:json)?\n([\s\S]*?)```/i);
|
|
400
|
+
if (fenced && fenced[1]) {
|
|
401
|
+
jsonText = fenced[1].trim();
|
|
402
|
+
} else {
|
|
403
|
+
const braceIdx = content.indexOf("{");
|
|
404
|
+
const bracketIdx = content.indexOf("[");
|
|
405
|
+
const candidates = [braceIdx, bracketIdx].filter((i) => i >= 0).sort((a, b) => a - b);
|
|
406
|
+
if (candidates.length > 0) {
|
|
407
|
+
jsonText = content.slice(candidates[0]).trim();
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
try {
|
|
411
|
+
const raw = JSON.parse(jsonText ?? content);
|
|
412
|
+
const validated = schema.parse(raw);
|
|
413
|
+
return { data: validated };
|
|
414
|
+
} catch (e) {
|
|
415
|
+
if (e?.issues) {
|
|
416
|
+
return { error: formatZodError(e, content) };
|
|
417
|
+
}
|
|
418
|
+
return {
|
|
419
|
+
error: {
|
|
420
|
+
type: "parse_error",
|
|
421
|
+
message: `Failed to parse JSON from assistant message: ${e?.message}`,
|
|
422
|
+
rawContent: content
|
|
423
|
+
}
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
buildCorrectionPrompt(error, previousAttempt) {
|
|
428
|
+
const fieldIssues = error.issues.map((issue) => {
|
|
429
|
+
const pathStr = issue.path.join(".") || "root";
|
|
430
|
+
const received = getNestedValue(previousAttempt, issue.path);
|
|
431
|
+
return `- "${pathStr}": ${issue.message} (received: ${JSON.stringify(received)})`;
|
|
432
|
+
}).join("\n");
|
|
433
|
+
return [
|
|
434
|
+
"Your previous structured output had validation errors:",
|
|
435
|
+
fieldIssues,
|
|
436
|
+
"",
|
|
437
|
+
"Please call `response` again with corrected values matching the schema."
|
|
438
|
+
].join("\n");
|
|
439
|
+
}
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
// src/structuredOutput/resolver.ts
|
|
443
|
+
function resolveStrategy(model) {
|
|
444
|
+
const capabilities = getModelCapabilities(model);
|
|
445
|
+
if (capabilities.structuredOutput === "native") {
|
|
446
|
+
return new NativeJsonSchemaStrategy();
|
|
447
|
+
}
|
|
448
|
+
return new ToolBasedStrategy();
|
|
449
|
+
}
|
|
450
|
+
function getModelCapabilities(model) {
|
|
451
|
+
if (model?.capabilities && typeof model.capabilities === "object") {
|
|
452
|
+
return model.capabilities;
|
|
453
|
+
}
|
|
454
|
+
const provider = detectProvider(model);
|
|
455
|
+
if (provider) {
|
|
456
|
+
return {
|
|
457
|
+
provider,
|
|
458
|
+
structuredOutput: supportsNativeStructuredOutput(provider) ? "native" : "tool_based",
|
|
459
|
+
strictToolCalling: requiresStrictToolCalling(provider),
|
|
460
|
+
streaming: true
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
return { structuredOutput: "tool_based" };
|
|
464
|
+
}
|
|
465
|
+
function detectProvider(model) {
|
|
466
|
+
if (!model || typeof model !== "object") return void 0;
|
|
467
|
+
if (typeof model.provider === "string") return model.provider;
|
|
468
|
+
const lcModel = model._lc;
|
|
469
|
+
if (lcModel) {
|
|
470
|
+
return detectProviderFromLCModel(lcModel);
|
|
471
|
+
}
|
|
472
|
+
const modelName = model.modelName || model.model || "";
|
|
473
|
+
if (modelName.startsWith("gpt-") || modelName.startsWith("o1") || modelName.startsWith("o3") || modelName.startsWith("o4")) return "openai";
|
|
474
|
+
if (modelName.startsWith("claude")) return "anthropic";
|
|
475
|
+
if (modelName.startsWith("gemini")) return "google";
|
|
476
|
+
if (modelName.startsWith("amazon.") || modelName.startsWith("anthropic.")) return "bedrock";
|
|
477
|
+
return void 0;
|
|
478
|
+
}
|
|
479
|
+
function detectProviderFromLCModel(lcModel) {
|
|
480
|
+
if (!lcModel) return void 0;
|
|
481
|
+
const className = lcModel.constructor?.name || "";
|
|
482
|
+
if (className.includes("ChatOpenAI") || className.includes("AzureChatOpenAI")) return "openai";
|
|
483
|
+
if (className.includes("ChatAnthropic")) return "anthropic";
|
|
484
|
+
if (className.includes("ChatGoogleGenerativeAI") || className.includes("ChatVertexAI")) return "google";
|
|
485
|
+
if (className.includes("ChatBedrock") || className.includes("BedrockChat")) return "bedrock";
|
|
486
|
+
if (className.includes("ChatGroq")) return "groq";
|
|
487
|
+
if (className.includes("ChatMistralAI")) return "mistral";
|
|
488
|
+
const llmType = typeof lcModel._llmType === "function" ? lcModel._llmType() : lcModel._llmType;
|
|
489
|
+
if (typeof llmType === "string") {
|
|
490
|
+
if (llmType.includes("openai")) return "openai";
|
|
491
|
+
if (llmType.includes("anthropic")) return "anthropic";
|
|
492
|
+
if (llmType.includes("google")) return "google";
|
|
493
|
+
}
|
|
494
|
+
return void 0;
|
|
495
|
+
}
|
|
496
|
+
function supportsNativeStructuredOutput(provider) {
|
|
497
|
+
return ["openai", "google", "groq", "mistral"].includes(provider);
|
|
498
|
+
}
|
|
499
|
+
function requiresStrictToolCalling(provider) {
|
|
500
|
+
return ["openai"].includes(provider);
|
|
501
|
+
}
|
|
502
|
+
|
|
137
503
|
// src/model.ts
|
|
138
504
|
function isSmartModel(m) {
|
|
139
505
|
return !!m && typeof m === "object" && typeof m.invoke === "function";
|
|
140
506
|
}
|
|
141
|
-
function withTools(model, tools) {
|
|
142
|
-
if (model?.bindTools) return model.bindTools(tools);
|
|
507
|
+
function withTools(model, tools, options) {
|
|
508
|
+
if (model?.bindTools) return model.bindTools(tools, options);
|
|
143
509
|
return model;
|
|
144
510
|
}
|
|
145
511
|
function fromLangchainModel(lcModel) {
|
|
@@ -184,7 +550,8 @@ function fromLangchainModel(lcModel) {
|
|
|
184
550
|
role: response.role || "assistant",
|
|
185
551
|
content,
|
|
186
552
|
tool_calls: response.tool_calls,
|
|
187
|
-
usage: response.usage,
|
|
553
|
+
usage: response.usage ?? response.usage_metadata ?? response.response_metadata?.token_usage ?? response.response_metadata?.tokenUsage,
|
|
554
|
+
usage_metadata: response.usage_metadata,
|
|
188
555
|
response_metadata: response.response_metadata,
|
|
189
556
|
...response
|
|
190
557
|
};
|
|
@@ -200,14 +567,14 @@ function fromLangchainModel(lcModel) {
|
|
|
200
567
|
yield chunk;
|
|
201
568
|
}
|
|
202
569
|
},
|
|
203
|
-
bindTools: (tools) => {
|
|
570
|
+
bindTools: (tools, options) => {
|
|
204
571
|
const lcReady = toLangchainTools(tools);
|
|
205
572
|
if (typeof lcModel.bindTools === "function") {
|
|
206
|
-
const bound = lcModel.bindTools(lcReady);
|
|
573
|
+
const bound = lcModel.bindTools(lcReady, options);
|
|
207
574
|
return fromLangchainModel(bound);
|
|
208
575
|
}
|
|
209
576
|
if (typeof lcModel.bind === "function") {
|
|
210
|
-
const bound = lcModel.bind({ tools: lcReady });
|
|
577
|
+
const bound = lcModel.bind({ tools: lcReady, ...options || {} });
|
|
211
578
|
return fromLangchainModel(bound);
|
|
212
579
|
}
|
|
213
580
|
return adapted;
|
|
@@ -215,48 +582,10 @@ function fromLangchainModel(lcModel) {
|
|
|
215
582
|
modelName: lcModel.modelName || lcModel._modelId || lcModel._llmType || lcModel.name,
|
|
216
583
|
_lc: lcModel
|
|
217
584
|
};
|
|
585
|
+
adapted.capabilities = getModelCapabilities(adapted);
|
|
218
586
|
return adapted;
|
|
219
587
|
}
|
|
220
588
|
|
|
221
|
-
// src/tool.ts
|
|
222
|
-
function createTool({
|
|
223
|
-
name,
|
|
224
|
-
description,
|
|
225
|
-
schema,
|
|
226
|
-
func,
|
|
227
|
-
needsApproval,
|
|
228
|
-
approvalPrompt,
|
|
229
|
-
approvalDefaults,
|
|
230
|
-
maxExecutionsPerRun
|
|
231
|
-
}) {
|
|
232
|
-
const execute = async (input) => func(input);
|
|
233
|
-
const toolRecord = {
|
|
234
|
-
name,
|
|
235
|
-
description,
|
|
236
|
-
schema,
|
|
237
|
-
// Our runtime prefers invoke but fallback helpers keep compatibility with LC-style tools.
|
|
238
|
-
invoke: execute,
|
|
239
|
-
call: execute,
|
|
240
|
-
run: execute,
|
|
241
|
-
func: execute
|
|
242
|
-
};
|
|
243
|
-
if (typeof needsApproval === "boolean") {
|
|
244
|
-
toolRecord.needsApproval = needsApproval;
|
|
245
|
-
}
|
|
246
|
-
if (approvalPrompt !== void 0) {
|
|
247
|
-
toolRecord.approvalPrompt = approvalPrompt;
|
|
248
|
-
}
|
|
249
|
-
if (approvalDefaults !== void 0) {
|
|
250
|
-
toolRecord.approvalDefaults = approvalDefaults;
|
|
251
|
-
}
|
|
252
|
-
if (maxExecutionsPerRun !== void 0) {
|
|
253
|
-
toolRecord.maxExecutionsPerRun = maxExecutionsPerRun;
|
|
254
|
-
}
|
|
255
|
-
toolRecord.__source = toolRecord.__source || "smart";
|
|
256
|
-
toolRecord.__impl = func;
|
|
257
|
-
return toolRecord;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
589
|
// src/prompts.ts
|
|
261
590
|
var DEFAULT_TODO_LIST_PROMPT = `Planning tools are available when they materially improve execution.
|
|
262
591
|
|
|
@@ -339,23 +668,39 @@ function normalizeUsage(raw) {
|
|
|
339
668
|
raw.totalTokens,
|
|
340
669
|
(typeof prompt === "number" ? prompt : 0) + (typeof completion === "number" ? completion : 0)
|
|
341
670
|
);
|
|
342
|
-
const promptDetailsSrc = raw.prompt_tokens_details || raw.promptTokensDetails || raw.input_token_details || raw.inputTokenDetails || {};
|
|
343
|
-
const completionDetailsSrc = raw.completion_tokens_details || raw.completionTokensDetails || raw.output_token_details || raw.outputTokenDetails || {};
|
|
671
|
+
const promptDetailsSrc = raw.prompt_tokens_details || raw.promptTokensDetails || raw.input_token_details || raw.inputTokenDetails || raw.input_tokens_details || raw.inputTokensDetails || {};
|
|
672
|
+
const completionDetailsSrc = raw.completion_tokens_details || raw.completionTokensDetails || raw.output_token_details || raw.outputTokenDetails || raw.output_tokens_details || raw.outputTokensDetails || {};
|
|
673
|
+
const cachedTokens = num(
|
|
674
|
+
promptDetailsSrc.cached_tokens,
|
|
675
|
+
promptDetailsSrc.cachedTokens,
|
|
676
|
+
promptDetailsSrc.cached,
|
|
677
|
+
promptDetailsSrc.cache_read,
|
|
678
|
+
promptDetailsSrc.cacheRead,
|
|
679
|
+
promptDetailsSrc.cache_read_tokens,
|
|
680
|
+
raw.cached_input_tokens,
|
|
681
|
+
raw.cachedInputTokens,
|
|
682
|
+
raw.cached_prompt_tokens,
|
|
683
|
+
raw.cachedPromptTokens,
|
|
684
|
+
raw.cache_read_input_tokens,
|
|
685
|
+
raw.cacheReadInputTokens
|
|
686
|
+
);
|
|
344
687
|
const normalized = {
|
|
345
688
|
prompt_tokens: safe(prompt),
|
|
346
689
|
completion_tokens: safe(completion),
|
|
347
690
|
total_tokens: safe(total || (prompt || 0) + (completion || 0)),
|
|
348
691
|
prompt_tokens_details: {
|
|
349
|
-
cached_tokens: safe(
|
|
350
|
-
|
|
351
|
-
),
|
|
352
|
-
audio_tokens: safe(promptDetailsSrc.audio_tokens || promptDetailsSrc.audio)
|
|
692
|
+
cached_tokens: safe(cachedTokens),
|
|
693
|
+
audio_tokens: safe(num(promptDetailsSrc.audio_tokens, promptDetailsSrc.audioTokens, promptDetailsSrc.audio))
|
|
353
694
|
},
|
|
354
695
|
completion_tokens_details: {
|
|
355
|
-
reasoning_tokens: safe(completionDetailsSrc.reasoning_tokens),
|
|
356
|
-
audio_tokens: safe(completionDetailsSrc.audio_tokens),
|
|
357
|
-
accepted_prediction_tokens: safe(
|
|
358
|
-
|
|
696
|
+
reasoning_tokens: safe(num(completionDetailsSrc.reasoning_tokens, completionDetailsSrc.reasoningTokens)),
|
|
697
|
+
audio_tokens: safe(num(completionDetailsSrc.audio_tokens, completionDetailsSrc.audioTokens)),
|
|
698
|
+
accepted_prediction_tokens: safe(
|
|
699
|
+
num(completionDetailsSrc.accepted_prediction_tokens, completionDetailsSrc.acceptedPredictionTokens)
|
|
700
|
+
),
|
|
701
|
+
rejected_prediction_tokens: safe(
|
|
702
|
+
num(completionDetailsSrc.rejected_prediction_tokens, completionDetailsSrc.rejectedPredictionTokens)
|
|
703
|
+
)
|
|
359
704
|
},
|
|
360
705
|
raw
|
|
361
706
|
};
|
|
@@ -465,21 +810,21 @@ var METADATA_PATHS = {
|
|
|
465
810
|
"lc_kwargs.provider"
|
|
466
811
|
]
|
|
467
812
|
};
|
|
468
|
-
function
|
|
813
|
+
function getNestedValue2(obj, path2) {
|
|
469
814
|
if (!obj || typeof path2 !== "string") return void 0;
|
|
470
815
|
return path2.split(".").reduce((current, key) => current?.[key], obj);
|
|
471
816
|
}
|
|
472
817
|
function extractMetadata(model, paths) {
|
|
473
818
|
if (!model) return void 0;
|
|
474
819
|
for (const path2 of paths) {
|
|
475
|
-
const value =
|
|
820
|
+
const value = getNestedValue2(model, path2);
|
|
476
821
|
const str = coerceToString(value);
|
|
477
822
|
if (str) return str;
|
|
478
823
|
}
|
|
479
824
|
const maybeLc = model?._lc;
|
|
480
825
|
if (maybeLc) {
|
|
481
826
|
for (const path2 of paths) {
|
|
482
|
-
const value =
|
|
827
|
+
const value = getNestedValue2(maybeLc, path2);
|
|
483
828
|
const str = coerceToString(value);
|
|
484
829
|
if (str) return str;
|
|
485
830
|
}
|
|
@@ -487,7 +832,7 @@ function extractMetadata(model, paths) {
|
|
|
487
832
|
const maybeClient = model?.client || model?.api || model?.service;
|
|
488
833
|
if (maybeClient) {
|
|
489
834
|
for (const path2 of paths) {
|
|
490
|
-
const value =
|
|
835
|
+
const value = getNestedValue2(maybeClient, path2);
|
|
491
836
|
const str = coerceToString(value);
|
|
492
837
|
if (str) return str;
|
|
493
838
|
}
|
|
@@ -519,7 +864,7 @@ function inferModelFromMessages(messageList) {
|
|
|
519
864
|
const message = messageList[i];
|
|
520
865
|
if (!message) continue;
|
|
521
866
|
for (const path2 of messagePaths) {
|
|
522
|
-
const value =
|
|
867
|
+
const value = getNestedValue2(message, path2);
|
|
523
868
|
const str = coerceToString(value);
|
|
524
869
|
if (str) return str;
|
|
525
870
|
}
|
|
@@ -538,7 +883,7 @@ function inferProviderFromMessages(messageList) {
|
|
|
538
883
|
const message = messageList[i];
|
|
539
884
|
if (!message) continue;
|
|
540
885
|
for (const path2 of messagePaths) {
|
|
541
|
-
const value =
|
|
886
|
+
const value = getNestedValue2(message, path2);
|
|
542
887
|
const str = coerceToString(value);
|
|
543
888
|
if (str) return str;
|
|
544
889
|
}
|
|
@@ -1549,7 +1894,13 @@ function createAgentCoreNode(opts) {
|
|
|
1549
1894
|
tracing: opts.tracing
|
|
1550
1895
|
};
|
|
1551
1896
|
const tools = runtime.tools ?? [];
|
|
1552
|
-
const
|
|
1897
|
+
const shouldUseStrictToolCalling = Boolean(
|
|
1898
|
+
runtime.responseFormat && runtime.model?.capabilities?.strictToolCalling
|
|
1899
|
+
);
|
|
1900
|
+
const modelWithTools = runtime.model?.bindTools ? runtime.model.bindTools(
|
|
1901
|
+
tools,
|
|
1902
|
+
shouldUseStrictToolCalling ? { strict: true } : void 0
|
|
1903
|
+
) : runtime.model;
|
|
1553
1904
|
const traceSession = state.ctx?.__traceSession;
|
|
1554
1905
|
const actorName = runtime.name ?? opts.name ?? "agent";
|
|
1555
1906
|
const actorVersion = runtime.version ?? opts.version;
|
|
@@ -1626,6 +1977,7 @@ function createAgentCoreNode(opts) {
|
|
|
1626
1977
|
const streamingEnabled = Boolean(state.ctx?.__streaming);
|
|
1627
1978
|
const cancellationToken = state.ctx?.__cancellationToken;
|
|
1628
1979
|
const abortSignal = state.ctx?.__abortSignal;
|
|
1980
|
+
const responseFormat = runtime.responseFormat;
|
|
1629
1981
|
const extractText = (chunk) => {
|
|
1630
1982
|
if (chunk == null) return "";
|
|
1631
1983
|
if (typeof chunk === "string") return chunk;
|
|
@@ -1644,7 +1996,8 @@ function createAgentCoreNode(opts) {
|
|
|
1644
1996
|
if (streamingEnabled && typeof modelWithTools.stream === "function") {
|
|
1645
1997
|
let streamedText = "";
|
|
1646
1998
|
let streamedMessage;
|
|
1647
|
-
|
|
1999
|
+
const streamOptions = { signal: abortSignal, cancellationToken, ...responseFormat };
|
|
2000
|
+
for await (const chunk of modelWithTools.stream(normalizedMessages, streamOptions)) {
|
|
1648
2001
|
if (cancellationToken && cancellationToken.isCancellationRequested || abortSignal?.aborted) {
|
|
1649
2002
|
break;
|
|
1650
2003
|
}
|
|
@@ -1667,7 +2020,7 @@ function createAgentCoreNode(opts) {
|
|
|
1667
2020
|
response = { role: "assistant", content: streamedText };
|
|
1668
2021
|
}
|
|
1669
2022
|
} else {
|
|
1670
|
-
response = await modelWithTools.invoke(normalizedMessages, { signal: abortSignal, cancellationToken });
|
|
2023
|
+
response = await modelWithTools.invoke(normalizedMessages, { signal: abortSignal, cancellationToken, ...responseFormat });
|
|
1671
2024
|
}
|
|
1672
2025
|
} catch (err) {
|
|
1673
2026
|
const durationMs2 = Date.now() - start;
|
|
@@ -1689,7 +2042,7 @@ function createAgentCoreNode(opts) {
|
|
|
1689
2042
|
...state.messages,
|
|
1690
2043
|
response
|
|
1691
2044
|
];
|
|
1692
|
-
const rawUsage = response?.usage || response?.response_metadata?.token_usage || response?.response_metadata?.tokenUsage || response?.response_metadata?.usage;
|
|
2045
|
+
const rawUsage = response?.usage || response?.usage_metadata || response?.response_metadata?.token_usage || response?.response_metadata?.tokenUsage || response?.response_metadata?.usage || response?.response_metadata?.usage_metadata || response?.response_metadata?.usageMetadata;
|
|
1693
2046
|
const normalized = normalizeUsage(rawUsage);
|
|
1694
2047
|
const modelName = getModelName(runtime.model || opts.model) || "unknown_model";
|
|
1695
2048
|
const durationMs = Date.now() - start;
|
|
@@ -2214,6 +2567,16 @@ function countSuccessfulToolExecutions(toolHistory, toolName) {
|
|
|
2214
2567
|
if (!Array.isArray(toolHistory)) return 0;
|
|
2215
2568
|
return toolHistory.filter((entry) => entry?.toolName === toolName && (entry?.status === "success" || entry?.status === "handoff")).length;
|
|
2216
2569
|
}
|
|
2570
|
+
function normalizeToolCall(call) {
|
|
2571
|
+
if (!call || typeof call !== "object") return null;
|
|
2572
|
+
const name = call.name ?? call.tool ?? call.function?.name ?? call.function_call?.name;
|
|
2573
|
+
if (typeof name !== "string" || name.length === 0) return null;
|
|
2574
|
+
return {
|
|
2575
|
+
id: call.id,
|
|
2576
|
+
name,
|
|
2577
|
+
args: call.args ?? call.arguments ?? call.input ?? call.function?.arguments ?? call.function_call?.arguments
|
|
2578
|
+
};
|
|
2579
|
+
}
|
|
2217
2580
|
function createToolsNode(initialTools, opts) {
|
|
2218
2581
|
return async (state) => {
|
|
2219
2582
|
const runtime = state.agent || {
|
|
@@ -2248,12 +2611,14 @@ function createToolsNode(initialTools, opts) {
|
|
|
2248
2611
|
if (anyTool._stateRef && typeof anyTool._stateRef === "object") {
|
|
2249
2612
|
anyTool._stateRef.toolHistory = state.toolHistory;
|
|
2250
2613
|
anyTool._stateRef.toolHistoryArchived = state.toolHistoryArchived;
|
|
2614
|
+
anyTool._stateRef.pendingApprovals = pendingApprovals;
|
|
2615
|
+
anyTool._stateRef.ctx = state.ctx || (state.ctx = {});
|
|
2251
2616
|
anyTool._stateRef.__onEvent = onEvent;
|
|
2252
2617
|
}
|
|
2253
2618
|
}
|
|
2254
2619
|
const last = state.messages[state.messages.length - 1];
|
|
2255
2620
|
let toolCount = state.toolCallCount || 0;
|
|
2256
|
-
const toolCalls = Array.isArray(last?.tool_calls) ? last.tool_calls : [];
|
|
2621
|
+
const toolCalls = Array.isArray(last?.tool_calls) ? last.tool_calls.map((toolCall) => normalizeToolCall(toolCall)).filter((toolCall) => toolCall !== null) : [];
|
|
2257
2622
|
const toolHistory = state.toolHistory || [];
|
|
2258
2623
|
const toolHistoryArchived = state.toolHistoryArchived || [];
|
|
2259
2624
|
const remaining = Math.max(0, limits.maxToolCalls - toolCount);
|
|
@@ -2312,6 +2677,13 @@ function createToolsNode(initialTools, opts) {
|
|
|
2312
2677
|
args = validatedArgs.value;
|
|
2313
2678
|
}
|
|
2314
2679
|
const toolName = tool.name || tc.name;
|
|
2680
|
+
const toolStateRef = tool._stateRef && typeof tool._stateRef === "object" ? tool._stateRef : null;
|
|
2681
|
+
if (toolStateRef) {
|
|
2682
|
+
toolStateRef.pendingApprovals = pendingApprovals;
|
|
2683
|
+
toolStateRef.ctx = state.ctx || (state.ctx = {});
|
|
2684
|
+
toolStateRef.__currentToolCallId = toolCallId;
|
|
2685
|
+
delete toolStateRef.__awaitingApproval;
|
|
2686
|
+
}
|
|
2315
2687
|
const maxExecutionsPerRun = normalizeMaxExecutionsPerRun(tool.maxExecutionsPerRun);
|
|
2316
2688
|
if (maxExecutionsPerRun !== null) {
|
|
2317
2689
|
const currentExecutions = countSuccessfulToolExecutions(toolHistory, toolName);
|
|
@@ -2424,6 +2796,22 @@ function createToolsNode(initialTools, opts) {
|
|
|
2424
2796
|
toolCount += 1;
|
|
2425
2797
|
return { status: "success", approval: approvalEntry };
|
|
2426
2798
|
}
|
|
2799
|
+
if (output && typeof output === "object" && output.__awaitingToolApproval) {
|
|
2800
|
+
if (toolStateRef?.ctx && typeof toolStateRef.ctx === "object") {
|
|
2801
|
+
state.ctx = toolStateRef.ctx;
|
|
2802
|
+
}
|
|
2803
|
+
const pendingApproval = pendingApprovals.find(
|
|
2804
|
+
(entry) => entry.toolCallId === output.toolCallId || entry.id === output.approvalId
|
|
2805
|
+
);
|
|
2806
|
+
if (!pendingApproval) {
|
|
2807
|
+
throw new Error(`Tool ${toolName} requested approval but no pending approval entry was recorded.`);
|
|
2808
|
+
}
|
|
2809
|
+
awaitingApproval = true;
|
|
2810
|
+
return {
|
|
2811
|
+
status: "awaiting_approval",
|
|
2812
|
+
approval: pendingApproval
|
|
2813
|
+
};
|
|
2814
|
+
}
|
|
2427
2815
|
if (output && typeof output === "object" && output.__finalStructuredOutput) {
|
|
2428
2816
|
if (!state.ctx) state.ctx = {};
|
|
2429
2817
|
state.ctx.__structuredOutputParsed = output.data;
|
|
@@ -2846,6 +3234,153 @@ function resolveToolApprovalState(state, resolution) {
|
|
|
2846
3234
|
};
|
|
2847
3235
|
}
|
|
2848
3236
|
|
|
3237
|
+
// src/structuredOutput/manager.ts
|
|
3238
|
+
var StructuredOutputManager = class {
|
|
3239
|
+
constructor(schema, strategy, config) {
|
|
3240
|
+
this.schema = schema;
|
|
3241
|
+
this.strategy = strategy;
|
|
3242
|
+
this.maxRetries = config?.maxRetries ?? 2;
|
|
3243
|
+
}
|
|
3244
|
+
// --- Setup Phase ---
|
|
3245
|
+
/**
|
|
3246
|
+
* Get the `response` tool for tool-based strategy.
|
|
3247
|
+
* Returns undefined for native strategy (no tool needed).
|
|
3248
|
+
*/
|
|
3249
|
+
getResponseTool() {
|
|
3250
|
+
if (this.strategy.kind !== "tool_based") return void 0;
|
|
3251
|
+
return this.strategy.createResponseTool(this.schema);
|
|
3252
|
+
}
|
|
3253
|
+
/**
|
|
3254
|
+
* Get response_format config for native strategy.
|
|
3255
|
+
* Returns undefined for tool-based strategy.
|
|
3256
|
+
*/
|
|
3257
|
+
getResponseFormat() {
|
|
3258
|
+
if (this.strategy.kind !== "native") return void 0;
|
|
3259
|
+
return this.strategy.buildResponseFormat(this.schema);
|
|
3260
|
+
}
|
|
3261
|
+
/**
|
|
3262
|
+
* Build the system prompt hint that tells the model how to produce structured output.
|
|
3263
|
+
*/
|
|
3264
|
+
buildSystemPromptHint() {
|
|
3265
|
+
if (this.strategy.kind === "native") {
|
|
3266
|
+
return [
|
|
3267
|
+
"A structured output schema is active.",
|
|
3268
|
+
"Your response MUST be valid JSON matching the output schema.",
|
|
3269
|
+
"Do NOT wrap the JSON in code fences or add any text before/after it."
|
|
3270
|
+
].join("\n");
|
|
3271
|
+
}
|
|
3272
|
+
return [
|
|
3273
|
+
"A structured output schema is active.",
|
|
3274
|
+
"Do NOT output the final JSON directly as an assistant message.",
|
|
3275
|
+
"When completely finished, call tool `response` passing the final JSON matching the schema as its arguments (direct object).",
|
|
3276
|
+
"Call it exactly once then STOP producing further assistant messages."
|
|
3277
|
+
].join("\n");
|
|
3278
|
+
}
|
|
3279
|
+
/**
|
|
3280
|
+
* Build a nudge message for when the model didn't produce structured output.
|
|
3281
|
+
* Used by the agent loop when it needs to retry.
|
|
3282
|
+
*/
|
|
3283
|
+
buildNudgeMessage(isLastAttempt) {
|
|
3284
|
+
if (this.strategy.kind === "native") {
|
|
3285
|
+
return {
|
|
3286
|
+
role: "system",
|
|
3287
|
+
content: [
|
|
3288
|
+
"A structured output schema is active.",
|
|
3289
|
+
"You MUST respond with valid JSON matching the schema.",
|
|
3290
|
+
"Do NOT include any text outside the JSON object.",
|
|
3291
|
+
isLastAttempt ? "This is your FINAL attempt. Produce the JSON NOW." : ""
|
|
3292
|
+
].filter(Boolean).join("\n")
|
|
3293
|
+
};
|
|
3294
|
+
}
|
|
3295
|
+
return {
|
|
3296
|
+
role: "system",
|
|
3297
|
+
content: [
|
|
3298
|
+
"A structured output schema is active.",
|
|
3299
|
+
"You MUST now call tool `response` with the final JSON object that matches the schema.",
|
|
3300
|
+
"Do not write the JSON in the assistant message.",
|
|
3301
|
+
"Call `response` exactly once, then stop.",
|
|
3302
|
+
isLastAttempt ? "Do NOT call any other tools. Produce the response NOW based on whatever information you have gathered so far." : ""
|
|
3303
|
+
].filter(Boolean).join("\n")
|
|
3304
|
+
};
|
|
3305
|
+
}
|
|
3306
|
+
/**
|
|
3307
|
+
* Build a correction message when schema validation failed.
|
|
3308
|
+
*/
|
|
3309
|
+
buildCorrectionMessage(error, _previousAttempt) {
|
|
3310
|
+
if (error.fieldErrors && error.fieldErrors.length > 0) {
|
|
3311
|
+
const fieldIssues = error.fieldErrors.map((f) => `- "${f.path}": ${f.message} (received: ${JSON.stringify(f.received)})`).join("\n");
|
|
3312
|
+
const action = this.strategy.kind === "native" ? "Please output corrected JSON matching the schema." : "Please call `response` again with corrected values matching the schema.";
|
|
3313
|
+
return {
|
|
3314
|
+
role: "system",
|
|
3315
|
+
content: [
|
|
3316
|
+
"Your previous structured output had validation errors:",
|
|
3317
|
+
fieldIssues,
|
|
3318
|
+
"",
|
|
3319
|
+
action
|
|
3320
|
+
].join("\n")
|
|
3321
|
+
};
|
|
3322
|
+
}
|
|
3323
|
+
return this.buildNudgeMessage(false);
|
|
3324
|
+
}
|
|
3325
|
+
// --- Parse Phase ---
|
|
3326
|
+
/**
|
|
3327
|
+
* Try to parse structured output from the tool-based finalization result.
|
|
3328
|
+
* Called when __structuredOutputParsed is available in ctx.
|
|
3329
|
+
*/
|
|
3330
|
+
parseFromToolResult(data) {
|
|
3331
|
+
return { success: true, data, attempts: 1 };
|
|
3332
|
+
}
|
|
3333
|
+
/**
|
|
3334
|
+
* Try to parse structured output from assistant message content.
|
|
3335
|
+
* For native strategy: content should be valid JSON.
|
|
3336
|
+
* For tool-based strategy: last-resort fallback extraction from text.
|
|
3337
|
+
*/
|
|
3338
|
+
parseFromContent(content) {
|
|
3339
|
+
if (this.strategy.kind === "native") {
|
|
3340
|
+
const result2 = this.strategy.extractOutput(content, this.schema);
|
|
3341
|
+
if ("data" in result2) {
|
|
3342
|
+
return { success: true, data: result2.data, attempts: 1 };
|
|
3343
|
+
}
|
|
3344
|
+
return { success: false, error: result2.error, attempts: 1, rawContent: content };
|
|
3345
|
+
}
|
|
3346
|
+
const result = this.strategy.extractFromText(content, this.schema);
|
|
3347
|
+
if ("data" in result) {
|
|
3348
|
+
return { success: true, data: result.data, attempts: 1 };
|
|
3349
|
+
}
|
|
3350
|
+
return { success: false, error: result.error, attempts: 1, rawContent: content };
|
|
3351
|
+
}
|
|
3352
|
+
/**
|
|
3353
|
+
* Create a "no output" error result.
|
|
3354
|
+
*/
|
|
3355
|
+
noOutputResult(attempts, rawContent) {
|
|
3356
|
+
return {
|
|
3357
|
+
success: false,
|
|
3358
|
+
error: {
|
|
3359
|
+
type: "no_output",
|
|
3360
|
+
message: "Model did not produce structured output after all retry attempts.",
|
|
3361
|
+
rawContent
|
|
3362
|
+
},
|
|
3363
|
+
attempts,
|
|
3364
|
+
rawContent
|
|
3365
|
+
};
|
|
3366
|
+
}
|
|
3367
|
+
/**
|
|
3368
|
+
* Create a "max retries" error result.
|
|
3369
|
+
*/
|
|
3370
|
+
maxRetriesResult(lastError, attempts, rawContent) {
|
|
3371
|
+
return {
|
|
3372
|
+
success: false,
|
|
3373
|
+
error: {
|
|
3374
|
+
...lastError,
|
|
3375
|
+
type: "max_retries",
|
|
3376
|
+
message: `Structured output failed after ${attempts} attempts: ${lastError.message}`
|
|
3377
|
+
},
|
|
3378
|
+
attempts,
|
|
3379
|
+
rawContent
|
|
3380
|
+
};
|
|
3381
|
+
}
|
|
3382
|
+
};
|
|
3383
|
+
|
|
2849
3384
|
// src/agent.ts
|
|
2850
3385
|
function isSyntheticSummaryMessage(message) {
|
|
2851
3386
|
if (!message) return false;
|
|
@@ -2897,14 +3432,13 @@ function createAgent(opts) {
|
|
|
2897
3432
|
const resolver = createResolverNode();
|
|
2898
3433
|
const agentCore = createAgentCoreNode(opts);
|
|
2899
3434
|
const toolsBase = [...opts.tools ?? []];
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
}
|
|
2907
|
-
toolsBase.push(responseTool);
|
|
3435
|
+
const soManager = opts.outputSchema ? new StructuredOutputManager(opts.outputSchema, resolveStrategy(opts.model)) : void 0;
|
|
3436
|
+
if (soManager) {
|
|
3437
|
+
const modelCapabilities = getModelCapabilities(opts.model);
|
|
3438
|
+
const responseTool = soManager.getResponseTool();
|
|
3439
|
+
if (responseTool && modelCapabilities.structuredOutput !== "native") {
|
|
3440
|
+
toolsBase.push(responseTool);
|
|
3441
|
+
}
|
|
2908
3442
|
}
|
|
2909
3443
|
const toolsNode = createToolsNode(toolsBase, opts);
|
|
2910
3444
|
const finalizeNode = createToolLimitFinalizeNode();
|
|
@@ -2939,6 +3473,7 @@ function createAgent(opts) {
|
|
|
2939
3473
|
limits: opts.limits,
|
|
2940
3474
|
useTodoList: void 0,
|
|
2941
3475
|
outputSchema: opts.outputSchema,
|
|
3476
|
+
responseFormat: soManager?.getResponseFormat(),
|
|
2942
3477
|
tracing: opts.tracing
|
|
2943
3478
|
};
|
|
2944
3479
|
const summarizationThreshold = getActiveSummarizationThreshold(opts);
|
|
@@ -3077,10 +3612,11 @@ function createAgent(opts) {
|
|
|
3077
3612
|
});
|
|
3078
3613
|
const tokenCount = countMessagesTokens(tokCountMessages);
|
|
3079
3614
|
if (tokenCount > summarizationThreshold) {
|
|
3615
|
+
const summarizationExhausted = !!state.ctx?.__summarizationExhausted;
|
|
3080
3616
|
const hasFreshSummary = (state.messages || []).some(
|
|
3081
3617
|
(m) => m.role === "tool" && typeof m.content === "string" && (m.content === "SUMMARIZED" || m.content.startsWith("SUMMARIZED_TOOL_RESPONSE"))
|
|
3082
3618
|
);
|
|
3083
|
-
if (hasFreshSummary && tokenCount <= summarizationThreshold * 1.15) ; else {
|
|
3619
|
+
if (hasFreshSummary && tokenCount <= summarizationThreshold * 1.15) ; else if (summarizationExhausted) ; else {
|
|
3084
3620
|
const ctx = { ...state.ctx || {}, __needsSummarization: true };
|
|
3085
3621
|
state = { ...state, ctx };
|
|
3086
3622
|
break;
|
|
@@ -3157,7 +3693,15 @@ function createAgent(opts) {
|
|
|
3157
3693
|
state = { ...state, ...await finalizeNode(state) };
|
|
3158
3694
|
continue;
|
|
3159
3695
|
}
|
|
3160
|
-
if (toolCalls.length === 0)
|
|
3696
|
+
if (toolCalls.length === 0) {
|
|
3697
|
+
if (soManager && !state.ctx?.__finalizedDueToStructuredOutput && !state.ctx?.__structuredOutputForceFinalize) {
|
|
3698
|
+
const nudge = soManager.buildNudgeMessage(false);
|
|
3699
|
+
const ctx = { ...state.ctx || {}, __structuredOutputForceFinalize: true };
|
|
3700
|
+
state = { ...state, messages: [...state.messages, nudge], ctx };
|
|
3701
|
+
continue;
|
|
3702
|
+
}
|
|
3703
|
+
break;
|
|
3704
|
+
}
|
|
3161
3705
|
onProgress?.({ stage: "tools", message: "Running tools" });
|
|
3162
3706
|
if (cancelIfRequested("before_tools")) break;
|
|
3163
3707
|
state = { ...state, ...await toolsNode(state) };
|
|
@@ -3166,35 +3710,28 @@ function createAgent(opts) {
|
|
|
3166
3710
|
if (checkpointIfRequested("after_tools")) break;
|
|
3167
3711
|
if (state.ctx?.__finalizedDueToStructuredOutput) break;
|
|
3168
3712
|
}
|
|
3169
|
-
if (
|
|
3170
|
-
const
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
const
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
try {
|
|
3187
|
-
state = { ...state, ...await agentCore(state) };
|
|
3188
|
-
const lastAfter = state.messages[state.messages.length - 1];
|
|
3189
|
-
const toolCallsAfter = Array.isArray(lastAfter?.tool_calls) ? lastAfter.tool_calls : [];
|
|
3190
|
-
if (toolCallsAfter.length > 0) {
|
|
3191
|
-
state = { ...state, ...await toolsNode(state) };
|
|
3192
|
-
}
|
|
3193
|
-
} catch (err) {
|
|
3194
|
-
const errMsg = err instanceof Error ? err.message : String(err);
|
|
3195
|
-
emit?.({ type: "metadata", error: `Structured output force-finalize failed: ${errMsg}` });
|
|
3196
|
-
throw err;
|
|
3713
|
+
if (soManager && !state.ctx?.__finalizedDueToStructuredOutput) {
|
|
3714
|
+
const maxPostLoopRetries = soManager.maxRetries;
|
|
3715
|
+
for (let postRetry = 0; postRetry < maxPostLoopRetries; postRetry++) {
|
|
3716
|
+
if (state.ctx?.__finalizedDueToStructuredOutput) break;
|
|
3717
|
+
const last = state.messages[state.messages.length - 1];
|
|
3718
|
+
const lastHasToolCalls = Array.isArray(last?.tool_calls) && last.tool_calls.length > 0;
|
|
3719
|
+
if (!lastHasToolCalls) {
|
|
3720
|
+
const isLastAttempt = postRetry === maxPostLoopRetries - 1;
|
|
3721
|
+
const nudge = soManager.buildNudgeMessage(isLastAttempt);
|
|
3722
|
+
state = { ...state, messages: [...state.messages, nudge] };
|
|
3723
|
+
}
|
|
3724
|
+
try {
|
|
3725
|
+
state = { ...state, ...await agentCore(state) };
|
|
3726
|
+
const lastAfter = state.messages[state.messages.length - 1];
|
|
3727
|
+
const toolCallsAfter = Array.isArray(lastAfter?.tool_calls) ? lastAfter.tool_calls : [];
|
|
3728
|
+
if (toolCallsAfter.length > 0) {
|
|
3729
|
+
state = { ...state, ...await toolsNode(state) };
|
|
3197
3730
|
}
|
|
3731
|
+
} catch (err) {
|
|
3732
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
3733
|
+
emit?.({ type: "metadata", error: `Structured output force-finalize failed: ${errMsg}` });
|
|
3734
|
+
throw err;
|
|
3198
3735
|
}
|
|
3199
3736
|
}
|
|
3200
3737
|
}
|
|
@@ -3284,23 +3821,22 @@ function createAgent(opts) {
|
|
|
3284
3821
|
const finalAssistantMsg = getLastAssistantMessage(res.messages);
|
|
3285
3822
|
const content = getMessageText(finalAssistantMsg);
|
|
3286
3823
|
let parsed = void 0;
|
|
3287
|
-
|
|
3288
|
-
if (
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
} catch {
|
|
3824
|
+
let outputError = void 0;
|
|
3825
|
+
if (soManager) {
|
|
3826
|
+
if (res.ctx?.__structuredOutputParsed) {
|
|
3827
|
+
parsed = res.ctx.__structuredOutputParsed;
|
|
3828
|
+
} else if (content) {
|
|
3829
|
+
const fallbackResult = soManager.parseFromContent(content);
|
|
3830
|
+
if (fallbackResult.success) {
|
|
3831
|
+
parsed = fallbackResult.data;
|
|
3832
|
+
} else {
|
|
3833
|
+
outputError = fallbackResult.error;
|
|
3834
|
+
}
|
|
3835
|
+
} else {
|
|
3836
|
+
const noResult = soManager.noOutputResult(1);
|
|
3837
|
+
if (!noResult.success) {
|
|
3838
|
+
outputError = noResult.error;
|
|
3839
|
+
}
|
|
3304
3840
|
}
|
|
3305
3841
|
}
|
|
3306
3842
|
emit({ type: "finalAnswer", content });
|
|
@@ -3311,6 +3847,7 @@ function createAgent(opts) {
|
|
|
3311
3847
|
return {
|
|
3312
3848
|
content,
|
|
3313
3849
|
output: parsed,
|
|
3850
|
+
outputError,
|
|
3314
3851
|
metadata: { usage: res.usage },
|
|
3315
3852
|
messages: res.messages,
|
|
3316
3853
|
state: res
|
|
@@ -3434,7 +3971,7 @@ function createAgentNode(opts) {
|
|
|
3434
3971
|
...messages,
|
|
3435
3972
|
response
|
|
3436
3973
|
];
|
|
3437
|
-
const rawUsage = response?.usage || response?.response_metadata?.token_usage || response?.response_metadata?.usage;
|
|
3974
|
+
const rawUsage = response?.usage || response?.usage_metadata || response?.response_metadata?.token_usage || response?.response_metadata?.tokenUsage || response?.response_metadata?.usage || response?.response_metadata?.usage_metadata || response?.response_metadata?.usageMetadata;
|
|
3438
3975
|
const normalized = normalizeUsage(rawUsage);
|
|
3439
3976
|
const modelName = getModelName(runtime.model || opts.model) || "unknown_model";
|
|
3440
3977
|
const providerName = getProviderName(runtime.model || opts.model);
|
|
@@ -3493,14 +4030,23 @@ function collectRecentTurns(messages, lastTurnsToKeep) {
|
|
|
3493
4030
|
if (lastTurnsToKeep <= 0) return [];
|
|
3494
4031
|
const systemPrefix = messages.filter((message, index) => index === 0 && message.role === "system");
|
|
3495
4032
|
const body = systemPrefix.length > 0 ? messages.slice(1) : [...messages];
|
|
3496
|
-
|
|
4033
|
+
const totalUserTurns = body.filter((m) => m.role === "user").length;
|
|
4034
|
+
const countAssistantTurns = totalUserTurns < lastTurnsToKeep;
|
|
4035
|
+
let turnCount = 0;
|
|
3497
4036
|
const collected = [];
|
|
3498
4037
|
for (let index = body.length - 1; index >= 0; index -= 1) {
|
|
3499
4038
|
const message = body[index];
|
|
3500
4039
|
collected.unshift(message);
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
4040
|
+
const isTurnBoundary = countAssistantTurns ? message.role === "assistant" : message.role === "user";
|
|
4041
|
+
if (isTurnBoundary) {
|
|
4042
|
+
turnCount += 1;
|
|
4043
|
+
if (turnCount >= lastTurnsToKeep) break;
|
|
4044
|
+
}
|
|
4045
|
+
}
|
|
4046
|
+
if (countAssistantTurns && collected.length > 0 && collected[0].role !== "user") {
|
|
4047
|
+
const firstUserMsg = body.find((m) => m.role === "user");
|
|
4048
|
+
if (firstUserMsg && !collected.includes(firstUserMsg)) {
|
|
4049
|
+
collected.unshift(firstUserMsg);
|
|
3504
4050
|
}
|
|
3505
4051
|
}
|
|
3506
4052
|
return [...systemPrefix, ...collected];
|
|
@@ -3540,16 +4086,53 @@ function clampToBudget(messages, maxContextTokens) {
|
|
|
3540
4086
|
if (tokenCount <= maxContextTokens) return working;
|
|
3541
4087
|
const firstNonSystem = working.findIndex((message, index) => !(index === 0 && message.role === "system"));
|
|
3542
4088
|
if (firstNonSystem < 0) break;
|
|
3543
|
-
working
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
4089
|
+
const target = working[firstNonSystem];
|
|
4090
|
+
if (target.role === "assistant" && Array.isArray(target.tool_calls) && target.tool_calls.length > 0) {
|
|
4091
|
+
const toolCallIds = new Set(
|
|
4092
|
+
target.tool_calls.map((tc) => tc.id).filter(Boolean)
|
|
4093
|
+
);
|
|
4094
|
+
working = working.filter((m, idx) => {
|
|
4095
|
+
if (idx === firstNonSystem) return false;
|
|
4096
|
+
if (m.role === "tool" && m.tool_call_id && toolCallIds.has(m.tool_call_id)) return false;
|
|
4097
|
+
return true;
|
|
4098
|
+
});
|
|
4099
|
+
} else if (target.role === "tool") {
|
|
4100
|
+
const toolCallId = target.tool_call_id;
|
|
4101
|
+
let parentIdx = -1;
|
|
4102
|
+
if (toolCallId) {
|
|
4103
|
+
for (let i = firstNonSystem - 1; i >= 0; i--) {
|
|
4104
|
+
const m = working[i];
|
|
4105
|
+
if (m.role === "assistant" && Array.isArray(m.tool_calls) && m.tool_calls.some((tc) => tc.id === toolCallId)) {
|
|
4106
|
+
parentIdx = i;
|
|
4107
|
+
break;
|
|
4108
|
+
}
|
|
4109
|
+
}
|
|
4110
|
+
}
|
|
4111
|
+
if (parentIdx >= 0) {
|
|
4112
|
+
const parent = working[parentIdx];
|
|
4113
|
+
const parentToolCallIds = new Set(
|
|
4114
|
+
(parent.tool_calls || []).map((tc) => tc.id).filter(Boolean)
|
|
4115
|
+
);
|
|
4116
|
+
working = working.filter((m, idx) => {
|
|
4117
|
+
if (idx === parentIdx) return false;
|
|
4118
|
+
if (m.role === "tool" && m.tool_call_id && parentToolCallIds.has(m.tool_call_id)) return false;
|
|
4119
|
+
return true;
|
|
4120
|
+
});
|
|
4121
|
+
} else {
|
|
4122
|
+
working.splice(firstNonSystem, 1);
|
|
4123
|
+
}
|
|
4124
|
+
} else {
|
|
4125
|
+
working.splice(firstNonSystem, 1);
|
|
4126
|
+
}
|
|
4127
|
+
}
|
|
4128
|
+
return working;
|
|
4129
|
+
}
|
|
4130
|
+
function buildModelMessages(state, config) {
|
|
4131
|
+
const rawMessages = state.messages || [];
|
|
4132
|
+
if (config.context.policy === "raw") {
|
|
4133
|
+
return clampToBudget(rawMessages, config.limits.maxContextTokens);
|
|
4134
|
+
}
|
|
4135
|
+
const recentMessages = collectRecentTurns(rawMessages, config.context.lastTurnsToKeep);
|
|
3553
4136
|
const latestSummary = state.summaryRecords?.[state.summaryRecords.length - 1];
|
|
3554
4137
|
const summaryText = renderStructuredSummary(latestSummary);
|
|
3555
4138
|
const memoryText = renderMemoryBlock(state.memoryFacts);
|
|
@@ -4190,6 +4773,12 @@ function calculateAdherenceScore(todoList) {
|
|
|
4190
4773
|
const completedCount = todoList.filter((item) => item.status === "completed").length;
|
|
4191
4774
|
return todoList.length === 0 ? 1 : Number((completedCount / todoList.length).toFixed(2));
|
|
4192
4775
|
}
|
|
4776
|
+
function formatTodoListSummary(todoList) {
|
|
4777
|
+
if (!Array.isArray(todoList) || todoList.length === 0) {
|
|
4778
|
+
return "No plan items.";
|
|
4779
|
+
}
|
|
4780
|
+
return todoList.map((item) => `${item.id}. ${item.title || item.step || item.description || `Step ${item.id}`} [${item.status}]`).join("\n");
|
|
4781
|
+
}
|
|
4193
4782
|
function createContextTools(stateRef, opts) {
|
|
4194
4783
|
const tools = [];
|
|
4195
4784
|
if (opts?.planningEnabled) {
|
|
@@ -4202,11 +4791,43 @@ function createContextTools(stateRef, opts) {
|
|
|
4202
4791
|
todoList: zod.z.array(zod.z.union([todoWriteItemSchema, todoUpdateItemSchema])).optional()
|
|
4203
4792
|
}),
|
|
4204
4793
|
func: async ({ operation, expectedVersion, todoList }) => {
|
|
4205
|
-
const
|
|
4794
|
+
const toolStateRef = manageTodo._stateRef;
|
|
4795
|
+
const onEvent = toolStateRef?.__onEvent;
|
|
4796
|
+
const emitPlanEvent = (list) => {
|
|
4797
|
+
const version = stateRef.planVersion || 1;
|
|
4798
|
+
const adherenceScore = stateRef.adherenceScore || 0;
|
|
4799
|
+
const planData = {
|
|
4800
|
+
source: "manage_todo_list",
|
|
4801
|
+
operation,
|
|
4802
|
+
version,
|
|
4803
|
+
adherenceScore,
|
|
4804
|
+
count: Array.isArray(list) ? list.length : 0
|
|
4805
|
+
};
|
|
4806
|
+
onEvent?.({ type: "plan", todoList: list, ...planData });
|
|
4807
|
+
const traceSession = toolStateRef?.ctx?.__traceSession;
|
|
4808
|
+
recordTraceEvent(traceSession, {
|
|
4809
|
+
type: "plan",
|
|
4810
|
+
label: `Plan ${operation}`,
|
|
4811
|
+
actor: { scope: "agent", name: "manage_todo_list", role: "planner" },
|
|
4812
|
+
sections: [
|
|
4813
|
+
{
|
|
4814
|
+
kind: "summary",
|
|
4815
|
+
label: "Todo List",
|
|
4816
|
+
content: formatTodoListSummary(list)
|
|
4817
|
+
},
|
|
4818
|
+
{
|
|
4819
|
+
kind: "metadata",
|
|
4820
|
+
label: "Plan Metadata",
|
|
4821
|
+
data: planData
|
|
4822
|
+
}
|
|
4823
|
+
],
|
|
4824
|
+
debug: planData
|
|
4825
|
+
});
|
|
4826
|
+
};
|
|
4206
4827
|
const currentVersion = stateRef.planVersion || 0;
|
|
4207
4828
|
if (operation === "read") {
|
|
4208
4829
|
const list = stateRef.todoList || [];
|
|
4209
|
-
|
|
4830
|
+
emitPlanEvent(list);
|
|
4210
4831
|
return list;
|
|
4211
4832
|
}
|
|
4212
4833
|
if (typeof expectedVersion === "number" && expectedVersion !== currentVersion) {
|
|
@@ -4271,9 +4892,10 @@ function createContextTools(stateRef, opts) {
|
|
|
4271
4892
|
adherenceScore: stateRef.adherenceScore || 0
|
|
4272
4893
|
};
|
|
4273
4894
|
}
|
|
4274
|
-
const
|
|
4895
|
+
const currentMap = new Map(currentList.map((item) => [item.id, item]));
|
|
4896
|
+
const patchMap = /* @__PURE__ */ new Map();
|
|
4275
4897
|
for (const item of parsed.data) {
|
|
4276
|
-
if (
|
|
4898
|
+
if (patchMap.has(item.id)) {
|
|
4277
4899
|
return {
|
|
4278
4900
|
status: "error",
|
|
4279
4901
|
operation,
|
|
@@ -4282,10 +4904,7 @@ function createContextTools(stateRef, opts) {
|
|
|
4282
4904
|
adherenceScore: stateRef.adherenceScore || 0
|
|
4283
4905
|
};
|
|
4284
4906
|
}
|
|
4285
|
-
|
|
4286
|
-
}
|
|
4287
|
-
const currentMap = new Map(currentList.map((item) => [item.id, item]));
|
|
4288
|
-
for (const item of parsed.data) {
|
|
4907
|
+
patchMap.set(item.id, item);
|
|
4289
4908
|
if (!currentMap.has(item.id)) {
|
|
4290
4909
|
return {
|
|
4291
4910
|
status: "error",
|
|
@@ -4297,7 +4916,7 @@ function createContextTools(stateRef, opts) {
|
|
|
4297
4916
|
}
|
|
4298
4917
|
}
|
|
4299
4918
|
const mergedList = currentList.map((item) => {
|
|
4300
|
-
const patch =
|
|
4919
|
+
const patch = patchMap.get(item.id);
|
|
4301
4920
|
return patch ? normalizeTodoItem(patch, item) : item;
|
|
4302
4921
|
});
|
|
4303
4922
|
const validation = validatePlanInvariants(mergedList, false);
|
|
@@ -4321,7 +4940,7 @@ function createContextTools(stateRef, opts) {
|
|
|
4321
4940
|
version: stateRef.planVersion || 1,
|
|
4322
4941
|
adherenceScore: stateRef.adherenceScore || 0
|
|
4323
4942
|
};
|
|
4324
|
-
|
|
4943
|
+
emitPlanEvent(stateRef.todoList || []);
|
|
4325
4944
|
return payload;
|
|
4326
4945
|
}
|
|
4327
4946
|
});
|
|
@@ -4346,23 +4965,6 @@ function createContextTools(stateRef, opts) {
|
|
|
4346
4965
|
});
|
|
4347
4966
|
getTool._stateRef = stateRef;
|
|
4348
4967
|
tools.push(getTool);
|
|
4349
|
-
if (opts?.outputSchema) {
|
|
4350
|
-
const responseTool = createTool({
|
|
4351
|
-
name: "response",
|
|
4352
|
-
description: "Finalize the answer by returning the final structured JSON matching the required schema. Call exactly once when you are fully done, then stop.",
|
|
4353
|
-
schema: opts.outputSchema.shape ? opts.outputSchema : zod.z.any(),
|
|
4354
|
-
func: async (data) => {
|
|
4355
|
-
try {
|
|
4356
|
-
const validated = opts.outputSchema.parse ? opts.outputSchema.parse(data) : data;
|
|
4357
|
-
return { __finalStructuredOutput: true, data: validated };
|
|
4358
|
-
} catch (e) {
|
|
4359
|
-
return { error: "Schema validation failed", details: e?.message };
|
|
4360
|
-
}
|
|
4361
|
-
}
|
|
4362
|
-
});
|
|
4363
|
-
responseTool._stateRef = stateRef;
|
|
4364
|
-
tools.push(responseTool);
|
|
4365
|
-
}
|
|
4366
4968
|
return tools;
|
|
4367
4969
|
}
|
|
4368
4970
|
|
|
@@ -4571,7 +5173,7 @@ function createSmartAgent(opts) {
|
|
|
4571
5173
|
useTodoList: planningEnabled
|
|
4572
5174
|
};
|
|
4573
5175
|
const stateRef = { toolHistory: void 0, toolHistoryArchived: void 0, todoList: void 0, planVersion: 0, adherenceScore: 0 };
|
|
4574
|
-
const contextTools = createContextTools(stateRef, { planningEnabled
|
|
5176
|
+
const contextTools = createContextTools(stateRef, { planningEnabled });
|
|
4575
5177
|
const mergedTools = [...opts.tools ?? [], ...contextTools];
|
|
4576
5178
|
const base = createAgent({ ...runtimeOpts, tools: mergedTools });
|
|
4577
5179
|
base.__runtime.runtimeProfile = resolved.runtimeProfile;
|
|
@@ -4580,12 +5182,8 @@ function createSmartAgent(opts) {
|
|
|
4580
5182
|
const summarizer = summarizationEnabled ? createContextSummarizeNode(runtimeOpts) : void 0;
|
|
4581
5183
|
const decideBefore = resolverDecisionFactory(runtimeOpts, summarizationEnabled);
|
|
4582
5184
|
const decideAfter = toolsDecisionFactory(runtimeOpts, summarizationEnabled);
|
|
4583
|
-
const
|
|
4584
|
-
|
|
4585
|
-
"Do NOT output the final JSON directly as an assistant message.",
|
|
4586
|
-
"When completely finished, call tool `response` passing the final JSON matching the schema as its arguments (direct object).",
|
|
4587
|
-
"Call it exactly once then STOP producing further assistant messages."
|
|
4588
|
-
].join("\n") : "";
|
|
5185
|
+
const soManager = opts.outputSchema ? new StructuredOutputManager(opts.outputSchema, resolveStrategy(opts.model)) : void 0;
|
|
5186
|
+
const structuredOutputHint = soManager ? soManager.buildSystemPromptHint() : "";
|
|
4589
5187
|
const runtimeHint = [
|
|
4590
5188
|
resolved.runtimeProfile === resolved.baseProfile ? `Runtime profile: ${resolved.runtimeProfile}.` : `Runtime profile: ${resolved.runtimeProfile} (base: ${resolved.baseProfile}).`,
|
|
4591
5189
|
`Context policy: ${resolved.context.policy}.`,
|
|
@@ -4655,8 +5253,13 @@ function createSmartAgent(opts) {
|
|
|
4655
5253
|
const next = summarizationEnabled ? decideBefore(state) : "agent";
|
|
4656
5254
|
if (next === "contextSummarize" && summarizer) {
|
|
4657
5255
|
const delta = await summarizer(state);
|
|
4658
|
-
if (!delta || Object.keys(delta).length === 0)
|
|
4659
|
-
|
|
5256
|
+
if (!delta || Object.keys(delta).length === 0) {
|
|
5257
|
+
const ctx = { ...state.ctx || {}, __summarizationExhausted: true };
|
|
5258
|
+
state = { ...state, ctx };
|
|
5259
|
+
} else {
|
|
5260
|
+
const ctx = { ...state.ctx || {} };
|
|
5261
|
+
delete ctx.__summarizationExhausted;
|
|
5262
|
+
state = await persistLatestSummary(syncPlanState({ ...state, ...delta, ctx }));
|
|
4660
5263
|
rawMessages = [...state.messages || rawMessages];
|
|
4661
5264
|
continue;
|
|
4662
5265
|
}
|
|
@@ -4689,20 +5292,32 @@ function createSmartAgent(opts) {
|
|
|
4689
5292
|
state = { ...state, ctx };
|
|
4690
5293
|
const delta = await summarizer(state);
|
|
4691
5294
|
if (delta && Object.keys(delta).length > 0) {
|
|
4692
|
-
|
|
5295
|
+
const successCtx = { ...state.ctx || {} };
|
|
5296
|
+
delete successCtx.__summarizationExhausted;
|
|
5297
|
+
state = await persistLatestSummary(syncPlanState({ ...state, ...delta, ctx: successCtx }));
|
|
4693
5298
|
rawMessages = [...state.messages || rawMessages];
|
|
4694
5299
|
continue;
|
|
5300
|
+
} else {
|
|
5301
|
+
const exhaustedCtx = { ...state.ctx || {}, __summarizationExhausted: true };
|
|
5302
|
+
state = { ...state, ctx: exhaustedCtx };
|
|
5303
|
+
continue;
|
|
4695
5304
|
}
|
|
4696
5305
|
}
|
|
4697
5306
|
if (state.ctx?.__finalizedDueToStructuredOutput) break;
|
|
5307
|
+
if (opts.outputSchema && lastResult?.output != null) break;
|
|
4698
5308
|
if (summarizationEnabled) {
|
|
4699
5309
|
const after = decideAfter(state);
|
|
4700
5310
|
if (after === "contextSummarize" && summarizer) {
|
|
4701
5311
|
const delta = await summarizer(state);
|
|
4702
5312
|
if (delta && Object.keys(delta).length > 0) {
|
|
4703
|
-
|
|
5313
|
+
const successCtx = { ...state.ctx || {} };
|
|
5314
|
+
delete successCtx.__summarizationExhausted;
|
|
5315
|
+
state = await persistLatestSummary(syncPlanState({ ...state, ...delta, ctx: successCtx }));
|
|
4704
5316
|
rawMessages = [...state.messages || rawMessages];
|
|
4705
5317
|
continue;
|
|
5318
|
+
} else {
|
|
5319
|
+
const exhaustedCtx = { ...state.ctx || {}, __summarizationExhausted: true };
|
|
5320
|
+
state = { ...state, ctx: exhaustedCtx };
|
|
4706
5321
|
}
|
|
4707
5322
|
}
|
|
4708
5323
|
}
|
|
@@ -5145,9 +5760,1754 @@ function createCodeGuardrail(options) {
|
|
|
5145
5760
|
});
|
|
5146
5761
|
}
|
|
5147
5762
|
|
|
5763
|
+
// src/providers/types.ts
|
|
5764
|
+
function emptyUsage() {
|
|
5765
|
+
return {
|
|
5766
|
+
inputTokens: 0,
|
|
5767
|
+
outputTokens: 0,
|
|
5768
|
+
totalTokens: 0,
|
|
5769
|
+
cachedInputTokens: 0,
|
|
5770
|
+
cachedWriteTokens: 0,
|
|
5771
|
+
cachedOutputTokens: 0,
|
|
5772
|
+
reasoningTokens: 0
|
|
5773
|
+
};
|
|
5774
|
+
}
|
|
5775
|
+
var ProviderError = class extends Error {
|
|
5776
|
+
constructor(message, provider, statusCode, responseBody) {
|
|
5777
|
+
super(message);
|
|
5778
|
+
this.provider = provider;
|
|
5779
|
+
this.statusCode = statusCode;
|
|
5780
|
+
this.responseBody = responseBody;
|
|
5781
|
+
this.name = "ProviderError";
|
|
5782
|
+
}
|
|
5783
|
+
};
|
|
5784
|
+
|
|
5785
|
+
// src/providers/base.ts
|
|
5786
|
+
var BaseProvider = class {
|
|
5787
|
+
};
|
|
5788
|
+
|
|
5789
|
+
// src/providers/utils/sse.ts
|
|
5790
|
+
async function* parseSSEStream(body, signal) {
|
|
5791
|
+
const reader = body.getReader();
|
|
5792
|
+
const decoder = new TextDecoder();
|
|
5793
|
+
let buffer = "";
|
|
5794
|
+
try {
|
|
5795
|
+
while (true) {
|
|
5796
|
+
if (signal?.aborted) ;
|
|
5797
|
+
const { done, value } = await reader.read();
|
|
5798
|
+
if (done) break;
|
|
5799
|
+
buffer += decoder.decode(value, { stream: true });
|
|
5800
|
+
const parts = buffer.split(/\r?\n\r?\n/);
|
|
5801
|
+
buffer = parts.pop() ?? "";
|
|
5802
|
+
for (const part of parts) {
|
|
5803
|
+
if (!part.trim()) continue;
|
|
5804
|
+
const event = parseSSEBlock(part);
|
|
5805
|
+
if (event) yield event;
|
|
5806
|
+
}
|
|
5807
|
+
}
|
|
5808
|
+
if (buffer.trim()) {
|
|
5809
|
+
const event = parseSSEBlock(buffer);
|
|
5810
|
+
if (event) yield event;
|
|
5811
|
+
}
|
|
5812
|
+
} finally {
|
|
5813
|
+
reader.releaseLock();
|
|
5814
|
+
}
|
|
5815
|
+
}
|
|
5816
|
+
function parseSSEBlock(block) {
|
|
5817
|
+
let event;
|
|
5818
|
+
let id;
|
|
5819
|
+
let retry;
|
|
5820
|
+
const dataLines = [];
|
|
5821
|
+
for (const line of block.split(/\r?\n/)) {
|
|
5822
|
+
if (line.startsWith(":")) continue;
|
|
5823
|
+
const colonIdx = line.indexOf(":");
|
|
5824
|
+
let field;
|
|
5825
|
+
let value;
|
|
5826
|
+
if (colonIdx === -1) {
|
|
5827
|
+
field = line;
|
|
5828
|
+
value = "";
|
|
5829
|
+
} else {
|
|
5830
|
+
field = line.slice(0, colonIdx);
|
|
5831
|
+
value = line.slice(colonIdx + 1);
|
|
5832
|
+
if (value.startsWith(" ")) value = value.slice(1);
|
|
5833
|
+
}
|
|
5834
|
+
switch (field) {
|
|
5835
|
+
case "event":
|
|
5836
|
+
event = value;
|
|
5837
|
+
break;
|
|
5838
|
+
case "data":
|
|
5839
|
+
dataLines.push(value);
|
|
5840
|
+
break;
|
|
5841
|
+
case "id":
|
|
5842
|
+
id = value;
|
|
5843
|
+
break;
|
|
5844
|
+
case "retry": {
|
|
5845
|
+
const n = parseInt(value, 10);
|
|
5846
|
+
if (!isNaN(n)) retry = n;
|
|
5847
|
+
break;
|
|
5848
|
+
}
|
|
5849
|
+
}
|
|
5850
|
+
}
|
|
5851
|
+
if (dataLines.length === 0) return null;
|
|
5852
|
+
return { event, data: dataLines.join("\n"), id, retry };
|
|
5853
|
+
}
|
|
5854
|
+
|
|
5855
|
+
// src/providers/openai.ts
|
|
5856
|
+
var OpenAIProvider = class extends BaseProvider {
|
|
5857
|
+
constructor(config) {
|
|
5858
|
+
super();
|
|
5859
|
+
this.providerName = "openai";
|
|
5860
|
+
this.apiKey = config.apiKey;
|
|
5861
|
+
this.baseURL = (config.baseURL ?? "https://api.openai.com/v1").replace(/\/$/, "");
|
|
5862
|
+
this.defaultModel = config.defaultModel ?? "gpt-4o";
|
|
5863
|
+
this.defaultHeaders = {
|
|
5864
|
+
...config.defaultHeaders,
|
|
5865
|
+
...config.organization ? { "OpenAI-Organization": config.organization } : {}
|
|
5866
|
+
};
|
|
5867
|
+
}
|
|
5868
|
+
async complete(request) {
|
|
5869
|
+
const body = this.buildRequestBody(request, false);
|
|
5870
|
+
const res = await this.doFetch(body);
|
|
5871
|
+
const json = await res.json();
|
|
5872
|
+
return this.parseResponse(json);
|
|
5873
|
+
}
|
|
5874
|
+
async *completeStream(request) {
|
|
5875
|
+
const body = this.buildRequestBody(request, true);
|
|
5876
|
+
const res = await this.doFetch(body);
|
|
5877
|
+
if (!res.body) throw new ProviderError("No response body for stream", this.providerName);
|
|
5878
|
+
let id = "";
|
|
5879
|
+
let model = "";
|
|
5880
|
+
const toolCallBuffers = /* @__PURE__ */ new Map();
|
|
5881
|
+
let usage;
|
|
5882
|
+
for await (const event of parseSSEStream(res.body)) {
|
|
5883
|
+
if (event.data === "[DONE]") break;
|
|
5884
|
+
let chunk;
|
|
5885
|
+
try {
|
|
5886
|
+
chunk = JSON.parse(event.data);
|
|
5887
|
+
} catch {
|
|
5888
|
+
continue;
|
|
5889
|
+
}
|
|
5890
|
+
id = chunk.id ?? id;
|
|
5891
|
+
model = chunk.model ?? model;
|
|
5892
|
+
const choice = chunk.choices?.[0];
|
|
5893
|
+
if (chunk.usage) {
|
|
5894
|
+
usage = this.extractUsage(chunk.usage);
|
|
5895
|
+
}
|
|
5896
|
+
if (!choice) {
|
|
5897
|
+
if (usage) yield { id, model, delta: {}, usage };
|
|
5898
|
+
continue;
|
|
5899
|
+
}
|
|
5900
|
+
const delta = choice.delta ?? {};
|
|
5901
|
+
const out = {
|
|
5902
|
+
id,
|
|
5903
|
+
model,
|
|
5904
|
+
delta: {},
|
|
5905
|
+
finishReason: mapFinishReason(choice.finish_reason)
|
|
5906
|
+
};
|
|
5907
|
+
if (delta.content) {
|
|
5908
|
+
out.delta.content = delta.content;
|
|
5909
|
+
}
|
|
5910
|
+
if (delta.tool_calls) {
|
|
5911
|
+
const partials = [];
|
|
5912
|
+
for (const tc of delta.tool_calls) {
|
|
5913
|
+
const idx = tc.index ?? 0;
|
|
5914
|
+
if (!toolCallBuffers.has(idx)) {
|
|
5915
|
+
toolCallBuffers.set(idx, { id: tc.id ?? "", name: "", arguments: "" });
|
|
5916
|
+
}
|
|
5917
|
+
const buf = toolCallBuffers.get(idx);
|
|
5918
|
+
if (tc.id) buf.id = tc.id;
|
|
5919
|
+
if (tc.function?.name) buf.name = tc.function.name;
|
|
5920
|
+
if (tc.function?.arguments) buf.arguments += tc.function.arguments;
|
|
5921
|
+
partials.push({ id: buf.id, name: buf.name || void 0, arguments: tc.function?.arguments });
|
|
5922
|
+
}
|
|
5923
|
+
out.delta.toolCalls = partials;
|
|
5924
|
+
}
|
|
5925
|
+
if (usage) out.usage = usage;
|
|
5926
|
+
yield out;
|
|
5927
|
+
}
|
|
5928
|
+
}
|
|
5929
|
+
// ─── Internals ───────────────────────────────────────────────────────────
|
|
5930
|
+
buildRequestBody(request, stream) {
|
|
5931
|
+
const body = {
|
|
5932
|
+
model: request.model || this.defaultModel,
|
|
5933
|
+
messages: request.messages.map((m) => this.toOpenAIMessage(m)),
|
|
5934
|
+
stream
|
|
5935
|
+
};
|
|
5936
|
+
if (stream) {
|
|
5937
|
+
body.stream_options = { include_usage: true };
|
|
5938
|
+
}
|
|
5939
|
+
if (request.temperature != null) body.temperature = request.temperature;
|
|
5940
|
+
if (request.maxTokens != null) body.max_tokens = request.maxTokens;
|
|
5941
|
+
if (request.topP != null) body.top_p = request.topP;
|
|
5942
|
+
if (request.stop) body.stop = request.stop;
|
|
5943
|
+
if (request.tools?.length) {
|
|
5944
|
+
body.tools = request.tools.map((t) => this.toOpenAITool(t));
|
|
5945
|
+
}
|
|
5946
|
+
if (request.toolChoice != null) {
|
|
5947
|
+
if (typeof request.toolChoice === "string") {
|
|
5948
|
+
body.tool_choice = request.toolChoice;
|
|
5949
|
+
} else {
|
|
5950
|
+
body.tool_choice = { type: "function", function: { name: request.toolChoice.name } };
|
|
5951
|
+
}
|
|
5952
|
+
}
|
|
5953
|
+
if (request.responseFormat) {
|
|
5954
|
+
if (request.responseFormat.type === "json_schema") {
|
|
5955
|
+
body.response_format = {
|
|
5956
|
+
type: "json_schema",
|
|
5957
|
+
json_schema: {
|
|
5958
|
+
name: request.responseFormat.name ?? "response",
|
|
5959
|
+
schema: request.responseFormat.schema,
|
|
5960
|
+
strict: true
|
|
5961
|
+
}
|
|
5962
|
+
};
|
|
5963
|
+
} else if (request.responseFormat.type === "json_object") {
|
|
5964
|
+
body.response_format = { type: "json_object" };
|
|
5965
|
+
}
|
|
5966
|
+
}
|
|
5967
|
+
if (request.extra) {
|
|
5968
|
+
Object.assign(body, request.extra);
|
|
5969
|
+
}
|
|
5970
|
+
return body;
|
|
5971
|
+
}
|
|
5972
|
+
toOpenAIMessage(m) {
|
|
5973
|
+
const msg = { role: m.role };
|
|
5974
|
+
if (typeof m.content === "string") {
|
|
5975
|
+
msg.content = m.content;
|
|
5976
|
+
} else if (Array.isArray(m.content)) {
|
|
5977
|
+
msg.content = m.content.map((part) => {
|
|
5978
|
+
if (part.type === "text") return { type: "text", text: part.text };
|
|
5979
|
+
if (part.type === "image") {
|
|
5980
|
+
const src = part.source;
|
|
5981
|
+
const url = src.type === "url" ? src.url : `data:${src.mediaType};base64,${src.data}`;
|
|
5982
|
+
return { type: "image_url", image_url: { url } };
|
|
5983
|
+
}
|
|
5984
|
+
return part;
|
|
5985
|
+
});
|
|
5986
|
+
}
|
|
5987
|
+
if (m.role === "assistant" && m.toolCalls?.length) {
|
|
5988
|
+
msg.tool_calls = m.toolCalls.map((tc) => ({
|
|
5989
|
+
id: tc.id,
|
|
5990
|
+
type: "function",
|
|
5991
|
+
function: { name: tc.name, arguments: tc.arguments }
|
|
5992
|
+
}));
|
|
5993
|
+
}
|
|
5994
|
+
if (m.role === "tool" && m.toolCallId) {
|
|
5995
|
+
msg.tool_call_id = m.toolCallId;
|
|
5996
|
+
}
|
|
5997
|
+
if (m.name) msg.name = m.name;
|
|
5998
|
+
return msg;
|
|
5999
|
+
}
|
|
6000
|
+
toOpenAITool(t) {
|
|
6001
|
+
const fn = {
|
|
6002
|
+
name: t.name,
|
|
6003
|
+
description: t.description,
|
|
6004
|
+
parameters: t.parameters
|
|
6005
|
+
};
|
|
6006
|
+
if (t.strict) {
|
|
6007
|
+
fn.strict = true;
|
|
6008
|
+
}
|
|
6009
|
+
return { type: "function", function: fn };
|
|
6010
|
+
}
|
|
6011
|
+
async doFetch(body) {
|
|
6012
|
+
const url = `${this.baseURL}/chat/completions`;
|
|
6013
|
+
const res = await fetch(url, {
|
|
6014
|
+
method: "POST",
|
|
6015
|
+
headers: {
|
|
6016
|
+
"Content-Type": "application/json",
|
|
6017
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
6018
|
+
...this.defaultHeaders
|
|
6019
|
+
},
|
|
6020
|
+
body: JSON.stringify(body)
|
|
6021
|
+
});
|
|
6022
|
+
if (!res.ok) {
|
|
6023
|
+
const text = await res.text().catch(() => "");
|
|
6024
|
+
throw new ProviderError(
|
|
6025
|
+
`OpenAI API error ${res.status}: ${text}`,
|
|
6026
|
+
this.providerName,
|
|
6027
|
+
res.status,
|
|
6028
|
+
text
|
|
6029
|
+
);
|
|
6030
|
+
}
|
|
6031
|
+
return res;
|
|
6032
|
+
}
|
|
6033
|
+
parseResponse(json) {
|
|
6034
|
+
const choice = json.choices?.[0];
|
|
6035
|
+
const message = choice?.message ?? {};
|
|
6036
|
+
const toolCalls = (message.tool_calls ?? []).map((tc) => ({
|
|
6037
|
+
id: tc.id,
|
|
6038
|
+
name: tc.function?.name ?? "",
|
|
6039
|
+
arguments: tc.function?.arguments ?? "{}"
|
|
6040
|
+
}));
|
|
6041
|
+
return {
|
|
6042
|
+
id: json.id ?? "",
|
|
6043
|
+
model: json.model ?? "",
|
|
6044
|
+
content: message.content ?? null,
|
|
6045
|
+
toolCalls,
|
|
6046
|
+
usage: this.extractUsage(json.usage),
|
|
6047
|
+
finishReason: mapFinishReason(choice?.finish_reason),
|
|
6048
|
+
raw: json
|
|
6049
|
+
};
|
|
6050
|
+
}
|
|
6051
|
+
extractUsage(usage) {
|
|
6052
|
+
if (!usage) return emptyUsage();
|
|
6053
|
+
const promptDetails = usage.prompt_tokens_details ?? {};
|
|
6054
|
+
const completionDetails = usage.completion_tokens_details ?? {};
|
|
6055
|
+
return {
|
|
6056
|
+
inputTokens: usage.prompt_tokens ?? 0,
|
|
6057
|
+
outputTokens: usage.completion_tokens ?? 0,
|
|
6058
|
+
totalTokens: usage.total_tokens ?? (usage.prompt_tokens ?? 0) + (usage.completion_tokens ?? 0),
|
|
6059
|
+
cachedInputTokens: promptDetails.cached_tokens ?? 0,
|
|
6060
|
+
cachedWriteTokens: 0,
|
|
6061
|
+
cachedOutputTokens: 0,
|
|
6062
|
+
reasoningTokens: completionDetails.reasoning_tokens ?? 0
|
|
6063
|
+
};
|
|
6064
|
+
}
|
|
6065
|
+
};
|
|
6066
|
+
function mapFinishReason(reason) {
|
|
6067
|
+
switch (reason) {
|
|
6068
|
+
case "stop":
|
|
6069
|
+
return "stop";
|
|
6070
|
+
case "tool_calls":
|
|
6071
|
+
return "tool_calls";
|
|
6072
|
+
case "length":
|
|
6073
|
+
return "length";
|
|
6074
|
+
case "content_filter":
|
|
6075
|
+
return "content_filter";
|
|
6076
|
+
default:
|
|
6077
|
+
return "stop";
|
|
6078
|
+
}
|
|
6079
|
+
}
|
|
6080
|
+
|
|
6081
|
+
// src/providers/anthropic.ts
|
|
6082
|
+
var AnthropicProvider = class extends BaseProvider {
|
|
6083
|
+
constructor(config) {
|
|
6084
|
+
super();
|
|
6085
|
+
this.providerName = "anthropic";
|
|
6086
|
+
this.apiKey = config.apiKey;
|
|
6087
|
+
this.baseURL = (config.baseURL ?? "https://api.anthropic.com").replace(/\/$/, "");
|
|
6088
|
+
this.defaultModel = config.defaultModel ?? "claude-sonnet-4-20250514";
|
|
6089
|
+
this.defaultHeaders = config.defaultHeaders ?? {};
|
|
6090
|
+
this.anthropicVersion = config.anthropicVersion ?? "2023-06-01";
|
|
6091
|
+
}
|
|
6092
|
+
async complete(request) {
|
|
6093
|
+
const body = this.buildRequestBody(request, false);
|
|
6094
|
+
const res = await this.doFetch(body);
|
|
6095
|
+
const json = await res.json();
|
|
6096
|
+
return this.parseResponse(json);
|
|
6097
|
+
}
|
|
6098
|
+
async *completeStream(request) {
|
|
6099
|
+
const body = this.buildRequestBody(request, true);
|
|
6100
|
+
const res = await this.doFetch(body);
|
|
6101
|
+
if (!res.body) throw new ProviderError("No response body for stream", this.providerName);
|
|
6102
|
+
let id = "";
|
|
6103
|
+
let model = "";
|
|
6104
|
+
let inputTokens = 0;
|
|
6105
|
+
let outputTokens = 0;
|
|
6106
|
+
let cacheReadInputTokens = 0;
|
|
6107
|
+
let cacheCreationInputTokens = 0;
|
|
6108
|
+
const currentToolCalls = /* @__PURE__ */ new Map();
|
|
6109
|
+
for await (const event of parseSSEStream(res.body)) {
|
|
6110
|
+
let data;
|
|
6111
|
+
try {
|
|
6112
|
+
data = JSON.parse(event.data);
|
|
6113
|
+
} catch {
|
|
6114
|
+
continue;
|
|
6115
|
+
}
|
|
6116
|
+
const eventType = event.event ?? data.type;
|
|
6117
|
+
switch (eventType) {
|
|
6118
|
+
case "message_start": {
|
|
6119
|
+
const msg = data.message;
|
|
6120
|
+
id = msg?.id ?? "";
|
|
6121
|
+
model = msg?.model ?? "";
|
|
6122
|
+
if (msg?.usage) {
|
|
6123
|
+
inputTokens = msg.usage.input_tokens ?? 0;
|
|
6124
|
+
cacheReadInputTokens = msg.usage.cache_read_input_tokens ?? 0;
|
|
6125
|
+
cacheCreationInputTokens = msg.usage.cache_creation_input_tokens ?? 0;
|
|
6126
|
+
}
|
|
6127
|
+
break;
|
|
6128
|
+
}
|
|
6129
|
+
case "content_block_start": {
|
|
6130
|
+
const idx = data.index ?? 0;
|
|
6131
|
+
const block = data.content_block;
|
|
6132
|
+
if (block?.type === "tool_use") {
|
|
6133
|
+
currentToolCalls.set(idx, {
|
|
6134
|
+
id: block.id ?? "",
|
|
6135
|
+
name: block.name ?? "",
|
|
6136
|
+
arguments: ""
|
|
6137
|
+
});
|
|
6138
|
+
}
|
|
6139
|
+
break;
|
|
6140
|
+
}
|
|
6141
|
+
case "content_block_delta": {
|
|
6142
|
+
const idx = data.index ?? 0;
|
|
6143
|
+
const delta = data.delta;
|
|
6144
|
+
if (delta?.type === "text_delta" && delta.text) {
|
|
6145
|
+
yield {
|
|
6146
|
+
id,
|
|
6147
|
+
model,
|
|
6148
|
+
delta: { content: delta.text }
|
|
6149
|
+
};
|
|
6150
|
+
} else if (delta?.type === "input_json_delta" && delta.partial_json) {
|
|
6151
|
+
const tc = currentToolCalls.get(idx);
|
|
6152
|
+
if (tc) {
|
|
6153
|
+
tc.arguments += delta.partial_json;
|
|
6154
|
+
yield {
|
|
6155
|
+
id,
|
|
6156
|
+
model,
|
|
6157
|
+
delta: {
|
|
6158
|
+
toolCalls: [{ id: tc.id, name: tc.name, arguments: delta.partial_json }]
|
|
6159
|
+
}
|
|
6160
|
+
};
|
|
6161
|
+
}
|
|
6162
|
+
}
|
|
6163
|
+
break;
|
|
6164
|
+
}
|
|
6165
|
+
case "content_block_stop":
|
|
6166
|
+
break;
|
|
6167
|
+
case "message_delta": {
|
|
6168
|
+
const delta = data.delta;
|
|
6169
|
+
if (data.usage) {
|
|
6170
|
+
outputTokens = data.usage.output_tokens ?? outputTokens;
|
|
6171
|
+
}
|
|
6172
|
+
const finishReason = mapAnthropicStopReason(delta?.stop_reason);
|
|
6173
|
+
const usage = {
|
|
6174
|
+
inputTokens,
|
|
6175
|
+
outputTokens,
|
|
6176
|
+
totalTokens: inputTokens + outputTokens,
|
|
6177
|
+
cachedInputTokens: cacheReadInputTokens,
|
|
6178
|
+
cachedWriteTokens: cacheCreationInputTokens,
|
|
6179
|
+
cachedOutputTokens: 0,
|
|
6180
|
+
reasoningTokens: 0
|
|
6181
|
+
};
|
|
6182
|
+
yield {
|
|
6183
|
+
id,
|
|
6184
|
+
model,
|
|
6185
|
+
delta: {},
|
|
6186
|
+
finishReason,
|
|
6187
|
+
usage
|
|
6188
|
+
};
|
|
6189
|
+
break;
|
|
6190
|
+
}
|
|
6191
|
+
case "message_stop":
|
|
6192
|
+
break;
|
|
6193
|
+
case "error": {
|
|
6194
|
+
const errMsg = data.error?.message ?? "Unknown Anthropic streaming error";
|
|
6195
|
+
throw new ProviderError(errMsg, this.providerName);
|
|
6196
|
+
}
|
|
6197
|
+
}
|
|
6198
|
+
}
|
|
6199
|
+
}
|
|
6200
|
+
// ─── Internals ───────────────────────────────────────────────────────────
|
|
6201
|
+
buildRequestBody(request, stream) {
|
|
6202
|
+
const { system, messages } = this.splitSystemMessages(request.messages);
|
|
6203
|
+
const body = {
|
|
6204
|
+
model: request.model || this.defaultModel,
|
|
6205
|
+
messages: messages.map((m) => this.toAnthropicMessage(m)),
|
|
6206
|
+
max_tokens: request.maxTokens ?? 4096,
|
|
6207
|
+
stream
|
|
6208
|
+
};
|
|
6209
|
+
if (system) body.system = system;
|
|
6210
|
+
if (request.temperature != null) body.temperature = request.temperature;
|
|
6211
|
+
if (request.topP != null) body.top_p = request.topP;
|
|
6212
|
+
if (request.stop) body.stop_sequences = request.stop;
|
|
6213
|
+
if (request.tools?.length) {
|
|
6214
|
+
body.tools = request.tools.map((t) => this.toAnthropicTool(t));
|
|
6215
|
+
}
|
|
6216
|
+
if (request.toolChoice != null) {
|
|
6217
|
+
if (request.toolChoice === "auto") {
|
|
6218
|
+
body.tool_choice = { type: "auto" };
|
|
6219
|
+
} else if (request.toolChoice === "required") {
|
|
6220
|
+
body.tool_choice = { type: "any" };
|
|
6221
|
+
} else if (request.toolChoice === "none") ; else {
|
|
6222
|
+
body.tool_choice = { type: "tool", name: request.toolChoice.name };
|
|
6223
|
+
}
|
|
6224
|
+
}
|
|
6225
|
+
if (request.extra) {
|
|
6226
|
+
Object.assign(body, request.extra);
|
|
6227
|
+
}
|
|
6228
|
+
return body;
|
|
6229
|
+
}
|
|
6230
|
+
splitSystemMessages(messages) {
|
|
6231
|
+
const systemParts = [];
|
|
6232
|
+
const rest = [];
|
|
6233
|
+
for (const m of messages) {
|
|
6234
|
+
if (m.role === "system") {
|
|
6235
|
+
if (typeof m.content === "string") {
|
|
6236
|
+
systemParts.push(m.content);
|
|
6237
|
+
} else {
|
|
6238
|
+
const text = m.content.filter((p) => p.type === "text").map((p) => p.text).join("\n");
|
|
6239
|
+
if (text) systemParts.push(text);
|
|
6240
|
+
}
|
|
6241
|
+
} else {
|
|
6242
|
+
rest.push(m);
|
|
6243
|
+
}
|
|
6244
|
+
}
|
|
6245
|
+
return {
|
|
6246
|
+
system: systemParts.length > 0 ? systemParts.join("\n\n") : void 0,
|
|
6247
|
+
messages: rest
|
|
6248
|
+
};
|
|
6249
|
+
}
|
|
6250
|
+
toAnthropicMessage(m) {
|
|
6251
|
+
if (m.role === "tool" && m.toolCallId) {
|
|
6252
|
+
const content = typeof m.content === "string" ? m.content : JSON.stringify(m.content);
|
|
6253
|
+
return {
|
|
6254
|
+
role: "user",
|
|
6255
|
+
content: [
|
|
6256
|
+
{
|
|
6257
|
+
type: "tool_result",
|
|
6258
|
+
tool_use_id: m.toolCallId,
|
|
6259
|
+
content
|
|
6260
|
+
}
|
|
6261
|
+
]
|
|
6262
|
+
};
|
|
6263
|
+
}
|
|
6264
|
+
if (m.role === "assistant" && m.toolCalls?.length) {
|
|
6265
|
+
const content = [];
|
|
6266
|
+
if (typeof m.content === "string" && m.content) {
|
|
6267
|
+
content.push({ type: "text", text: m.content });
|
|
6268
|
+
}
|
|
6269
|
+
for (const tc of m.toolCalls) {
|
|
6270
|
+
content.push({
|
|
6271
|
+
type: "tool_use",
|
|
6272
|
+
id: tc.id,
|
|
6273
|
+
name: tc.name,
|
|
6274
|
+
input: safeJsonParse(tc.arguments)
|
|
6275
|
+
});
|
|
6276
|
+
}
|
|
6277
|
+
return { role: "assistant", content };
|
|
6278
|
+
}
|
|
6279
|
+
const msg = { role: m.role };
|
|
6280
|
+
if (typeof m.content === "string") {
|
|
6281
|
+
msg.content = m.content;
|
|
6282
|
+
} else if (Array.isArray(m.content)) {
|
|
6283
|
+
msg.content = m.content.map((part) => {
|
|
6284
|
+
if (part.type === "text") return { type: "text", text: part.text };
|
|
6285
|
+
if (part.type === "image") {
|
|
6286
|
+
const src = part.source;
|
|
6287
|
+
if (src.type === "base64") {
|
|
6288
|
+
return {
|
|
6289
|
+
type: "image",
|
|
6290
|
+
source: { type: "base64", media_type: src.mediaType, data: src.data }
|
|
6291
|
+
};
|
|
6292
|
+
}
|
|
6293
|
+
return {
|
|
6294
|
+
type: "image",
|
|
6295
|
+
source: { type: "url", url: src.url }
|
|
6296
|
+
};
|
|
6297
|
+
}
|
|
6298
|
+
return part;
|
|
6299
|
+
});
|
|
6300
|
+
}
|
|
6301
|
+
return msg;
|
|
6302
|
+
}
|
|
6303
|
+
toAnthropicTool(t) {
|
|
6304
|
+
return {
|
|
6305
|
+
name: t.name,
|
|
6306
|
+
description: t.description,
|
|
6307
|
+
input_schema: t.parameters
|
|
6308
|
+
};
|
|
6309
|
+
}
|
|
6310
|
+
async doFetch(body) {
|
|
6311
|
+
const url = `${this.baseURL}/v1/messages`;
|
|
6312
|
+
const res = await fetch(url, {
|
|
6313
|
+
method: "POST",
|
|
6314
|
+
headers: {
|
|
6315
|
+
"Content-Type": "application/json",
|
|
6316
|
+
"x-api-key": this.apiKey,
|
|
6317
|
+
"anthropic-version": this.anthropicVersion,
|
|
6318
|
+
...this.defaultHeaders
|
|
6319
|
+
},
|
|
6320
|
+
body: JSON.stringify(body)
|
|
6321
|
+
});
|
|
6322
|
+
if (!res.ok) {
|
|
6323
|
+
const text = await res.text().catch(() => "");
|
|
6324
|
+
throw new ProviderError(
|
|
6325
|
+
`Anthropic API error ${res.status}: ${text}`,
|
|
6326
|
+
this.providerName,
|
|
6327
|
+
res.status,
|
|
6328
|
+
text
|
|
6329
|
+
);
|
|
6330
|
+
}
|
|
6331
|
+
return res;
|
|
6332
|
+
}
|
|
6333
|
+
parseResponse(json) {
|
|
6334
|
+
const textParts = [];
|
|
6335
|
+
const toolCalls = [];
|
|
6336
|
+
for (const block of json.content ?? []) {
|
|
6337
|
+
if (block.type === "text") {
|
|
6338
|
+
textParts.push(block.text);
|
|
6339
|
+
} else if (block.type === "tool_use") {
|
|
6340
|
+
toolCalls.push({
|
|
6341
|
+
id: block.id,
|
|
6342
|
+
name: block.name,
|
|
6343
|
+
arguments: typeof block.input === "string" ? block.input : JSON.stringify(block.input)
|
|
6344
|
+
});
|
|
6345
|
+
}
|
|
6346
|
+
}
|
|
6347
|
+
const usage = json.usage ?? {};
|
|
6348
|
+
const tokenUsage = {
|
|
6349
|
+
inputTokens: usage.input_tokens ?? 0,
|
|
6350
|
+
outputTokens: usage.output_tokens ?? 0,
|
|
6351
|
+
totalTokens: (usage.input_tokens ?? 0) + (usage.output_tokens ?? 0),
|
|
6352
|
+
cachedInputTokens: usage.cache_read_input_tokens ?? 0,
|
|
6353
|
+
cachedWriteTokens: usage.cache_creation_input_tokens ?? 0,
|
|
6354
|
+
cachedOutputTokens: 0,
|
|
6355
|
+
reasoningTokens: 0
|
|
6356
|
+
};
|
|
6357
|
+
return {
|
|
6358
|
+
id: json.id ?? "",
|
|
6359
|
+
model: json.model ?? "",
|
|
6360
|
+
content: textParts.join("") || null,
|
|
6361
|
+
toolCalls,
|
|
6362
|
+
usage: tokenUsage,
|
|
6363
|
+
finishReason: mapAnthropicStopReason(json.stop_reason),
|
|
6364
|
+
raw: json
|
|
6365
|
+
};
|
|
6366
|
+
}
|
|
6367
|
+
};
|
|
6368
|
+
function mapAnthropicStopReason(reason) {
|
|
6369
|
+
switch (reason) {
|
|
6370
|
+
case "end_turn":
|
|
6371
|
+
case "stop_sequence":
|
|
6372
|
+
return "stop";
|
|
6373
|
+
case "tool_use":
|
|
6374
|
+
return "tool_calls";
|
|
6375
|
+
case "max_tokens":
|
|
6376
|
+
return "length";
|
|
6377
|
+
default:
|
|
6378
|
+
return "stop";
|
|
6379
|
+
}
|
|
6380
|
+
}
|
|
6381
|
+
function safeJsonParse(str) {
|
|
6382
|
+
try {
|
|
6383
|
+
return JSON.parse(str);
|
|
6384
|
+
} catch {
|
|
6385
|
+
return {};
|
|
6386
|
+
}
|
|
6387
|
+
}
|
|
6388
|
+
|
|
6389
|
+
// src/providers/azure.ts
|
|
6390
|
+
var AzureProvider = class extends OpenAIProvider {
|
|
6391
|
+
constructor(config) {
|
|
6392
|
+
super({
|
|
6393
|
+
provider: "openai",
|
|
6394
|
+
apiKey: config.apiKey,
|
|
6395
|
+
defaultModel: config.defaultModel ?? config.deploymentName ?? "gpt-4o",
|
|
6396
|
+
defaultHeaders: config.defaultHeaders
|
|
6397
|
+
});
|
|
6398
|
+
this.providerName = "azure";
|
|
6399
|
+
this.endpoint = config.endpoint.replace(/\/$/, "");
|
|
6400
|
+
this.apiVersion = config.apiVersion ?? "2024-10-21";
|
|
6401
|
+
this.deploymentName = config.deploymentName;
|
|
6402
|
+
this.azureApiKey = config.apiKey;
|
|
6403
|
+
}
|
|
6404
|
+
async doFetch(body) {
|
|
6405
|
+
const deployment = this.deploymentName ?? body.model;
|
|
6406
|
+
if (!deployment) {
|
|
6407
|
+
throw new ProviderError("Azure requires a deploymentName or model", this.providerName);
|
|
6408
|
+
}
|
|
6409
|
+
const url = `${this.endpoint}/openai/deployments/${deployment}/chat/completions?api-version=${this.apiVersion}`;
|
|
6410
|
+
const { model: _model, ...bodyWithoutModel } = body;
|
|
6411
|
+
const res = await fetch(url, {
|
|
6412
|
+
method: "POST",
|
|
6413
|
+
headers: {
|
|
6414
|
+
"Content-Type": "application/json",
|
|
6415
|
+
"api-key": this.azureApiKey,
|
|
6416
|
+
...this.defaultHeaders
|
|
6417
|
+
},
|
|
6418
|
+
body: JSON.stringify(bodyWithoutModel)
|
|
6419
|
+
});
|
|
6420
|
+
if (!res.ok) {
|
|
6421
|
+
const text = await res.text().catch(() => "");
|
|
6422
|
+
throw new ProviderError(
|
|
6423
|
+
`Azure OpenAI API error ${res.status}: ${text}`,
|
|
6424
|
+
this.providerName,
|
|
6425
|
+
res.status,
|
|
6426
|
+
text
|
|
6427
|
+
);
|
|
6428
|
+
}
|
|
6429
|
+
return res;
|
|
6430
|
+
}
|
|
6431
|
+
};
|
|
6432
|
+
|
|
6433
|
+
// src/providers/openaiCompatible.ts
|
|
6434
|
+
var OpenAICompatibleProvider = class extends OpenAIProvider {
|
|
6435
|
+
constructor(config) {
|
|
6436
|
+
super({
|
|
6437
|
+
provider: "openai",
|
|
6438
|
+
apiKey: config.apiKey,
|
|
6439
|
+
baseURL: config.baseURL,
|
|
6440
|
+
defaultModel: config.defaultModel,
|
|
6441
|
+
defaultHeaders: config.defaultHeaders
|
|
6442
|
+
});
|
|
6443
|
+
this.providerName = "openai-compatible";
|
|
6444
|
+
}
|
|
6445
|
+
};
|
|
6446
|
+
function signRequest(req) {
|
|
6447
|
+
const now = /* @__PURE__ */ new Date();
|
|
6448
|
+
const dateStamp = toDateStamp(now);
|
|
6449
|
+
const amzDate = toAmzDate(now);
|
|
6450
|
+
const parsed = new URL(req.url);
|
|
6451
|
+
const host = parsed.host;
|
|
6452
|
+
const canonicalUri = parsed.pathname || "/";
|
|
6453
|
+
const canonicalQuerystring = sortQueryParams(parsed.searchParams);
|
|
6454
|
+
const headers = {
|
|
6455
|
+
...req.headers,
|
|
6456
|
+
host,
|
|
6457
|
+
"x-amz-date": amzDate
|
|
6458
|
+
};
|
|
6459
|
+
if (req.credentials.sessionToken) {
|
|
6460
|
+
headers["x-amz-security-token"] = req.credentials.sessionToken;
|
|
6461
|
+
}
|
|
6462
|
+
const signedHeaderKeys = Object.keys(headers).map((k) => k.toLowerCase()).sort();
|
|
6463
|
+
const canonicalHeaders = signedHeaderKeys.map((k) => `${k}:${headers[Object.keys(headers).find((h) => h.toLowerCase() === k)]?.trim()}`).join("\n") + "\n";
|
|
6464
|
+
const signedHeaders = signedHeaderKeys.join(";");
|
|
6465
|
+
const payloadHash = sha256(req.body);
|
|
6466
|
+
const canonicalRequest = [
|
|
6467
|
+
req.method,
|
|
6468
|
+
canonicalUri,
|
|
6469
|
+
canonicalQuerystring,
|
|
6470
|
+
canonicalHeaders,
|
|
6471
|
+
signedHeaders,
|
|
6472
|
+
payloadHash
|
|
6473
|
+
].join("\n");
|
|
6474
|
+
const credentialScope = `${dateStamp}/${req.region}/${req.service}/aws4_request`;
|
|
6475
|
+
const stringToSign = [
|
|
6476
|
+
"AWS4-HMAC-SHA256",
|
|
6477
|
+
amzDate,
|
|
6478
|
+
credentialScope,
|
|
6479
|
+
sha256(canonicalRequest)
|
|
6480
|
+
].join("\n");
|
|
6481
|
+
const signingKey = getSignatureKey(
|
|
6482
|
+
req.credentials.secretAccessKey,
|
|
6483
|
+
dateStamp,
|
|
6484
|
+
req.region,
|
|
6485
|
+
req.service
|
|
6486
|
+
);
|
|
6487
|
+
const signature = hmacHex(signingKey, stringToSign);
|
|
6488
|
+
const authorization = `AWS4-HMAC-SHA256 Credential=${req.credentials.accessKeyId}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signature}`;
|
|
6489
|
+
const result = {
|
|
6490
|
+
...req.headers,
|
|
6491
|
+
"x-amz-date": amzDate,
|
|
6492
|
+
Authorization: authorization
|
|
6493
|
+
};
|
|
6494
|
+
if (req.credentials.sessionToken) {
|
|
6495
|
+
result["x-amz-security-token"] = req.credentials.sessionToken;
|
|
6496
|
+
}
|
|
6497
|
+
return result;
|
|
6498
|
+
}
|
|
6499
|
+
function toDateStamp(d) {
|
|
6500
|
+
return d.toISOString().replace(/[-:T]/g, "").slice(0, 8);
|
|
6501
|
+
}
|
|
6502
|
+
function toAmzDate(d) {
|
|
6503
|
+
return d.toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "Z");
|
|
6504
|
+
}
|
|
6505
|
+
function sha256(data) {
|
|
6506
|
+
return crypto.createHash("sha256").update(data, "utf8").digest("hex");
|
|
6507
|
+
}
|
|
6508
|
+
function hmac(key, data) {
|
|
6509
|
+
return crypto.createHmac("sha256", key).update(data, "utf8").digest();
|
|
6510
|
+
}
|
|
6511
|
+
function hmacHex(key, data) {
|
|
6512
|
+
return crypto.createHmac("sha256", key).update(data, "utf8").digest("hex");
|
|
6513
|
+
}
|
|
6514
|
+
function getSignatureKey(secret, dateStamp, region, service) {
|
|
6515
|
+
const kDate = hmac(`AWS4${secret}`, dateStamp);
|
|
6516
|
+
const kRegion = hmac(kDate, region);
|
|
6517
|
+
const kService = hmac(kRegion, service);
|
|
6518
|
+
return hmac(kService, "aws4_request");
|
|
6519
|
+
}
|
|
6520
|
+
function sortQueryParams(params) {
|
|
6521
|
+
const sorted = [...params.entries()].sort(([a], [b]) => a.localeCompare(b));
|
|
6522
|
+
return sorted.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&");
|
|
6523
|
+
}
|
|
6524
|
+
|
|
6525
|
+
// src/providers/bedrock.ts
|
|
6526
|
+
var BedrockProvider = class extends BaseProvider {
|
|
6527
|
+
constructor(config) {
|
|
6528
|
+
super();
|
|
6529
|
+
this.providerName = "bedrock";
|
|
6530
|
+
this.region = config.region;
|
|
6531
|
+
this.defaultModel = config.defaultModel ?? "anthropic.claude-sonnet-4-20250514-v1:0";
|
|
6532
|
+
this.defaultHeaders = config.defaultHeaders ?? {};
|
|
6533
|
+
this.credentials = {
|
|
6534
|
+
accessKeyId: config.accessKeyId ?? process.env.AWS_ACCESS_KEY_ID ?? "",
|
|
6535
|
+
secretAccessKey: config.secretAccessKey ?? process.env.AWS_SECRET_ACCESS_KEY ?? "",
|
|
6536
|
+
sessionToken: config.sessionToken ?? process.env.AWS_SESSION_TOKEN
|
|
6537
|
+
};
|
|
6538
|
+
if (!this.credentials.accessKeyId || !this.credentials.secretAccessKey) {
|
|
6539
|
+
throw new ProviderError(
|
|
6540
|
+
"AWS credentials required: provide accessKeyId/secretAccessKey or set AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY env vars",
|
|
6541
|
+
"bedrock"
|
|
6542
|
+
);
|
|
6543
|
+
}
|
|
6544
|
+
}
|
|
6545
|
+
async complete(request) {
|
|
6546
|
+
const modelId = request.model || this.defaultModel;
|
|
6547
|
+
const body = this.buildConverseBody(request);
|
|
6548
|
+
const json = await this.doFetch(modelId, body);
|
|
6549
|
+
return this.parseResponse(json, modelId);
|
|
6550
|
+
}
|
|
6551
|
+
async *completeStream(request) {
|
|
6552
|
+
const response = await this.complete(request);
|
|
6553
|
+
yield {
|
|
6554
|
+
id: response.id,
|
|
6555
|
+
model: response.model,
|
|
6556
|
+
delta: {
|
|
6557
|
+
content: response.content ?? void 0,
|
|
6558
|
+
toolCalls: response.toolCalls.length > 0 ? response.toolCalls : void 0
|
|
6559
|
+
},
|
|
6560
|
+
usage: response.usage,
|
|
6561
|
+
finishReason: response.finishReason
|
|
6562
|
+
};
|
|
6563
|
+
}
|
|
6564
|
+
// ─── Internals ───────────────────────────────────────────────────────────
|
|
6565
|
+
buildConverseBody(request) {
|
|
6566
|
+
const { system, messages } = this.splitSystemMessages(request.messages);
|
|
6567
|
+
const body = {
|
|
6568
|
+
messages: messages.map((m) => this.toBedrockMessage(m))
|
|
6569
|
+
};
|
|
6570
|
+
if (system.length > 0) {
|
|
6571
|
+
body.system = system.map((s) => ({ text: s }));
|
|
6572
|
+
}
|
|
6573
|
+
const inferenceConfig = {};
|
|
6574
|
+
if (request.maxTokens != null) inferenceConfig.maxTokens = request.maxTokens;
|
|
6575
|
+
if (request.temperature != null) inferenceConfig.temperature = request.temperature;
|
|
6576
|
+
if (request.topP != null) inferenceConfig.topP = request.topP;
|
|
6577
|
+
if (request.stop) inferenceConfig.stopSequences = request.stop;
|
|
6578
|
+
if (Object.keys(inferenceConfig).length > 0) body.inferenceConfig = inferenceConfig;
|
|
6579
|
+
if (request.tools?.length) {
|
|
6580
|
+
body.toolConfig = {
|
|
6581
|
+
tools: request.tools.map((t) => this.toBedrockTool(t))
|
|
6582
|
+
};
|
|
6583
|
+
if (request.toolChoice != null) {
|
|
6584
|
+
if (request.toolChoice === "auto") {
|
|
6585
|
+
body.toolConfig.toolChoice = { auto: {} };
|
|
6586
|
+
} else if (request.toolChoice === "required") {
|
|
6587
|
+
body.toolConfig.toolChoice = { any: {} };
|
|
6588
|
+
} else if (typeof request.toolChoice === "object") {
|
|
6589
|
+
body.toolConfig.toolChoice = { tool: { name: request.toolChoice.name } };
|
|
6590
|
+
}
|
|
6591
|
+
}
|
|
6592
|
+
}
|
|
6593
|
+
if (request.extra) {
|
|
6594
|
+
Object.assign(body, request.extra);
|
|
6595
|
+
}
|
|
6596
|
+
return body;
|
|
6597
|
+
}
|
|
6598
|
+
splitSystemMessages(messages) {
|
|
6599
|
+
const system = [];
|
|
6600
|
+
const rest = [];
|
|
6601
|
+
for (const m of messages) {
|
|
6602
|
+
if (m.role === "system") {
|
|
6603
|
+
const text = typeof m.content === "string" ? m.content : m.content.filter((p) => p.type === "text").map((p) => p.text).join("\n");
|
|
6604
|
+
if (text) system.push(text);
|
|
6605
|
+
} else {
|
|
6606
|
+
rest.push(m);
|
|
6607
|
+
}
|
|
6608
|
+
}
|
|
6609
|
+
return { system, messages: rest };
|
|
6610
|
+
}
|
|
6611
|
+
toBedrockMessage(m) {
|
|
6612
|
+
if (m.role === "tool" && m.toolCallId) {
|
|
6613
|
+
const text = typeof m.content === "string" ? m.content : JSON.stringify(m.content);
|
|
6614
|
+
return {
|
|
6615
|
+
role: "user",
|
|
6616
|
+
content: [
|
|
6617
|
+
{
|
|
6618
|
+
toolResult: {
|
|
6619
|
+
toolUseId: m.toolCallId,
|
|
6620
|
+
content: [{ text }],
|
|
6621
|
+
status: "success"
|
|
6622
|
+
}
|
|
6623
|
+
}
|
|
6624
|
+
]
|
|
6625
|
+
};
|
|
6626
|
+
}
|
|
6627
|
+
if (m.role === "assistant" && m.toolCalls?.length) {
|
|
6628
|
+
const content2 = [];
|
|
6629
|
+
if (typeof m.content === "string" && m.content) {
|
|
6630
|
+
content2.push({ text: m.content });
|
|
6631
|
+
}
|
|
6632
|
+
for (const tc of m.toolCalls) {
|
|
6633
|
+
content2.push({
|
|
6634
|
+
toolUse: {
|
|
6635
|
+
toolUseId: tc.id,
|
|
6636
|
+
name: tc.name,
|
|
6637
|
+
input: safeJsonParse2(tc.arguments)
|
|
6638
|
+
}
|
|
6639
|
+
});
|
|
6640
|
+
}
|
|
6641
|
+
return { role: "assistant", content: content2 };
|
|
6642
|
+
}
|
|
6643
|
+
const content = [];
|
|
6644
|
+
if (typeof m.content === "string") {
|
|
6645
|
+
content.push({ text: m.content });
|
|
6646
|
+
} else if (Array.isArray(m.content)) {
|
|
6647
|
+
for (const part of m.content) {
|
|
6648
|
+
if (part.type === "text") {
|
|
6649
|
+
content.push({ text: part.text });
|
|
6650
|
+
} else if (part.type === "image") {
|
|
6651
|
+
const src = part.source;
|
|
6652
|
+
if (src.type === "base64") {
|
|
6653
|
+
content.push({
|
|
6654
|
+
image: {
|
|
6655
|
+
format: mediaTypeToFormat(src.mediaType),
|
|
6656
|
+
source: { bytes: src.data }
|
|
6657
|
+
}
|
|
6658
|
+
});
|
|
6659
|
+
}
|
|
6660
|
+
}
|
|
6661
|
+
}
|
|
6662
|
+
}
|
|
6663
|
+
return { role: m.role === "user" ? "user" : "assistant", content };
|
|
6664
|
+
}
|
|
6665
|
+
toBedrockTool(t) {
|
|
6666
|
+
return {
|
|
6667
|
+
toolSpec: {
|
|
6668
|
+
name: t.name,
|
|
6669
|
+
description: t.description,
|
|
6670
|
+
inputSchema: { json: t.parameters }
|
|
6671
|
+
}
|
|
6672
|
+
};
|
|
6673
|
+
}
|
|
6674
|
+
async doFetch(modelId, body) {
|
|
6675
|
+
const host = `bedrock-runtime.${this.region}.amazonaws.com`;
|
|
6676
|
+
const url = `https://${host}/model/${encodeURIComponent(modelId)}/converse`;
|
|
6677
|
+
const bodyStr = JSON.stringify(body);
|
|
6678
|
+
const headers = signRequest({
|
|
6679
|
+
method: "POST",
|
|
6680
|
+
url,
|
|
6681
|
+
headers: {
|
|
6682
|
+
"Content-Type": "application/json",
|
|
6683
|
+
Accept: "application/json",
|
|
6684
|
+
...this.defaultHeaders
|
|
6685
|
+
},
|
|
6686
|
+
body: bodyStr,
|
|
6687
|
+
region: this.region,
|
|
6688
|
+
service: "bedrock",
|
|
6689
|
+
credentials: this.credentials
|
|
6690
|
+
});
|
|
6691
|
+
const res = await fetch(url, {
|
|
6692
|
+
method: "POST",
|
|
6693
|
+
headers,
|
|
6694
|
+
body: bodyStr
|
|
6695
|
+
});
|
|
6696
|
+
if (!res.ok) {
|
|
6697
|
+
const text = await res.text().catch(() => "");
|
|
6698
|
+
throw new ProviderError(
|
|
6699
|
+
`Bedrock API error ${res.status}: ${text}`,
|
|
6700
|
+
this.providerName,
|
|
6701
|
+
res.status,
|
|
6702
|
+
text
|
|
6703
|
+
);
|
|
6704
|
+
}
|
|
6705
|
+
return res.json();
|
|
6706
|
+
}
|
|
6707
|
+
parseResponse(json, modelId) {
|
|
6708
|
+
const output = json.output?.message;
|
|
6709
|
+
const textParts = [];
|
|
6710
|
+
const toolCalls = [];
|
|
6711
|
+
for (const block of output?.content ?? []) {
|
|
6712
|
+
if (block.text) {
|
|
6713
|
+
textParts.push(block.text);
|
|
6714
|
+
} else if (block.toolUse) {
|
|
6715
|
+
toolCalls.push({
|
|
6716
|
+
id: block.toolUse.toolUseId,
|
|
6717
|
+
name: block.toolUse.name,
|
|
6718
|
+
arguments: typeof block.toolUse.input === "string" ? block.toolUse.input : JSON.stringify(block.toolUse.input ?? {})
|
|
6719
|
+
});
|
|
6720
|
+
}
|
|
6721
|
+
}
|
|
6722
|
+
const usage = json.usage ?? {};
|
|
6723
|
+
const tokenUsage = {
|
|
6724
|
+
inputTokens: usage.inputTokens ?? 0,
|
|
6725
|
+
outputTokens: usage.outputTokens ?? 0,
|
|
6726
|
+
totalTokens: usage.totalTokens ?? (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0),
|
|
6727
|
+
cachedInputTokens: usage.cacheReadInputTokenCount ?? 0,
|
|
6728
|
+
cachedWriteTokens: usage.cacheWriteInputTokenCount ?? 0,
|
|
6729
|
+
cachedOutputTokens: 0,
|
|
6730
|
+
reasoningTokens: 0
|
|
6731
|
+
};
|
|
6732
|
+
return {
|
|
6733
|
+
id: json.requestId ?? json.$metadata?.requestId ?? "",
|
|
6734
|
+
model: modelId,
|
|
6735
|
+
content: textParts.join("") || null,
|
|
6736
|
+
toolCalls,
|
|
6737
|
+
usage: tokenUsage,
|
|
6738
|
+
finishReason: mapBedrockStopReason(json.stopReason),
|
|
6739
|
+
raw: json
|
|
6740
|
+
};
|
|
6741
|
+
}
|
|
6742
|
+
};
|
|
6743
|
+
function mapBedrockStopReason(reason) {
|
|
6744
|
+
switch (reason) {
|
|
6745
|
+
case "end_turn":
|
|
6746
|
+
case "stop_sequence":
|
|
6747
|
+
return "stop";
|
|
6748
|
+
case "tool_use":
|
|
6749
|
+
return "tool_calls";
|
|
6750
|
+
case "max_tokens":
|
|
6751
|
+
return "length";
|
|
6752
|
+
case "content_filtered":
|
|
6753
|
+
case "guardrail":
|
|
6754
|
+
return "content_filter";
|
|
6755
|
+
default:
|
|
6756
|
+
return "stop";
|
|
6757
|
+
}
|
|
6758
|
+
}
|
|
6759
|
+
function mediaTypeToFormat(mediaType) {
|
|
6760
|
+
const map = {
|
|
6761
|
+
"image/jpeg": "jpeg",
|
|
6762
|
+
"image/png": "png",
|
|
6763
|
+
"image/gif": "gif",
|
|
6764
|
+
"image/webp": "webp"
|
|
6765
|
+
};
|
|
6766
|
+
return map[mediaType] ?? "png";
|
|
6767
|
+
}
|
|
6768
|
+
function safeJsonParse2(str) {
|
|
6769
|
+
try {
|
|
6770
|
+
return JSON.parse(str);
|
|
6771
|
+
} catch {
|
|
6772
|
+
return {};
|
|
6773
|
+
}
|
|
6774
|
+
}
|
|
6775
|
+
|
|
6776
|
+
// src/providers/vertex.ts
|
|
6777
|
+
var VertexProvider = class extends BaseProvider {
|
|
6778
|
+
constructor(config) {
|
|
6779
|
+
super();
|
|
6780
|
+
this.providerName = "vertex";
|
|
6781
|
+
this.tokenExpiry = 0;
|
|
6782
|
+
this.projectId = config.projectId;
|
|
6783
|
+
this.location = config.location ?? "us-central1";
|
|
6784
|
+
this.defaultModel = config.defaultModel ?? "gemini-2.0-flash";
|
|
6785
|
+
this.defaultHeaders = config.defaultHeaders ?? {};
|
|
6786
|
+
if (config.accessToken) {
|
|
6787
|
+
this.accessToken = config.accessToken;
|
|
6788
|
+
this.tokenExpiry = Date.now() + 36e5;
|
|
6789
|
+
}
|
|
6790
|
+
if (config.serviceAccountJson) {
|
|
6791
|
+
this.serviceAccountJson = typeof config.serviceAccountJson === "string" ? JSON.parse(config.serviceAccountJson) : config.serviceAccountJson;
|
|
6792
|
+
}
|
|
6793
|
+
}
|
|
6794
|
+
async complete(request) {
|
|
6795
|
+
const modelId = request.model || this.defaultModel;
|
|
6796
|
+
const body = this.buildGeminiBody(request);
|
|
6797
|
+
const url = this.buildUrl(modelId, false);
|
|
6798
|
+
const res = await this.doFetch(url, body);
|
|
6799
|
+
const json = await res.json();
|
|
6800
|
+
return this.parseResponse(json, modelId);
|
|
6801
|
+
}
|
|
6802
|
+
async *completeStream(request) {
|
|
6803
|
+
const modelId = request.model || this.defaultModel;
|
|
6804
|
+
const body = this.buildGeminiBody(request);
|
|
6805
|
+
const url = this.buildUrl(modelId, true);
|
|
6806
|
+
const res = await this.doFetch(url, body);
|
|
6807
|
+
if (!res.body) throw new ProviderError("No response body for stream", this.providerName);
|
|
6808
|
+
const contentType = res.headers.get("content-type") ?? "";
|
|
6809
|
+
if (contentType.includes("text/event-stream")) {
|
|
6810
|
+
for await (const event of parseSSEStream(res.body)) {
|
|
6811
|
+
if (event.data === "[DONE]") break;
|
|
6812
|
+
let chunk;
|
|
6813
|
+
try {
|
|
6814
|
+
chunk = JSON.parse(event.data);
|
|
6815
|
+
} catch {
|
|
6816
|
+
continue;
|
|
6817
|
+
}
|
|
6818
|
+
yield this.parseStreamChunk(chunk, modelId);
|
|
6819
|
+
}
|
|
6820
|
+
} else {
|
|
6821
|
+
const reader = res.body.getReader();
|
|
6822
|
+
const decoder = new TextDecoder();
|
|
6823
|
+
let buffer = "";
|
|
6824
|
+
try {
|
|
6825
|
+
while (true) {
|
|
6826
|
+
const { done, value } = await reader.read();
|
|
6827
|
+
if (done) break;
|
|
6828
|
+
buffer += decoder.decode(value, { stream: true });
|
|
6829
|
+
const lines = buffer.split("\n");
|
|
6830
|
+
buffer = lines.pop() ?? "";
|
|
6831
|
+
for (const line of lines) {
|
|
6832
|
+
const trimmed = line.trim().replace(/^,/, "").replace(/^\[/, "").replace(/\]$/, "");
|
|
6833
|
+
if (!trimmed || trimmed === "[" || trimmed === "]") continue;
|
|
6834
|
+
try {
|
|
6835
|
+
const chunk = JSON.parse(trimmed);
|
|
6836
|
+
yield this.parseStreamChunk(chunk, modelId);
|
|
6837
|
+
} catch {
|
|
6838
|
+
buffer = line + "\n" + buffer;
|
|
6839
|
+
break;
|
|
6840
|
+
}
|
|
6841
|
+
}
|
|
6842
|
+
}
|
|
6843
|
+
const remaining = buffer.trim().replace(/^,/, "").replace(/^\[/, "").replace(/\]$/, "");
|
|
6844
|
+
if (remaining && remaining !== "[" && remaining !== "]") {
|
|
6845
|
+
try {
|
|
6846
|
+
const chunk = JSON.parse(remaining);
|
|
6847
|
+
yield this.parseStreamChunk(chunk, modelId);
|
|
6848
|
+
} catch {
|
|
6849
|
+
}
|
|
6850
|
+
}
|
|
6851
|
+
} finally {
|
|
6852
|
+
reader.releaseLock();
|
|
6853
|
+
}
|
|
6854
|
+
}
|
|
6855
|
+
}
|
|
6856
|
+
// ─── Internals ───────────────────────────────────────────────────────────
|
|
6857
|
+
buildUrl(modelId, stream) {
|
|
6858
|
+
const base = `https://${this.location}-aiplatform.googleapis.com/v1`;
|
|
6859
|
+
const path2 = `projects/${this.projectId}/locations/${this.location}/publishers/google/models/${modelId}`;
|
|
6860
|
+
const action = stream ? "streamGenerateContent?alt=sse" : "generateContent";
|
|
6861
|
+
return `${base}/${path2}:${action}`;
|
|
6862
|
+
}
|
|
6863
|
+
buildGeminiBody(request) {
|
|
6864
|
+
const { system, contents } = this.buildContents(request.messages);
|
|
6865
|
+
const body = { contents };
|
|
6866
|
+
if (system) {
|
|
6867
|
+
body.systemInstruction = { parts: [{ text: system }] };
|
|
6868
|
+
}
|
|
6869
|
+
const generationConfig = {};
|
|
6870
|
+
if (request.maxTokens != null) generationConfig.maxOutputTokens = request.maxTokens;
|
|
6871
|
+
if (request.temperature != null) generationConfig.temperature = request.temperature;
|
|
6872
|
+
if (request.topP != null) generationConfig.topP = request.topP;
|
|
6873
|
+
if (request.stop) generationConfig.stopSequences = request.stop;
|
|
6874
|
+
if (request.responseFormat) {
|
|
6875
|
+
if (request.responseFormat.type === "json_schema" || request.responseFormat.type === "json_object") {
|
|
6876
|
+
generationConfig.responseMimeType = "application/json";
|
|
6877
|
+
if (request.responseFormat.schema) {
|
|
6878
|
+
generationConfig.responseSchema = request.responseFormat.schema;
|
|
6879
|
+
}
|
|
6880
|
+
}
|
|
6881
|
+
}
|
|
6882
|
+
if (Object.keys(generationConfig).length > 0) body.generationConfig = generationConfig;
|
|
6883
|
+
if (request.tools?.length) {
|
|
6884
|
+
body.tools = [
|
|
6885
|
+
{
|
|
6886
|
+
functionDeclarations: request.tools.map((t) => this.toGeminiTool(t))
|
|
6887
|
+
}
|
|
6888
|
+
];
|
|
6889
|
+
if (request.toolChoice != null) {
|
|
6890
|
+
const mode = request.toolChoice === "auto" ? "AUTO" : request.toolChoice === "required" ? "ANY" : request.toolChoice === "none" ? "NONE" : "AUTO";
|
|
6891
|
+
body.toolConfig = { functionCallingConfig: { mode } };
|
|
6892
|
+
}
|
|
6893
|
+
}
|
|
6894
|
+
if (request.extra) {
|
|
6895
|
+
Object.assign(body, request.extra);
|
|
6896
|
+
}
|
|
6897
|
+
return body;
|
|
6898
|
+
}
|
|
6899
|
+
buildContents(messages) {
|
|
6900
|
+
const systemParts = [];
|
|
6901
|
+
const contents = [];
|
|
6902
|
+
for (const m of messages) {
|
|
6903
|
+
if (m.role === "system") {
|
|
6904
|
+
const text = typeof m.content === "string" ? m.content : m.content.filter((p) => p.type === "text").map((p) => p.text).join("\n");
|
|
6905
|
+
if (text) systemParts.push(text);
|
|
6906
|
+
continue;
|
|
6907
|
+
}
|
|
6908
|
+
contents.push(this.toGeminiContent(m));
|
|
6909
|
+
}
|
|
6910
|
+
return {
|
|
6911
|
+
system: systemParts.length > 0 ? systemParts.join("\n\n") : void 0,
|
|
6912
|
+
contents
|
|
6913
|
+
};
|
|
6914
|
+
}
|
|
6915
|
+
toGeminiContent(m) {
|
|
6916
|
+
if (m.role === "tool" && m.toolCallId) {
|
|
6917
|
+
const text = typeof m.content === "string" ? m.content : JSON.stringify(m.content);
|
|
6918
|
+
let response;
|
|
6919
|
+
try {
|
|
6920
|
+
response = JSON.parse(text);
|
|
6921
|
+
} catch {
|
|
6922
|
+
response = { result: text };
|
|
6923
|
+
}
|
|
6924
|
+
return {
|
|
6925
|
+
role: "user",
|
|
6926
|
+
parts: [
|
|
6927
|
+
{
|
|
6928
|
+
functionResponse: {
|
|
6929
|
+
name: m.name ?? m.toolCallId,
|
|
6930
|
+
response
|
|
6931
|
+
}
|
|
6932
|
+
}
|
|
6933
|
+
]
|
|
6934
|
+
};
|
|
6935
|
+
}
|
|
6936
|
+
if (m.role === "assistant" && m.toolCalls?.length) {
|
|
6937
|
+
const parts2 = [];
|
|
6938
|
+
if (typeof m.content === "string" && m.content) {
|
|
6939
|
+
parts2.push({ text: m.content });
|
|
6940
|
+
}
|
|
6941
|
+
for (const tc of m.toolCalls) {
|
|
6942
|
+
parts2.push({
|
|
6943
|
+
functionCall: {
|
|
6944
|
+
name: tc.name,
|
|
6945
|
+
args: safeJsonParse3(tc.arguments)
|
|
6946
|
+
}
|
|
6947
|
+
});
|
|
6948
|
+
}
|
|
6949
|
+
return { role: "model", parts: parts2 };
|
|
6950
|
+
}
|
|
6951
|
+
const role = m.role === "assistant" ? "model" : "user";
|
|
6952
|
+
const parts = [];
|
|
6953
|
+
if (typeof m.content === "string") {
|
|
6954
|
+
parts.push({ text: m.content });
|
|
6955
|
+
} else if (Array.isArray(m.content)) {
|
|
6956
|
+
for (const part of m.content) {
|
|
6957
|
+
if (part.type === "text") {
|
|
6958
|
+
parts.push({ text: part.text });
|
|
6959
|
+
} else if (part.type === "image") {
|
|
6960
|
+
const src = part.source;
|
|
6961
|
+
if (src.type === "base64") {
|
|
6962
|
+
parts.push({
|
|
6963
|
+
inlineData: { mimeType: src.mediaType, data: src.data }
|
|
6964
|
+
});
|
|
6965
|
+
} else {
|
|
6966
|
+
parts.push({
|
|
6967
|
+
fileData: { mimeType: "image/jpeg", fileUri: src.url }
|
|
6968
|
+
});
|
|
6969
|
+
}
|
|
6970
|
+
}
|
|
6971
|
+
}
|
|
6972
|
+
}
|
|
6973
|
+
return { role, parts };
|
|
6974
|
+
}
|
|
6975
|
+
toGeminiTool(t) {
|
|
6976
|
+
return {
|
|
6977
|
+
name: t.name,
|
|
6978
|
+
description: t.description,
|
|
6979
|
+
parameters: t.parameters
|
|
6980
|
+
};
|
|
6981
|
+
}
|
|
6982
|
+
async getAccessToken() {
|
|
6983
|
+
if (this.accessToken && Date.now() < this.tokenExpiry) {
|
|
6984
|
+
return this.accessToken;
|
|
6985
|
+
}
|
|
6986
|
+
if (this.serviceAccountJson) {
|
|
6987
|
+
const token = await this.getTokenFromServiceAccount(this.serviceAccountJson);
|
|
6988
|
+
this.accessToken = token;
|
|
6989
|
+
this.tokenExpiry = Date.now() + 35e5;
|
|
6990
|
+
return token;
|
|
6991
|
+
}
|
|
6992
|
+
try {
|
|
6993
|
+
const { execSync } = await import('child_process');
|
|
6994
|
+
const token = execSync("gcloud auth print-access-token", { encoding: "utf8" }).trim();
|
|
6995
|
+
this.accessToken = token;
|
|
6996
|
+
this.tokenExpiry = Date.now() + 35e5;
|
|
6997
|
+
return token;
|
|
6998
|
+
} catch {
|
|
6999
|
+
throw new ProviderError(
|
|
7000
|
+
"No access token available. Provide accessToken, serviceAccountJson, or ensure gcloud CLI is authenticated.",
|
|
7001
|
+
this.providerName
|
|
7002
|
+
);
|
|
7003
|
+
}
|
|
7004
|
+
}
|
|
7005
|
+
async getTokenFromServiceAccount(sa) {
|
|
7006
|
+
const { createSign } = await import('crypto');
|
|
7007
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
7008
|
+
const header = { alg: "RS256", typ: "JWT" };
|
|
7009
|
+
const claims = {
|
|
7010
|
+
iss: sa.client_email,
|
|
7011
|
+
scope: "https://www.googleapis.com/auth/cloud-platform",
|
|
7012
|
+
aud: "https://oauth2.googleapis.com/token",
|
|
7013
|
+
iat: now,
|
|
7014
|
+
exp: now + 3600
|
|
7015
|
+
};
|
|
7016
|
+
const segments = [
|
|
7017
|
+
base64url(JSON.stringify(header)),
|
|
7018
|
+
base64url(JSON.stringify(claims))
|
|
7019
|
+
];
|
|
7020
|
+
const signingInput = segments.join(".");
|
|
7021
|
+
const sign = createSign("RSA-SHA256");
|
|
7022
|
+
sign.update(signingInput);
|
|
7023
|
+
const signature = sign.sign(sa.private_key, "base64url");
|
|
7024
|
+
const jwt = `${signingInput}.${signature}`;
|
|
7025
|
+
const tokenRes = await fetch("https://oauth2.googleapis.com/token", {
|
|
7026
|
+
method: "POST",
|
|
7027
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
7028
|
+
body: `grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=${jwt}`
|
|
7029
|
+
});
|
|
7030
|
+
if (!tokenRes.ok) {
|
|
7031
|
+
const text = await tokenRes.text().catch(() => "");
|
|
7032
|
+
throw new ProviderError(`Failed to get Vertex access token: ${text}`, this.providerName);
|
|
7033
|
+
}
|
|
7034
|
+
const tokenJson = await tokenRes.json();
|
|
7035
|
+
return tokenJson.access_token;
|
|
7036
|
+
}
|
|
7037
|
+
async doFetch(url, body) {
|
|
7038
|
+
const token = await this.getAccessToken();
|
|
7039
|
+
const res = await fetch(url, {
|
|
7040
|
+
method: "POST",
|
|
7041
|
+
headers: {
|
|
7042
|
+
"Content-Type": "application/json",
|
|
7043
|
+
Authorization: `Bearer ${token}`,
|
|
7044
|
+
...this.defaultHeaders
|
|
7045
|
+
},
|
|
7046
|
+
body: JSON.stringify(body)
|
|
7047
|
+
});
|
|
7048
|
+
if (!res.ok) {
|
|
7049
|
+
const text = await res.text().catch(() => "");
|
|
7050
|
+
throw new ProviderError(
|
|
7051
|
+
`Vertex AI API error ${res.status}: ${text}`,
|
|
7052
|
+
this.providerName,
|
|
7053
|
+
res.status,
|
|
7054
|
+
text
|
|
7055
|
+
);
|
|
7056
|
+
}
|
|
7057
|
+
return res;
|
|
7058
|
+
}
|
|
7059
|
+
parseResponse(json, modelId) {
|
|
7060
|
+
const candidate = json.candidates?.[0];
|
|
7061
|
+
const parts = candidate?.content?.parts ?? [];
|
|
7062
|
+
const textParts = [];
|
|
7063
|
+
const toolCalls = [];
|
|
7064
|
+
let toolCallIdx = 0;
|
|
7065
|
+
for (const part of parts) {
|
|
7066
|
+
if (part.text) {
|
|
7067
|
+
textParts.push(part.text);
|
|
7068
|
+
} else if (part.functionCall) {
|
|
7069
|
+
toolCalls.push({
|
|
7070
|
+
id: `call_${toolCallIdx++}`,
|
|
7071
|
+
name: part.functionCall.name,
|
|
7072
|
+
arguments: JSON.stringify(part.functionCall.args ?? {})
|
|
7073
|
+
});
|
|
7074
|
+
}
|
|
7075
|
+
}
|
|
7076
|
+
const usageMeta = json.usageMetadata ?? {};
|
|
7077
|
+
const tokenUsage = {
|
|
7078
|
+
inputTokens: usageMeta.promptTokenCount ?? 0,
|
|
7079
|
+
outputTokens: usageMeta.candidatesTokenCount ?? 0,
|
|
7080
|
+
totalTokens: usageMeta.totalTokenCount ?? 0,
|
|
7081
|
+
cachedInputTokens: usageMeta.cachedContentTokenCount ?? 0,
|
|
7082
|
+
cachedWriteTokens: 0,
|
|
7083
|
+
cachedOutputTokens: 0,
|
|
7084
|
+
reasoningTokens: usageMeta.thoughtsTokenCount ?? 0
|
|
7085
|
+
};
|
|
7086
|
+
return {
|
|
7087
|
+
id: json.responseId ?? "",
|
|
7088
|
+
model: modelId,
|
|
7089
|
+
content: textParts.join("") || null,
|
|
7090
|
+
toolCalls,
|
|
7091
|
+
usage: tokenUsage,
|
|
7092
|
+
finishReason: mapGeminiFinishReason(candidate?.finishReason),
|
|
7093
|
+
raw: json
|
|
7094
|
+
};
|
|
7095
|
+
}
|
|
7096
|
+
parseStreamChunk(chunk, modelId) {
|
|
7097
|
+
const candidate = chunk.candidates?.[0];
|
|
7098
|
+
const parts = candidate?.content?.parts ?? [];
|
|
7099
|
+
const delta = {};
|
|
7100
|
+
for (const part of parts) {
|
|
7101
|
+
if (part.text) {
|
|
7102
|
+
delta.content = (delta.content ?? "") + part.text;
|
|
7103
|
+
} else if (part.functionCall) {
|
|
7104
|
+
if (!delta.toolCalls) delta.toolCalls = [];
|
|
7105
|
+
delta.toolCalls.push({
|
|
7106
|
+
id: `call_${delta.toolCalls.length}`,
|
|
7107
|
+
name: part.functionCall.name,
|
|
7108
|
+
arguments: JSON.stringify(part.functionCall.args ?? {})
|
|
7109
|
+
});
|
|
7110
|
+
}
|
|
7111
|
+
}
|
|
7112
|
+
let usage;
|
|
7113
|
+
if (chunk.usageMetadata) {
|
|
7114
|
+
const u = chunk.usageMetadata;
|
|
7115
|
+
usage = {
|
|
7116
|
+
inputTokens: u.promptTokenCount ?? 0,
|
|
7117
|
+
outputTokens: u.candidatesTokenCount ?? 0,
|
|
7118
|
+
totalTokens: u.totalTokenCount ?? 0,
|
|
7119
|
+
cachedInputTokens: u.cachedContentTokenCount ?? 0,
|
|
7120
|
+
cachedWriteTokens: 0,
|
|
7121
|
+
cachedOutputTokens: 0,
|
|
7122
|
+
reasoningTokens: u.thoughtsTokenCount ?? 0
|
|
7123
|
+
};
|
|
7124
|
+
}
|
|
7125
|
+
return {
|
|
7126
|
+
id: chunk.responseId ?? "",
|
|
7127
|
+
model: modelId,
|
|
7128
|
+
delta,
|
|
7129
|
+
usage,
|
|
7130
|
+
finishReason: mapGeminiFinishReason(candidate?.finishReason)
|
|
7131
|
+
};
|
|
7132
|
+
}
|
|
7133
|
+
};
|
|
7134
|
+
function mapGeminiFinishReason(reason) {
|
|
7135
|
+
switch (reason) {
|
|
7136
|
+
case "STOP":
|
|
7137
|
+
return "stop";
|
|
7138
|
+
case "MAX_TOKENS":
|
|
7139
|
+
return "length";
|
|
7140
|
+
case "SAFETY":
|
|
7141
|
+
case "RECITATION":
|
|
7142
|
+
return "content_filter";
|
|
7143
|
+
case "TOOL_CALLS":
|
|
7144
|
+
return "tool_calls";
|
|
7145
|
+
default:
|
|
7146
|
+
return "stop";
|
|
7147
|
+
}
|
|
7148
|
+
}
|
|
7149
|
+
function base64url(str) {
|
|
7150
|
+
return Buffer.from(str).toString("base64url");
|
|
7151
|
+
}
|
|
7152
|
+
function safeJsonParse3(str) {
|
|
7153
|
+
try {
|
|
7154
|
+
return JSON.parse(str);
|
|
7155
|
+
} catch {
|
|
7156
|
+
return {};
|
|
7157
|
+
}
|
|
7158
|
+
}
|
|
7159
|
+
function fromNativeProvider(provider, options) {
|
|
7160
|
+
let boundTools = options?.__tools;
|
|
7161
|
+
const modelName = options?.model ?? provider.defaultModel ?? provider.providerName;
|
|
7162
|
+
const adapted = {
|
|
7163
|
+
modelName,
|
|
7164
|
+
capabilities: resolveCapabilities(provider.providerName),
|
|
7165
|
+
invoke: async (messages, _invokeOptions) => {
|
|
7166
|
+
const request = buildRequest(messages, boundTools, options, _invokeOptions);
|
|
7167
|
+
const response = await provider.complete(request);
|
|
7168
|
+
return toBaseChatMessage(response);
|
|
7169
|
+
},
|
|
7170
|
+
stream: async function* (messages, _invokeOptions) {
|
|
7171
|
+
const request = buildRequest(messages, boundTools, options, _invokeOptions);
|
|
7172
|
+
request.stream = true;
|
|
7173
|
+
let fullContent = "";
|
|
7174
|
+
let allToolCalls = [];
|
|
7175
|
+
let lastUsage;
|
|
7176
|
+
const toolCallBuffers = /* @__PURE__ */ new Map();
|
|
7177
|
+
for await (const chunk of provider.completeStream(request)) {
|
|
7178
|
+
if (chunk.delta.content) {
|
|
7179
|
+
fullContent += chunk.delta.content;
|
|
7180
|
+
yield chunk.delta.content;
|
|
7181
|
+
}
|
|
7182
|
+
if (chunk.delta.toolCalls) {
|
|
7183
|
+
for (const tc of chunk.delta.toolCalls) {
|
|
7184
|
+
if (tc.id) {
|
|
7185
|
+
const existing = toolCallBuffers.get(tc.id);
|
|
7186
|
+
if (existing) {
|
|
7187
|
+
if (tc.arguments) existing.arguments += tc.arguments;
|
|
7188
|
+
} else {
|
|
7189
|
+
toolCallBuffers.set(tc.id, {
|
|
7190
|
+
id: tc.id,
|
|
7191
|
+
name: tc.name ?? "",
|
|
7192
|
+
arguments: tc.arguments ?? ""
|
|
7193
|
+
});
|
|
7194
|
+
}
|
|
7195
|
+
}
|
|
7196
|
+
}
|
|
7197
|
+
}
|
|
7198
|
+
if (chunk.usage) lastUsage = chunk.usage;
|
|
7199
|
+
}
|
|
7200
|
+
allToolCalls = [...toolCallBuffers.values()];
|
|
7201
|
+
const finalMessage = assembleMessage(fullContent, allToolCalls, lastUsage);
|
|
7202
|
+
yield finalMessage;
|
|
7203
|
+
},
|
|
7204
|
+
bindTools: (tools, _bindOptions) => {
|
|
7205
|
+
const strict = _bindOptions?.strict ?? false;
|
|
7206
|
+
const toolDefs = tools.map((t) => toToolDefinition(t, strict));
|
|
7207
|
+
return fromNativeProvider(provider, { ...options, __tools: toolDefs });
|
|
7208
|
+
}
|
|
7209
|
+
};
|
|
7210
|
+
return adapted;
|
|
7211
|
+
function buildRequest(messages, tools, opts, invokeOptions) {
|
|
7212
|
+
const req = {
|
|
7213
|
+
model: opts?.model ?? modelName,
|
|
7214
|
+
messages: messages.map(toUnifiedMessage)
|
|
7215
|
+
};
|
|
7216
|
+
if (opts?.temperature != null) req.temperature = opts.temperature;
|
|
7217
|
+
if (opts?.maxTokens != null) req.maxTokens = opts.maxTokens;
|
|
7218
|
+
if (tools?.length) req.tools = tools;
|
|
7219
|
+
if (opts?.extra) req.extra = opts.extra;
|
|
7220
|
+
const rf = invokeOptions?.response_format;
|
|
7221
|
+
if (rf && typeof rf === "object" && rf.type) {
|
|
7222
|
+
if (rf.type === "json_schema" && rf.json_schema) {
|
|
7223
|
+
req.responseFormat = {
|
|
7224
|
+
type: "json_schema",
|
|
7225
|
+
schema: rf.json_schema.schema,
|
|
7226
|
+
name: rf.json_schema.name
|
|
7227
|
+
};
|
|
7228
|
+
} else if (rf.type === "json_object") {
|
|
7229
|
+
req.responseFormat = { type: "json_object" };
|
|
7230
|
+
} else if (rf.type === "text") {
|
|
7231
|
+
req.responseFormat = { type: "text" };
|
|
7232
|
+
}
|
|
7233
|
+
}
|
|
7234
|
+
if (!req.responseFormat) {
|
|
7235
|
+
const lastMsg = messages[messages.length - 1];
|
|
7236
|
+
if (lastMsg && lastMsg.__responseFormat) {
|
|
7237
|
+
req.responseFormat = lastMsg.__responseFormat;
|
|
7238
|
+
}
|
|
7239
|
+
}
|
|
7240
|
+
return req;
|
|
7241
|
+
}
|
|
7242
|
+
}
|
|
7243
|
+
function toUnifiedMessage(msg) {
|
|
7244
|
+
const unified = {
|
|
7245
|
+
role: msg.role,
|
|
7246
|
+
content: convertContent(msg.content)
|
|
7247
|
+
};
|
|
7248
|
+
if (msg.name) unified.name = msg.name;
|
|
7249
|
+
if (msg.tool_call_id) unified.toolCallId = msg.tool_call_id;
|
|
7250
|
+
if (msg.tool_calls?.length) {
|
|
7251
|
+
unified.toolCalls = msg.tool_calls.map((tc) => ({
|
|
7252
|
+
id: tc.id,
|
|
7253
|
+
name: tc.function?.name ?? tc.name ?? "",
|
|
7254
|
+
arguments: tc.function?.arguments ?? tc.arguments ?? "{}"
|
|
7255
|
+
}));
|
|
7256
|
+
}
|
|
7257
|
+
return unified;
|
|
7258
|
+
}
|
|
7259
|
+
function convertContent(content) {
|
|
7260
|
+
if (typeof content === "string") return content;
|
|
7261
|
+
if (!Array.isArray(content)) return String(content ?? "");
|
|
7262
|
+
return content.map((part) => {
|
|
7263
|
+
if (part.type === "text" || !part.type && part.text) {
|
|
7264
|
+
return { type: "text", text: part.text ?? part.content ?? "" };
|
|
7265
|
+
}
|
|
7266
|
+
if (part.type === "image_url") {
|
|
7267
|
+
const img = part.image_url;
|
|
7268
|
+
if (typeof img === "string" || img?.url?.startsWith("http")) {
|
|
7269
|
+
return {
|
|
7270
|
+
type: "image",
|
|
7271
|
+
source: { type: "url", url: typeof img === "string" ? img : img.url }
|
|
7272
|
+
};
|
|
7273
|
+
}
|
|
7274
|
+
if (img?.url?.startsWith("data:")) {
|
|
7275
|
+
const match = img.url.match(/^data:([^;]+);base64,(.+)$/);
|
|
7276
|
+
if (match) {
|
|
7277
|
+
return {
|
|
7278
|
+
type: "image",
|
|
7279
|
+
source: { type: "base64", mediaType: match[1], data: match[2] }
|
|
7280
|
+
};
|
|
7281
|
+
}
|
|
7282
|
+
}
|
|
7283
|
+
}
|
|
7284
|
+
return { type: "text", text: part.text ?? part.content ?? JSON.stringify(part) };
|
|
7285
|
+
});
|
|
7286
|
+
}
|
|
7287
|
+
function isZodSchema(obj) {
|
|
7288
|
+
return obj != null && typeof obj === "object" && typeof obj.safeParse === "function" && "_def" in obj;
|
|
7289
|
+
}
|
|
7290
|
+
function convertToJsonSchema(schema, strict = false) {
|
|
7291
|
+
if (!schema || typeof schema !== "object") {
|
|
7292
|
+
return { type: "object", properties: {}, required: [], additionalProperties: false };
|
|
7293
|
+
}
|
|
7294
|
+
if (isZodSchema(schema)) {
|
|
7295
|
+
const converted = zodToJsonSchema.zodToJsonSchema(schema, {
|
|
7296
|
+
$refStrategy: "none",
|
|
7297
|
+
...strict ? { openaiStrictMode: true } : {}
|
|
7298
|
+
});
|
|
7299
|
+
delete converted["$schema"];
|
|
7300
|
+
if (!converted.type) converted.type = "object";
|
|
7301
|
+
return converted;
|
|
7302
|
+
}
|
|
7303
|
+
if (!schema.type && schema.properties) {
|
|
7304
|
+
return { type: "object", ...schema };
|
|
7305
|
+
}
|
|
7306
|
+
return schema;
|
|
7307
|
+
}
|
|
7308
|
+
function normalizeStrictSchema(schema) {
|
|
7309
|
+
if (!schema || typeof schema !== "object") return schema;
|
|
7310
|
+
const clone2 = { ...schema };
|
|
7311
|
+
if (clone2.type === "object" || clone2.properties) {
|
|
7312
|
+
if (!clone2.type) clone2.type = "object";
|
|
7313
|
+
if (clone2.properties && typeof clone2.properties === "object") {
|
|
7314
|
+
const normalized = {};
|
|
7315
|
+
for (const [k, v] of Object.entries(clone2.properties)) {
|
|
7316
|
+
normalized[k] = normalizeStrictSchema(v);
|
|
7317
|
+
}
|
|
7318
|
+
clone2.properties = normalized;
|
|
7319
|
+
clone2.required = Object.keys(normalized);
|
|
7320
|
+
} else {
|
|
7321
|
+
clone2.properties = {};
|
|
7322
|
+
clone2.required = [];
|
|
7323
|
+
}
|
|
7324
|
+
clone2.additionalProperties = false;
|
|
7325
|
+
}
|
|
7326
|
+
if (clone2.items) clone2.items = normalizeStrictSchema(clone2.items);
|
|
7327
|
+
for (const key of ["anyOf", "oneOf", "allOf"]) {
|
|
7328
|
+
if (Array.isArray(clone2[key])) {
|
|
7329
|
+
clone2[key] = clone2[key].map((s) => normalizeStrictSchema(s));
|
|
7330
|
+
}
|
|
7331
|
+
}
|
|
7332
|
+
if (clone2.definitions && typeof clone2.definitions === "object") {
|
|
7333
|
+
const nd = {};
|
|
7334
|
+
for (const [k, v] of Object.entries(clone2.definitions)) {
|
|
7335
|
+
nd[k] = normalizeStrictSchema(v);
|
|
7336
|
+
}
|
|
7337
|
+
clone2.definitions = nd;
|
|
7338
|
+
}
|
|
7339
|
+
if (clone2.$defs && typeof clone2.$defs === "object") {
|
|
7340
|
+
const nd = {};
|
|
7341
|
+
for (const [k, v] of Object.entries(clone2.$defs)) {
|
|
7342
|
+
nd[k] = normalizeStrictSchema(v);
|
|
7343
|
+
}
|
|
7344
|
+
clone2.$defs = nd;
|
|
7345
|
+
}
|
|
7346
|
+
return clone2;
|
|
7347
|
+
}
|
|
7348
|
+
function toToolDefinition(tool, strict) {
|
|
7349
|
+
let name;
|
|
7350
|
+
let description;
|
|
7351
|
+
let parameters;
|
|
7352
|
+
if (tool.type === "function" && tool.function) {
|
|
7353
|
+
name = tool.function.name;
|
|
7354
|
+
description = tool.function.description ?? "";
|
|
7355
|
+
parameters = tool.function.parameters ?? { type: "object", properties: {}, additionalProperties: false };
|
|
7356
|
+
} else {
|
|
7357
|
+
name = tool.name ?? "unknown";
|
|
7358
|
+
description = tool.description ?? "";
|
|
7359
|
+
const rawSchema = tool.schema ?? tool.parameters;
|
|
7360
|
+
parameters = convertToJsonSchema(rawSchema, strict);
|
|
7361
|
+
}
|
|
7362
|
+
if (strict) {
|
|
7363
|
+
parameters = normalizeStrictSchema(parameters);
|
|
7364
|
+
}
|
|
7365
|
+
return { name, description, parameters, ...strict ? { strict: true } : {} };
|
|
7366
|
+
}
|
|
7367
|
+
function toBaseChatMessage(response) {
|
|
7368
|
+
const msg = {
|
|
7369
|
+
role: "assistant",
|
|
7370
|
+
content: response.content ?? "",
|
|
7371
|
+
usage: {
|
|
7372
|
+
prompt_tokens: response.usage.inputTokens,
|
|
7373
|
+
completion_tokens: response.usage.outputTokens,
|
|
7374
|
+
total_tokens: response.usage.totalTokens,
|
|
7375
|
+
prompt_tokens_details: {
|
|
7376
|
+
cached_tokens: response.usage.cachedInputTokens
|
|
7377
|
+
},
|
|
7378
|
+
completion_tokens_details: {
|
|
7379
|
+
reasoning_tokens: response.usage.reasoningTokens
|
|
7380
|
+
}
|
|
7381
|
+
},
|
|
7382
|
+
response_metadata: {
|
|
7383
|
+
token_usage: {
|
|
7384
|
+
prompt_tokens: response.usage.inputTokens,
|
|
7385
|
+
completion_tokens: response.usage.outputTokens,
|
|
7386
|
+
total_tokens: response.usage.totalTokens,
|
|
7387
|
+
cached_tokens: response.usage.cachedInputTokens,
|
|
7388
|
+
cache_write_tokens: response.usage.cachedWriteTokens
|
|
7389
|
+
},
|
|
7390
|
+
finish_reason: response.finishReason,
|
|
7391
|
+
model_name: response.model
|
|
7392
|
+
}
|
|
7393
|
+
};
|
|
7394
|
+
if (response.toolCalls.length > 0) {
|
|
7395
|
+
msg.tool_calls = response.toolCalls.map((tc) => ({
|
|
7396
|
+
id: tc.id,
|
|
7397
|
+
type: "function",
|
|
7398
|
+
function: { name: tc.name, arguments: tc.arguments }
|
|
7399
|
+
}));
|
|
7400
|
+
}
|
|
7401
|
+
return msg;
|
|
7402
|
+
}
|
|
7403
|
+
function assembleMessage(content, toolCalls, usage) {
|
|
7404
|
+
const msg = {
|
|
7405
|
+
role: "assistant",
|
|
7406
|
+
content
|
|
7407
|
+
};
|
|
7408
|
+
if (usage) {
|
|
7409
|
+
msg.usage = {
|
|
7410
|
+
prompt_tokens: usage.inputTokens,
|
|
7411
|
+
completion_tokens: usage.outputTokens,
|
|
7412
|
+
total_tokens: usage.totalTokens,
|
|
7413
|
+
prompt_tokens_details: {
|
|
7414
|
+
cached_tokens: usage.cachedInputTokens
|
|
7415
|
+
},
|
|
7416
|
+
completion_tokens_details: {
|
|
7417
|
+
reasoning_tokens: usage.reasoningTokens
|
|
7418
|
+
}
|
|
7419
|
+
};
|
|
7420
|
+
msg.response_metadata = {
|
|
7421
|
+
token_usage: {
|
|
7422
|
+
prompt_tokens: usage.inputTokens,
|
|
7423
|
+
completion_tokens: usage.outputTokens,
|
|
7424
|
+
total_tokens: usage.totalTokens,
|
|
7425
|
+
cached_tokens: usage.cachedInputTokens,
|
|
7426
|
+
cache_write_tokens: usage.cachedWriteTokens
|
|
7427
|
+
}
|
|
7428
|
+
};
|
|
7429
|
+
}
|
|
7430
|
+
if (toolCalls.length > 0) {
|
|
7431
|
+
msg.tool_calls = toolCalls.map((tc) => ({
|
|
7432
|
+
id: tc.id,
|
|
7433
|
+
type: "function",
|
|
7434
|
+
function: { name: tc.name, arguments: tc.arguments }
|
|
7435
|
+
}));
|
|
7436
|
+
}
|
|
7437
|
+
return msg;
|
|
7438
|
+
}
|
|
7439
|
+
function resolveCapabilities(provider) {
|
|
7440
|
+
switch (provider) {
|
|
7441
|
+
case "openai":
|
|
7442
|
+
case "azure":
|
|
7443
|
+
case "openai-compatible":
|
|
7444
|
+
return {
|
|
7445
|
+
structuredOutput: "native",
|
|
7446
|
+
strictToolCalling: true,
|
|
7447
|
+
streaming: true,
|
|
7448
|
+
provider
|
|
7449
|
+
};
|
|
7450
|
+
case "anthropic":
|
|
7451
|
+
return {
|
|
7452
|
+
structuredOutput: "tool_based",
|
|
7453
|
+
strictToolCalling: false,
|
|
7454
|
+
streaming: true,
|
|
7455
|
+
provider: "anthropic"
|
|
7456
|
+
};
|
|
7457
|
+
case "bedrock":
|
|
7458
|
+
return {
|
|
7459
|
+
structuredOutput: "tool_based",
|
|
7460
|
+
strictToolCalling: false,
|
|
7461
|
+
streaming: false,
|
|
7462
|
+
// streaming uses binary protocol
|
|
7463
|
+
provider: "bedrock"
|
|
7464
|
+
};
|
|
7465
|
+
case "vertex":
|
|
7466
|
+
return {
|
|
7467
|
+
structuredOutput: "native",
|
|
7468
|
+
strictToolCalling: false,
|
|
7469
|
+
streaming: true,
|
|
7470
|
+
provider: "google"
|
|
7471
|
+
};
|
|
7472
|
+
default:
|
|
7473
|
+
return { provider };
|
|
7474
|
+
}
|
|
7475
|
+
}
|
|
7476
|
+
|
|
7477
|
+
// src/providers/index.ts
|
|
7478
|
+
function createProvider(config) {
|
|
7479
|
+
switch (config.provider) {
|
|
7480
|
+
case "openai":
|
|
7481
|
+
return new OpenAIProvider(config);
|
|
7482
|
+
case "anthropic":
|
|
7483
|
+
return new AnthropicProvider(config);
|
|
7484
|
+
case "azure":
|
|
7485
|
+
return new AzureProvider(config);
|
|
7486
|
+
case "openai-compatible":
|
|
7487
|
+
return new OpenAICompatibleProvider(config);
|
|
7488
|
+
case "bedrock":
|
|
7489
|
+
return new BedrockProvider(config);
|
|
7490
|
+
case "vertex":
|
|
7491
|
+
return new VertexProvider(config);
|
|
7492
|
+
default:
|
|
7493
|
+
throw new Error(`Unknown provider: ${config.provider}`);
|
|
7494
|
+
}
|
|
7495
|
+
}
|
|
7496
|
+
|
|
7497
|
+
exports.AnthropicProvider = AnthropicProvider;
|
|
7498
|
+
exports.AzureProvider = AzureProvider;
|
|
7499
|
+
exports.BaseProvider = BaseProvider;
|
|
7500
|
+
exports.BedrockProvider = BedrockProvider;
|
|
5148
7501
|
exports.DEFAULT_PROFILE_CONFIGS = DEFAULT_PROFILE_CONFIGS;
|
|
5149
7502
|
exports.GuardrailPhase = GuardrailPhase;
|
|
5150
7503
|
exports.InMemoryMemoryStore = InMemoryMemoryStore;
|
|
7504
|
+
exports.NativeJsonSchemaStrategy = NativeJsonSchemaStrategy;
|
|
7505
|
+
exports.OpenAICompatibleProvider = OpenAICompatibleProvider;
|
|
7506
|
+
exports.OpenAIProvider = OpenAIProvider;
|
|
7507
|
+
exports.ProviderError = ProviderError;
|
|
7508
|
+
exports.StructuredOutputManager = StructuredOutputManager;
|
|
7509
|
+
exports.ToolBasedStrategy = ToolBasedStrategy;
|
|
7510
|
+
exports.VertexProvider = VertexProvider;
|
|
5151
7511
|
exports.agentVerdictRule = agentVerdictRule;
|
|
5152
7512
|
exports.applyTokenLimits = applyTokenLimits;
|
|
5153
7513
|
exports.buildSystemPrompt = buildSystemPrompt;
|
|
@@ -5163,6 +7523,7 @@ exports.createContextSummarizeNode = createContextSummarizeNode;
|
|
|
5163
7523
|
exports.createContextTools = createContextTools;
|
|
5164
7524
|
exports.createGuardrail = createGuardrail;
|
|
5165
7525
|
exports.createJsonGuardrail = createJsonGuardrail;
|
|
7526
|
+
exports.createProvider = createProvider;
|
|
5166
7527
|
exports.createRegexGuardrail = createRegexGuardrail;
|
|
5167
7528
|
exports.createResolverNode = createResolverNode;
|
|
5168
7529
|
exports.createSmartAgent = createSmartAgent;
|
|
@@ -5171,12 +7532,17 @@ exports.createToolLimitFinalizeNode = createToolLimitFinalizeNode;
|
|
|
5171
7532
|
exports.createToolsNode = createToolsNode;
|
|
5172
7533
|
exports.customCallbackRule = customCallbackRule;
|
|
5173
7534
|
exports.customSink = customSink;
|
|
7535
|
+
exports.emptyUsage = emptyUsage;
|
|
5174
7536
|
exports.evaluateGuardrails = evaluateGuardrails;
|
|
5175
7537
|
exports.fileSink = fileSink;
|
|
7538
|
+
exports.formatZodError = formatZodError;
|
|
5176
7539
|
exports.fromLangchainModel = fromLangchainModel;
|
|
5177
7540
|
exports.fromLangchainTools = fromLangchainTools;
|
|
7541
|
+
exports.fromNativeProvider = fromNativeProvider;
|
|
5178
7542
|
exports.generateSpanId = generateSpanId;
|
|
5179
7543
|
exports.generateTraceId = generateTraceId;
|
|
7544
|
+
exports.getModelCapabilities = getModelCapabilities;
|
|
7545
|
+
exports.getNestedValue = getNestedValue;
|
|
5180
7546
|
exports.getResolvedSmartConfig = getResolvedSmartConfig;
|
|
5181
7547
|
exports.httpSink = httpSink;
|
|
5182
7548
|
exports.isSmartModel = isSmartModel;
|
|
@@ -5187,6 +7553,7 @@ exports.readMemoryFacts = readMemoryFacts;
|
|
|
5187
7553
|
exports.regexRule = regexRule;
|
|
5188
7554
|
exports.renderMemoryFacts = renderMemoryFacts;
|
|
5189
7555
|
exports.resolveMemoryStore = resolveMemoryStore;
|
|
7556
|
+
exports.resolveStrategy = resolveStrategy;
|
|
5190
7557
|
exports.resolveToolApprovalState = resolveToolApprovalState;
|
|
5191
7558
|
exports.restoreSnapshot = restoreSnapshot;
|
|
5192
7559
|
exports.runSmartAgentEvalHarness = runSmartAgentEvalHarness;
|