@cognipeer/agent-sdk 0.4.9 → 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 +2517 -166
- 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 +2504 -171
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { z, ZodError } from 'zod';
|
|
2
2
|
import { createRequire } from 'module';
|
|
3
|
+
import { zodToJsonSchema } from 'zod-to-json-schema';
|
|
3
4
|
import fs from 'fs';
|
|
4
5
|
import path from 'path';
|
|
5
|
-
import { Buffer } from 'buffer';
|
|
6
|
+
import { Buffer as Buffer$1 } from 'buffer';
|
|
6
7
|
import { nanoid } from 'nanoid';
|
|
7
|
-
import crypto from 'crypto';
|
|
8
|
+
import crypto, { createHash, createHmac } from 'crypto';
|
|
8
9
|
import AjvModule from 'ajv';
|
|
9
10
|
|
|
10
11
|
// src/adapters/langchain.ts
|
|
@@ -124,12 +125,377 @@ function createSimpleTool({
|
|
|
124
125
|
};
|
|
125
126
|
}
|
|
126
127
|
|
|
128
|
+
// src/structuredOutput/types.ts
|
|
129
|
+
function getNestedValue(obj, path2) {
|
|
130
|
+
let current = obj;
|
|
131
|
+
for (const key of path2) {
|
|
132
|
+
if (current == null || typeof current !== "object") return void 0;
|
|
133
|
+
current = current[key];
|
|
134
|
+
}
|
|
135
|
+
return current;
|
|
136
|
+
}
|
|
137
|
+
function formatZodError(error, rawContent) {
|
|
138
|
+
const fieldErrors = error.issues.map((issue) => ({
|
|
139
|
+
path: issue.path.join(".") || "root",
|
|
140
|
+
expected: issue.expected ?? issue.code,
|
|
141
|
+
received: issue.received ?? "unknown",
|
|
142
|
+
message: issue.message
|
|
143
|
+
}));
|
|
144
|
+
return {
|
|
145
|
+
type: "validation_error",
|
|
146
|
+
message: `Schema validation failed: ${fieldErrors.map((f) => `${f.path}: ${f.message}`).join("; ")}`,
|
|
147
|
+
fieldErrors,
|
|
148
|
+
rawContent
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// src/structuredOutput/nativeStrategy.ts
|
|
153
|
+
var NativeJsonSchemaStrategy = class {
|
|
154
|
+
constructor() {
|
|
155
|
+
this.kind = "native";
|
|
156
|
+
}
|
|
157
|
+
resolveJsonPointer(document, pointer) {
|
|
158
|
+
if (!pointer.startsWith("#/")) {
|
|
159
|
+
return void 0;
|
|
160
|
+
}
|
|
161
|
+
const segments = pointer.slice(2).split("/").map((segment) => segment.replace(/~1/g, "/").replace(/~0/g, "~"));
|
|
162
|
+
let current = document;
|
|
163
|
+
for (const segment of segments) {
|
|
164
|
+
if (!current || typeof current !== "object" || !(segment in current)) {
|
|
165
|
+
return void 0;
|
|
166
|
+
}
|
|
167
|
+
current = current[segment];
|
|
168
|
+
}
|
|
169
|
+
return current;
|
|
170
|
+
}
|
|
171
|
+
unwrapRootObjectSchema(schema) {
|
|
172
|
+
if (schema.type === "object") {
|
|
173
|
+
return schema;
|
|
174
|
+
}
|
|
175
|
+
const ref = typeof schema.$ref === "string" ? schema.$ref : null;
|
|
176
|
+
if (!ref) {
|
|
177
|
+
return schema;
|
|
178
|
+
}
|
|
179
|
+
const resolved = this.resolveJsonPointer(schema, ref);
|
|
180
|
+
if (!resolved || typeof resolved !== "object" || Array.isArray(resolved)) {
|
|
181
|
+
return schema;
|
|
182
|
+
}
|
|
183
|
+
const resolvedSchema = { ...resolved };
|
|
184
|
+
if (resolvedSchema.type !== "object") {
|
|
185
|
+
return schema;
|
|
186
|
+
}
|
|
187
|
+
if (schema.definitions && resolvedSchema.definitions === void 0) {
|
|
188
|
+
resolvedSchema.definitions = schema.definitions;
|
|
189
|
+
}
|
|
190
|
+
if (schema.$defs && resolvedSchema.$defs === void 0) {
|
|
191
|
+
resolvedSchema.$defs = schema.$defs;
|
|
192
|
+
}
|
|
193
|
+
return resolvedSchema;
|
|
194
|
+
}
|
|
195
|
+
normalizeOpenAIStrictSchema(schema) {
|
|
196
|
+
const visit = (node) => {
|
|
197
|
+
if (!node || typeof node !== "object") return node;
|
|
198
|
+
if (Array.isArray(node)) {
|
|
199
|
+
return node.map(visit);
|
|
200
|
+
}
|
|
201
|
+
const clone2 = { ...node };
|
|
202
|
+
if (clone2.properties && typeof clone2.properties === "object") {
|
|
203
|
+
const normalizedProperties = {};
|
|
204
|
+
for (const [key, value] of Object.entries(clone2.properties)) {
|
|
205
|
+
normalizedProperties[key] = visit(value);
|
|
206
|
+
}
|
|
207
|
+
clone2.properties = normalizedProperties;
|
|
208
|
+
clone2.required = Object.keys(normalizedProperties);
|
|
209
|
+
if (clone2.additionalProperties === void 0) {
|
|
210
|
+
clone2.additionalProperties = false;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (clone2.items) {
|
|
214
|
+
clone2.items = visit(clone2.items);
|
|
215
|
+
}
|
|
216
|
+
if (clone2.definitions && typeof clone2.definitions === "object") {
|
|
217
|
+
const normalizedDefinitions = {};
|
|
218
|
+
for (const [key, value] of Object.entries(clone2.definitions)) {
|
|
219
|
+
normalizedDefinitions[key] = visit(value);
|
|
220
|
+
}
|
|
221
|
+
clone2.definitions = normalizedDefinitions;
|
|
222
|
+
}
|
|
223
|
+
if (clone2.$defs && typeof clone2.$defs === "object") {
|
|
224
|
+
const normalizedDefs = {};
|
|
225
|
+
for (const [key, value] of Object.entries(clone2.$defs)) {
|
|
226
|
+
normalizedDefs[key] = visit(value);
|
|
227
|
+
}
|
|
228
|
+
clone2.$defs = normalizedDefs;
|
|
229
|
+
}
|
|
230
|
+
for (const compositeKey of ["anyOf", "oneOf", "allOf"]) {
|
|
231
|
+
if (Array.isArray(clone2[compositeKey])) {
|
|
232
|
+
clone2[compositeKey] = clone2[compositeKey].map(visit);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return clone2;
|
|
236
|
+
};
|
|
237
|
+
return visit(schema);
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Convert a Zod schema to the JSON Schema object used in response_format.
|
|
241
|
+
*/
|
|
242
|
+
toJsonSchema(schema, name = "structured_response") {
|
|
243
|
+
const result = zodToJsonSchema(schema, {
|
|
244
|
+
name,
|
|
245
|
+
openaiStrictMode: true,
|
|
246
|
+
nameStrategy: "duplicate-ref",
|
|
247
|
+
$refStrategy: "extract-to-root",
|
|
248
|
+
nullableStrategy: "property"
|
|
249
|
+
});
|
|
250
|
+
const normalized = this.normalizeOpenAIStrictSchema(result);
|
|
251
|
+
return this.unwrapRootObjectSchema(normalized);
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Build response_format configuration for the model call.
|
|
255
|
+
* Returns the object that should be spread into model invocation options.
|
|
256
|
+
*/
|
|
257
|
+
buildResponseFormat(schema, name) {
|
|
258
|
+
const schemaName = name || "structured_response";
|
|
259
|
+
const jsonSchema = this.toJsonSchema(schema, schemaName);
|
|
260
|
+
delete jsonSchema["$schema"];
|
|
261
|
+
return {
|
|
262
|
+
response_format: {
|
|
263
|
+
type: "json_schema",
|
|
264
|
+
json_schema: {
|
|
265
|
+
name: schemaName,
|
|
266
|
+
strict: true,
|
|
267
|
+
schema: jsonSchema
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Parse and validate model response content.
|
|
274
|
+
* With native support, content is guaranteed JSON — but we still validate with Zod
|
|
275
|
+
* for type safety and field-level error reporting.
|
|
276
|
+
*/
|
|
277
|
+
extractOutput(content, schema) {
|
|
278
|
+
try {
|
|
279
|
+
const raw = JSON.parse(content);
|
|
280
|
+
const validated = schema.parse(raw);
|
|
281
|
+
return { data: validated };
|
|
282
|
+
} catch (e) {
|
|
283
|
+
if (e?.issues) {
|
|
284
|
+
return { error: formatZodError(e, content) };
|
|
285
|
+
}
|
|
286
|
+
return {
|
|
287
|
+
error: {
|
|
288
|
+
type: "parse_error",
|
|
289
|
+
message: `Failed to parse native structured output: ${e?.message}`,
|
|
290
|
+
rawContent: content
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
buildCorrectionPrompt(error, previousAttempt) {
|
|
296
|
+
const fieldIssues = error.issues.map((issue) => {
|
|
297
|
+
const pathStr = issue.path.join(".") || "root";
|
|
298
|
+
const received = getNestedValue(previousAttempt, issue.path);
|
|
299
|
+
return `- "${pathStr}": ${issue.message} (received: ${JSON.stringify(received)})`;
|
|
300
|
+
}).join("\n");
|
|
301
|
+
return [
|
|
302
|
+
"Your previous JSON response had validation errors:",
|
|
303
|
+
fieldIssues,
|
|
304
|
+
"",
|
|
305
|
+
"Please output corrected JSON matching the schema."
|
|
306
|
+
].join("\n");
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
// src/tool.ts
|
|
311
|
+
function createTool({
|
|
312
|
+
name,
|
|
313
|
+
description,
|
|
314
|
+
schema,
|
|
315
|
+
func,
|
|
316
|
+
needsApproval,
|
|
317
|
+
approvalPrompt,
|
|
318
|
+
approvalDefaults,
|
|
319
|
+
maxExecutionsPerRun
|
|
320
|
+
}) {
|
|
321
|
+
const execute = async (input) => func(input);
|
|
322
|
+
const toolRecord = {
|
|
323
|
+
name,
|
|
324
|
+
description,
|
|
325
|
+
schema,
|
|
326
|
+
// Our runtime prefers invoke but fallback helpers keep compatibility with LC-style tools.
|
|
327
|
+
invoke: execute,
|
|
328
|
+
call: execute,
|
|
329
|
+
run: execute,
|
|
330
|
+
func: execute
|
|
331
|
+
};
|
|
332
|
+
if (typeof needsApproval === "boolean") {
|
|
333
|
+
toolRecord.needsApproval = needsApproval;
|
|
334
|
+
}
|
|
335
|
+
if (approvalPrompt !== void 0) {
|
|
336
|
+
toolRecord.approvalPrompt = approvalPrompt;
|
|
337
|
+
}
|
|
338
|
+
if (approvalDefaults !== void 0) {
|
|
339
|
+
toolRecord.approvalDefaults = approvalDefaults;
|
|
340
|
+
}
|
|
341
|
+
if (maxExecutionsPerRun !== void 0) {
|
|
342
|
+
toolRecord.maxExecutionsPerRun = maxExecutionsPerRun;
|
|
343
|
+
}
|
|
344
|
+
toolRecord.__source = toolRecord.__source || "smart";
|
|
345
|
+
toolRecord.__impl = func;
|
|
346
|
+
return toolRecord;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// src/structuredOutput/toolStrategy.ts
|
|
350
|
+
var ToolBasedStrategy = class {
|
|
351
|
+
constructor() {
|
|
352
|
+
this.kind = "tool_based";
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Create the `response` tool for a given Zod schema.
|
|
356
|
+
* The tool validates input against the schema and returns a sentinel object
|
|
357
|
+
* that the tools node picks up to finalize the structured output.
|
|
358
|
+
*/
|
|
359
|
+
createResponseTool(schema) {
|
|
360
|
+
return createTool({
|
|
361
|
+
name: "response",
|
|
362
|
+
description: "Finalize the answer by returning the final structured JSON matching the required schema. Call exactly once when you are fully done, then stop.",
|
|
363
|
+
schema,
|
|
364
|
+
func: async (data) => {
|
|
365
|
+
try {
|
|
366
|
+
const validated = schema.parse ? schema.parse(data) : data;
|
|
367
|
+
return { __finalStructuredOutput: true, data: validated };
|
|
368
|
+
} catch (e) {
|
|
369
|
+
const zodError = e;
|
|
370
|
+
if (zodError?.issues) {
|
|
371
|
+
const soError = formatZodError(zodError);
|
|
372
|
+
return {
|
|
373
|
+
error: "Schema validation failed",
|
|
374
|
+
details: soError.message,
|
|
375
|
+
fieldErrors: soError.fieldErrors
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
return { error: "Schema validation failed", details: e?.message };
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Try to extract structured output from raw assistant text content.
|
|
385
|
+
* Used as a last resort when the model writes JSON directly instead of calling the tool.
|
|
386
|
+
*/
|
|
387
|
+
extractFromText(content, schema) {
|
|
388
|
+
let jsonText = null;
|
|
389
|
+
const fenced = content.match(/```(?:json)?\n([\s\S]*?)```/i);
|
|
390
|
+
if (fenced && fenced[1]) {
|
|
391
|
+
jsonText = fenced[1].trim();
|
|
392
|
+
} else {
|
|
393
|
+
const braceIdx = content.indexOf("{");
|
|
394
|
+
const bracketIdx = content.indexOf("[");
|
|
395
|
+
const candidates = [braceIdx, bracketIdx].filter((i) => i >= 0).sort((a, b) => a - b);
|
|
396
|
+
if (candidates.length > 0) {
|
|
397
|
+
jsonText = content.slice(candidates[0]).trim();
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
try {
|
|
401
|
+
const raw = JSON.parse(jsonText ?? content);
|
|
402
|
+
const validated = schema.parse(raw);
|
|
403
|
+
return { data: validated };
|
|
404
|
+
} catch (e) {
|
|
405
|
+
if (e?.issues) {
|
|
406
|
+
return { error: formatZodError(e, content) };
|
|
407
|
+
}
|
|
408
|
+
return {
|
|
409
|
+
error: {
|
|
410
|
+
type: "parse_error",
|
|
411
|
+
message: `Failed to parse JSON from assistant message: ${e?.message}`,
|
|
412
|
+
rawContent: content
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
buildCorrectionPrompt(error, previousAttempt) {
|
|
418
|
+
const fieldIssues = error.issues.map((issue) => {
|
|
419
|
+
const pathStr = issue.path.join(".") || "root";
|
|
420
|
+
const received = getNestedValue(previousAttempt, issue.path);
|
|
421
|
+
return `- "${pathStr}": ${issue.message} (received: ${JSON.stringify(received)})`;
|
|
422
|
+
}).join("\n");
|
|
423
|
+
return [
|
|
424
|
+
"Your previous structured output had validation errors:",
|
|
425
|
+
fieldIssues,
|
|
426
|
+
"",
|
|
427
|
+
"Please call `response` again with corrected values matching the schema."
|
|
428
|
+
].join("\n");
|
|
429
|
+
}
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
// src/structuredOutput/resolver.ts
|
|
433
|
+
function resolveStrategy(model) {
|
|
434
|
+
const capabilities = getModelCapabilities(model);
|
|
435
|
+
if (capabilities.structuredOutput === "native") {
|
|
436
|
+
return new NativeJsonSchemaStrategy();
|
|
437
|
+
}
|
|
438
|
+
return new ToolBasedStrategy();
|
|
439
|
+
}
|
|
440
|
+
function getModelCapabilities(model) {
|
|
441
|
+
if (model?.capabilities && typeof model.capabilities === "object") {
|
|
442
|
+
return model.capabilities;
|
|
443
|
+
}
|
|
444
|
+
const provider = detectProvider(model);
|
|
445
|
+
if (provider) {
|
|
446
|
+
return {
|
|
447
|
+
provider,
|
|
448
|
+
structuredOutput: supportsNativeStructuredOutput(provider) ? "native" : "tool_based",
|
|
449
|
+
strictToolCalling: requiresStrictToolCalling(provider),
|
|
450
|
+
streaming: true
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
return { structuredOutput: "tool_based" };
|
|
454
|
+
}
|
|
455
|
+
function detectProvider(model) {
|
|
456
|
+
if (!model || typeof model !== "object") return void 0;
|
|
457
|
+
if (typeof model.provider === "string") return model.provider;
|
|
458
|
+
const lcModel = model._lc;
|
|
459
|
+
if (lcModel) {
|
|
460
|
+
return detectProviderFromLCModel(lcModel);
|
|
461
|
+
}
|
|
462
|
+
const modelName = model.modelName || model.model || "";
|
|
463
|
+
if (modelName.startsWith("gpt-") || modelName.startsWith("o1") || modelName.startsWith("o3") || modelName.startsWith("o4")) return "openai";
|
|
464
|
+
if (modelName.startsWith("claude")) return "anthropic";
|
|
465
|
+
if (modelName.startsWith("gemini")) return "google";
|
|
466
|
+
if (modelName.startsWith("amazon.") || modelName.startsWith("anthropic.")) return "bedrock";
|
|
467
|
+
return void 0;
|
|
468
|
+
}
|
|
469
|
+
function detectProviderFromLCModel(lcModel) {
|
|
470
|
+
if (!lcModel) return void 0;
|
|
471
|
+
const className = lcModel.constructor?.name || "";
|
|
472
|
+
if (className.includes("ChatOpenAI") || className.includes("AzureChatOpenAI")) return "openai";
|
|
473
|
+
if (className.includes("ChatAnthropic")) return "anthropic";
|
|
474
|
+
if (className.includes("ChatGoogleGenerativeAI") || className.includes("ChatVertexAI")) return "google";
|
|
475
|
+
if (className.includes("ChatBedrock") || className.includes("BedrockChat")) return "bedrock";
|
|
476
|
+
if (className.includes("ChatGroq")) return "groq";
|
|
477
|
+
if (className.includes("ChatMistralAI")) return "mistral";
|
|
478
|
+
const llmType = typeof lcModel._llmType === "function" ? lcModel._llmType() : lcModel._llmType;
|
|
479
|
+
if (typeof llmType === "string") {
|
|
480
|
+
if (llmType.includes("openai")) return "openai";
|
|
481
|
+
if (llmType.includes("anthropic")) return "anthropic";
|
|
482
|
+
if (llmType.includes("google")) return "google";
|
|
483
|
+
}
|
|
484
|
+
return void 0;
|
|
485
|
+
}
|
|
486
|
+
function supportsNativeStructuredOutput(provider) {
|
|
487
|
+
return ["openai", "google", "groq", "mistral"].includes(provider);
|
|
488
|
+
}
|
|
489
|
+
function requiresStrictToolCalling(provider) {
|
|
490
|
+
return ["openai"].includes(provider);
|
|
491
|
+
}
|
|
492
|
+
|
|
127
493
|
// src/model.ts
|
|
128
494
|
function isSmartModel(m) {
|
|
129
495
|
return !!m && typeof m === "object" && typeof m.invoke === "function";
|
|
130
496
|
}
|
|
131
|
-
function withTools(model, tools) {
|
|
132
|
-
if (model?.bindTools) return model.bindTools(tools);
|
|
497
|
+
function withTools(model, tools, options) {
|
|
498
|
+
if (model?.bindTools) return model.bindTools(tools, options);
|
|
133
499
|
return model;
|
|
134
500
|
}
|
|
135
501
|
function fromLangchainModel(lcModel) {
|
|
@@ -174,7 +540,8 @@ function fromLangchainModel(lcModel) {
|
|
|
174
540
|
role: response.role || "assistant",
|
|
175
541
|
content,
|
|
176
542
|
tool_calls: response.tool_calls,
|
|
177
|
-
usage: response.usage,
|
|
543
|
+
usage: response.usage ?? response.usage_metadata ?? response.response_metadata?.token_usage ?? response.response_metadata?.tokenUsage,
|
|
544
|
+
usage_metadata: response.usage_metadata,
|
|
178
545
|
response_metadata: response.response_metadata,
|
|
179
546
|
...response
|
|
180
547
|
};
|
|
@@ -190,14 +557,14 @@ function fromLangchainModel(lcModel) {
|
|
|
190
557
|
yield chunk;
|
|
191
558
|
}
|
|
192
559
|
},
|
|
193
|
-
bindTools: (tools) => {
|
|
560
|
+
bindTools: (tools, options) => {
|
|
194
561
|
const lcReady = toLangchainTools(tools);
|
|
195
562
|
if (typeof lcModel.bindTools === "function") {
|
|
196
|
-
const bound = lcModel.bindTools(lcReady);
|
|
563
|
+
const bound = lcModel.bindTools(lcReady, options);
|
|
197
564
|
return fromLangchainModel(bound);
|
|
198
565
|
}
|
|
199
566
|
if (typeof lcModel.bind === "function") {
|
|
200
|
-
const bound = lcModel.bind({ tools: lcReady });
|
|
567
|
+
const bound = lcModel.bind({ tools: lcReady, ...options || {} });
|
|
201
568
|
return fromLangchainModel(bound);
|
|
202
569
|
}
|
|
203
570
|
return adapted;
|
|
@@ -205,48 +572,10 @@ function fromLangchainModel(lcModel) {
|
|
|
205
572
|
modelName: lcModel.modelName || lcModel._modelId || lcModel._llmType || lcModel.name,
|
|
206
573
|
_lc: lcModel
|
|
207
574
|
};
|
|
575
|
+
adapted.capabilities = getModelCapabilities(adapted);
|
|
208
576
|
return adapted;
|
|
209
577
|
}
|
|
210
578
|
|
|
211
|
-
// src/tool.ts
|
|
212
|
-
function createTool({
|
|
213
|
-
name,
|
|
214
|
-
description,
|
|
215
|
-
schema,
|
|
216
|
-
func,
|
|
217
|
-
needsApproval,
|
|
218
|
-
approvalPrompt,
|
|
219
|
-
approvalDefaults,
|
|
220
|
-
maxExecutionsPerRun
|
|
221
|
-
}) {
|
|
222
|
-
const execute = async (input) => func(input);
|
|
223
|
-
const toolRecord = {
|
|
224
|
-
name,
|
|
225
|
-
description,
|
|
226
|
-
schema,
|
|
227
|
-
// Our runtime prefers invoke but fallback helpers keep compatibility with LC-style tools.
|
|
228
|
-
invoke: execute,
|
|
229
|
-
call: execute,
|
|
230
|
-
run: execute,
|
|
231
|
-
func: execute
|
|
232
|
-
};
|
|
233
|
-
if (typeof needsApproval === "boolean") {
|
|
234
|
-
toolRecord.needsApproval = needsApproval;
|
|
235
|
-
}
|
|
236
|
-
if (approvalPrompt !== void 0) {
|
|
237
|
-
toolRecord.approvalPrompt = approvalPrompt;
|
|
238
|
-
}
|
|
239
|
-
if (approvalDefaults !== void 0) {
|
|
240
|
-
toolRecord.approvalDefaults = approvalDefaults;
|
|
241
|
-
}
|
|
242
|
-
if (maxExecutionsPerRun !== void 0) {
|
|
243
|
-
toolRecord.maxExecutionsPerRun = maxExecutionsPerRun;
|
|
244
|
-
}
|
|
245
|
-
toolRecord.__source = toolRecord.__source || "smart";
|
|
246
|
-
toolRecord.__impl = func;
|
|
247
|
-
return toolRecord;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
579
|
// src/prompts.ts
|
|
251
580
|
var DEFAULT_TODO_LIST_PROMPT = `Planning tools are available when they materially improve execution.
|
|
252
581
|
|
|
@@ -329,23 +658,39 @@ function normalizeUsage(raw) {
|
|
|
329
658
|
raw.totalTokens,
|
|
330
659
|
(typeof prompt === "number" ? prompt : 0) + (typeof completion === "number" ? completion : 0)
|
|
331
660
|
);
|
|
332
|
-
const promptDetailsSrc = raw.prompt_tokens_details || raw.promptTokensDetails || raw.input_token_details || raw.inputTokenDetails || {};
|
|
333
|
-
const completionDetailsSrc = raw.completion_tokens_details || raw.completionTokensDetails || raw.output_token_details || raw.outputTokenDetails || {};
|
|
661
|
+
const promptDetailsSrc = raw.prompt_tokens_details || raw.promptTokensDetails || raw.input_token_details || raw.inputTokenDetails || raw.input_tokens_details || raw.inputTokensDetails || {};
|
|
662
|
+
const completionDetailsSrc = raw.completion_tokens_details || raw.completionTokensDetails || raw.output_token_details || raw.outputTokenDetails || raw.output_tokens_details || raw.outputTokensDetails || {};
|
|
663
|
+
const cachedTokens = num(
|
|
664
|
+
promptDetailsSrc.cached_tokens,
|
|
665
|
+
promptDetailsSrc.cachedTokens,
|
|
666
|
+
promptDetailsSrc.cached,
|
|
667
|
+
promptDetailsSrc.cache_read,
|
|
668
|
+
promptDetailsSrc.cacheRead,
|
|
669
|
+
promptDetailsSrc.cache_read_tokens,
|
|
670
|
+
raw.cached_input_tokens,
|
|
671
|
+
raw.cachedInputTokens,
|
|
672
|
+
raw.cached_prompt_tokens,
|
|
673
|
+
raw.cachedPromptTokens,
|
|
674
|
+
raw.cache_read_input_tokens,
|
|
675
|
+
raw.cacheReadInputTokens
|
|
676
|
+
);
|
|
334
677
|
const normalized = {
|
|
335
678
|
prompt_tokens: safe(prompt),
|
|
336
679
|
completion_tokens: safe(completion),
|
|
337
680
|
total_tokens: safe(total || (prompt || 0) + (completion || 0)),
|
|
338
681
|
prompt_tokens_details: {
|
|
339
|
-
cached_tokens: safe(
|
|
340
|
-
|
|
341
|
-
),
|
|
342
|
-
audio_tokens: safe(promptDetailsSrc.audio_tokens || promptDetailsSrc.audio)
|
|
682
|
+
cached_tokens: safe(cachedTokens),
|
|
683
|
+
audio_tokens: safe(num(promptDetailsSrc.audio_tokens, promptDetailsSrc.audioTokens, promptDetailsSrc.audio))
|
|
343
684
|
},
|
|
344
685
|
completion_tokens_details: {
|
|
345
|
-
reasoning_tokens: safe(completionDetailsSrc.reasoning_tokens),
|
|
346
|
-
audio_tokens: safe(completionDetailsSrc.audio_tokens),
|
|
347
|
-
accepted_prediction_tokens: safe(
|
|
348
|
-
|
|
686
|
+
reasoning_tokens: safe(num(completionDetailsSrc.reasoning_tokens, completionDetailsSrc.reasoningTokens)),
|
|
687
|
+
audio_tokens: safe(num(completionDetailsSrc.audio_tokens, completionDetailsSrc.audioTokens)),
|
|
688
|
+
accepted_prediction_tokens: safe(
|
|
689
|
+
num(completionDetailsSrc.accepted_prediction_tokens, completionDetailsSrc.acceptedPredictionTokens)
|
|
690
|
+
),
|
|
691
|
+
rejected_prediction_tokens: safe(
|
|
692
|
+
num(completionDetailsSrc.rejected_prediction_tokens, completionDetailsSrc.rejectedPredictionTokens)
|
|
693
|
+
)
|
|
349
694
|
},
|
|
350
695
|
raw
|
|
351
696
|
};
|
|
@@ -455,21 +800,21 @@ var METADATA_PATHS = {
|
|
|
455
800
|
"lc_kwargs.provider"
|
|
456
801
|
]
|
|
457
802
|
};
|
|
458
|
-
function
|
|
803
|
+
function getNestedValue2(obj, path2) {
|
|
459
804
|
if (!obj || typeof path2 !== "string") return void 0;
|
|
460
805
|
return path2.split(".").reduce((current, key) => current?.[key], obj);
|
|
461
806
|
}
|
|
462
807
|
function extractMetadata(model, paths) {
|
|
463
808
|
if (!model) return void 0;
|
|
464
809
|
for (const path2 of paths) {
|
|
465
|
-
const value =
|
|
810
|
+
const value = getNestedValue2(model, path2);
|
|
466
811
|
const str = coerceToString(value);
|
|
467
812
|
if (str) return str;
|
|
468
813
|
}
|
|
469
814
|
const maybeLc = model?._lc;
|
|
470
815
|
if (maybeLc) {
|
|
471
816
|
for (const path2 of paths) {
|
|
472
|
-
const value =
|
|
817
|
+
const value = getNestedValue2(maybeLc, path2);
|
|
473
818
|
const str = coerceToString(value);
|
|
474
819
|
if (str) return str;
|
|
475
820
|
}
|
|
@@ -477,7 +822,7 @@ function extractMetadata(model, paths) {
|
|
|
477
822
|
const maybeClient = model?.client || model?.api || model?.service;
|
|
478
823
|
if (maybeClient) {
|
|
479
824
|
for (const path2 of paths) {
|
|
480
|
-
const value =
|
|
825
|
+
const value = getNestedValue2(maybeClient, path2);
|
|
481
826
|
const str = coerceToString(value);
|
|
482
827
|
if (str) return str;
|
|
483
828
|
}
|
|
@@ -509,7 +854,7 @@ function inferModelFromMessages(messageList) {
|
|
|
509
854
|
const message = messageList[i];
|
|
510
855
|
if (!message) continue;
|
|
511
856
|
for (const path2 of messagePaths) {
|
|
512
|
-
const value =
|
|
857
|
+
const value = getNestedValue2(message, path2);
|
|
513
858
|
const str = coerceToString(value);
|
|
514
859
|
if (str) return str;
|
|
515
860
|
}
|
|
@@ -528,7 +873,7 @@ function inferProviderFromMessages(messageList) {
|
|
|
528
873
|
const message = messageList[i];
|
|
529
874
|
if (!message) continue;
|
|
530
875
|
for (const path2 of messagePaths) {
|
|
531
|
-
const value =
|
|
876
|
+
const value = getNestedValue2(message, path2);
|
|
532
877
|
const str = coerceToString(value);
|
|
533
878
|
if (str) return str;
|
|
534
879
|
}
|
|
@@ -1514,10 +1859,10 @@ function sanitizeTracePayload(value) {
|
|
|
1514
1859
|
function estimatePayloadBytes(value) {
|
|
1515
1860
|
try {
|
|
1516
1861
|
const json = JSON.stringify(value);
|
|
1517
|
-
return Buffer.byteLength(json ?? "", "utf8");
|
|
1862
|
+
return Buffer$1.byteLength(json ?? "", "utf8");
|
|
1518
1863
|
} catch {
|
|
1519
1864
|
try {
|
|
1520
|
-
return Buffer.byteLength(String(value ?? ""), "utf8");
|
|
1865
|
+
return Buffer$1.byteLength(String(value ?? ""), "utf8");
|
|
1521
1866
|
} catch {
|
|
1522
1867
|
return 0;
|
|
1523
1868
|
}
|
|
@@ -1539,7 +1884,13 @@ function createAgentCoreNode(opts) {
|
|
|
1539
1884
|
tracing: opts.tracing
|
|
1540
1885
|
};
|
|
1541
1886
|
const tools = runtime.tools ?? [];
|
|
1542
|
-
const
|
|
1887
|
+
const shouldUseStrictToolCalling = Boolean(
|
|
1888
|
+
runtime.responseFormat && runtime.model?.capabilities?.strictToolCalling
|
|
1889
|
+
);
|
|
1890
|
+
const modelWithTools = runtime.model?.bindTools ? runtime.model.bindTools(
|
|
1891
|
+
tools,
|
|
1892
|
+
shouldUseStrictToolCalling ? { strict: true } : void 0
|
|
1893
|
+
) : runtime.model;
|
|
1543
1894
|
const traceSession = state.ctx?.__traceSession;
|
|
1544
1895
|
const actorName = runtime.name ?? opts.name ?? "agent";
|
|
1545
1896
|
const actorVersion = runtime.version ?? opts.version;
|
|
@@ -1616,6 +1967,7 @@ function createAgentCoreNode(opts) {
|
|
|
1616
1967
|
const streamingEnabled = Boolean(state.ctx?.__streaming);
|
|
1617
1968
|
const cancellationToken = state.ctx?.__cancellationToken;
|
|
1618
1969
|
const abortSignal = state.ctx?.__abortSignal;
|
|
1970
|
+
const responseFormat = runtime.responseFormat;
|
|
1619
1971
|
const extractText = (chunk) => {
|
|
1620
1972
|
if (chunk == null) return "";
|
|
1621
1973
|
if (typeof chunk === "string") return chunk;
|
|
@@ -1634,7 +1986,8 @@ function createAgentCoreNode(opts) {
|
|
|
1634
1986
|
if (streamingEnabled && typeof modelWithTools.stream === "function") {
|
|
1635
1987
|
let streamedText = "";
|
|
1636
1988
|
let streamedMessage;
|
|
1637
|
-
|
|
1989
|
+
const streamOptions = { signal: abortSignal, cancellationToken, ...responseFormat };
|
|
1990
|
+
for await (const chunk of modelWithTools.stream(normalizedMessages, streamOptions)) {
|
|
1638
1991
|
if (cancellationToken && cancellationToken.isCancellationRequested || abortSignal?.aborted) {
|
|
1639
1992
|
break;
|
|
1640
1993
|
}
|
|
@@ -1657,7 +2010,7 @@ function createAgentCoreNode(opts) {
|
|
|
1657
2010
|
response = { role: "assistant", content: streamedText };
|
|
1658
2011
|
}
|
|
1659
2012
|
} else {
|
|
1660
|
-
response = await modelWithTools.invoke(normalizedMessages, { signal: abortSignal, cancellationToken });
|
|
2013
|
+
response = await modelWithTools.invoke(normalizedMessages, { signal: abortSignal, cancellationToken, ...responseFormat });
|
|
1661
2014
|
}
|
|
1662
2015
|
} catch (err) {
|
|
1663
2016
|
const durationMs2 = Date.now() - start;
|
|
@@ -1679,7 +2032,7 @@ function createAgentCoreNode(opts) {
|
|
|
1679
2032
|
...state.messages,
|
|
1680
2033
|
response
|
|
1681
2034
|
];
|
|
1682
|
-
const rawUsage = response?.usage || response?.response_metadata?.token_usage || response?.response_metadata?.tokenUsage || response?.response_metadata?.usage;
|
|
2035
|
+
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;
|
|
1683
2036
|
const normalized = normalizeUsage(rawUsage);
|
|
1684
2037
|
const modelName = getModelName(runtime.model || opts.model) || "unknown_model";
|
|
1685
2038
|
const durationMs = Date.now() - start;
|
|
@@ -2204,6 +2557,16 @@ function countSuccessfulToolExecutions(toolHistory, toolName) {
|
|
|
2204
2557
|
if (!Array.isArray(toolHistory)) return 0;
|
|
2205
2558
|
return toolHistory.filter((entry) => entry?.toolName === toolName && (entry?.status === "success" || entry?.status === "handoff")).length;
|
|
2206
2559
|
}
|
|
2560
|
+
function normalizeToolCall(call) {
|
|
2561
|
+
if (!call || typeof call !== "object") return null;
|
|
2562
|
+
const name = call.name ?? call.tool ?? call.function?.name ?? call.function_call?.name;
|
|
2563
|
+
if (typeof name !== "string" || name.length === 0) return null;
|
|
2564
|
+
return {
|
|
2565
|
+
id: call.id,
|
|
2566
|
+
name,
|
|
2567
|
+
args: call.args ?? call.arguments ?? call.input ?? call.function?.arguments ?? call.function_call?.arguments
|
|
2568
|
+
};
|
|
2569
|
+
}
|
|
2207
2570
|
function createToolsNode(initialTools, opts) {
|
|
2208
2571
|
return async (state) => {
|
|
2209
2572
|
const runtime = state.agent || {
|
|
@@ -2238,12 +2601,14 @@ function createToolsNode(initialTools, opts) {
|
|
|
2238
2601
|
if (anyTool._stateRef && typeof anyTool._stateRef === "object") {
|
|
2239
2602
|
anyTool._stateRef.toolHistory = state.toolHistory;
|
|
2240
2603
|
anyTool._stateRef.toolHistoryArchived = state.toolHistoryArchived;
|
|
2604
|
+
anyTool._stateRef.pendingApprovals = pendingApprovals;
|
|
2605
|
+
anyTool._stateRef.ctx = state.ctx || (state.ctx = {});
|
|
2241
2606
|
anyTool._stateRef.__onEvent = onEvent;
|
|
2242
2607
|
}
|
|
2243
2608
|
}
|
|
2244
2609
|
const last = state.messages[state.messages.length - 1];
|
|
2245
2610
|
let toolCount = state.toolCallCount || 0;
|
|
2246
|
-
const toolCalls = Array.isArray(last?.tool_calls) ? last.tool_calls : [];
|
|
2611
|
+
const toolCalls = Array.isArray(last?.tool_calls) ? last.tool_calls.map((toolCall) => normalizeToolCall(toolCall)).filter((toolCall) => toolCall !== null) : [];
|
|
2247
2612
|
const toolHistory = state.toolHistory || [];
|
|
2248
2613
|
const toolHistoryArchived = state.toolHistoryArchived || [];
|
|
2249
2614
|
const remaining = Math.max(0, limits.maxToolCalls - toolCount);
|
|
@@ -2302,6 +2667,13 @@ function createToolsNode(initialTools, opts) {
|
|
|
2302
2667
|
args = validatedArgs.value;
|
|
2303
2668
|
}
|
|
2304
2669
|
const toolName = tool.name || tc.name;
|
|
2670
|
+
const toolStateRef = tool._stateRef && typeof tool._stateRef === "object" ? tool._stateRef : null;
|
|
2671
|
+
if (toolStateRef) {
|
|
2672
|
+
toolStateRef.pendingApprovals = pendingApprovals;
|
|
2673
|
+
toolStateRef.ctx = state.ctx || (state.ctx = {});
|
|
2674
|
+
toolStateRef.__currentToolCallId = toolCallId;
|
|
2675
|
+
delete toolStateRef.__awaitingApproval;
|
|
2676
|
+
}
|
|
2305
2677
|
const maxExecutionsPerRun = normalizeMaxExecutionsPerRun(tool.maxExecutionsPerRun);
|
|
2306
2678
|
if (maxExecutionsPerRun !== null) {
|
|
2307
2679
|
const currentExecutions = countSuccessfulToolExecutions(toolHistory, toolName);
|
|
@@ -2414,6 +2786,22 @@ function createToolsNode(initialTools, opts) {
|
|
|
2414
2786
|
toolCount += 1;
|
|
2415
2787
|
return { status: "success", approval: approvalEntry };
|
|
2416
2788
|
}
|
|
2789
|
+
if (output && typeof output === "object" && output.__awaitingToolApproval) {
|
|
2790
|
+
if (toolStateRef?.ctx && typeof toolStateRef.ctx === "object") {
|
|
2791
|
+
state.ctx = toolStateRef.ctx;
|
|
2792
|
+
}
|
|
2793
|
+
const pendingApproval = pendingApprovals.find(
|
|
2794
|
+
(entry) => entry.toolCallId === output.toolCallId || entry.id === output.approvalId
|
|
2795
|
+
);
|
|
2796
|
+
if (!pendingApproval) {
|
|
2797
|
+
throw new Error(`Tool ${toolName} requested approval but no pending approval entry was recorded.`);
|
|
2798
|
+
}
|
|
2799
|
+
awaitingApproval = true;
|
|
2800
|
+
return {
|
|
2801
|
+
status: "awaiting_approval",
|
|
2802
|
+
approval: pendingApproval
|
|
2803
|
+
};
|
|
2804
|
+
}
|
|
2417
2805
|
if (output && typeof output === "object" && output.__finalStructuredOutput) {
|
|
2418
2806
|
if (!state.ctx) state.ctx = {};
|
|
2419
2807
|
state.ctx.__structuredOutputParsed = output.data;
|
|
@@ -2836,6 +3224,153 @@ function resolveToolApprovalState(state, resolution) {
|
|
|
2836
3224
|
};
|
|
2837
3225
|
}
|
|
2838
3226
|
|
|
3227
|
+
// src/structuredOutput/manager.ts
|
|
3228
|
+
var StructuredOutputManager = class {
|
|
3229
|
+
constructor(schema, strategy, config) {
|
|
3230
|
+
this.schema = schema;
|
|
3231
|
+
this.strategy = strategy;
|
|
3232
|
+
this.maxRetries = config?.maxRetries ?? 2;
|
|
3233
|
+
}
|
|
3234
|
+
// --- Setup Phase ---
|
|
3235
|
+
/**
|
|
3236
|
+
* Get the `response` tool for tool-based strategy.
|
|
3237
|
+
* Returns undefined for native strategy (no tool needed).
|
|
3238
|
+
*/
|
|
3239
|
+
getResponseTool() {
|
|
3240
|
+
if (this.strategy.kind !== "tool_based") return void 0;
|
|
3241
|
+
return this.strategy.createResponseTool(this.schema);
|
|
3242
|
+
}
|
|
3243
|
+
/**
|
|
3244
|
+
* Get response_format config for native strategy.
|
|
3245
|
+
* Returns undefined for tool-based strategy.
|
|
3246
|
+
*/
|
|
3247
|
+
getResponseFormat() {
|
|
3248
|
+
if (this.strategy.kind !== "native") return void 0;
|
|
3249
|
+
return this.strategy.buildResponseFormat(this.schema);
|
|
3250
|
+
}
|
|
3251
|
+
/**
|
|
3252
|
+
* Build the system prompt hint that tells the model how to produce structured output.
|
|
3253
|
+
*/
|
|
3254
|
+
buildSystemPromptHint() {
|
|
3255
|
+
if (this.strategy.kind === "native") {
|
|
3256
|
+
return [
|
|
3257
|
+
"A structured output schema is active.",
|
|
3258
|
+
"Your response MUST be valid JSON matching the output schema.",
|
|
3259
|
+
"Do NOT wrap the JSON in code fences or add any text before/after it."
|
|
3260
|
+
].join("\n");
|
|
3261
|
+
}
|
|
3262
|
+
return [
|
|
3263
|
+
"A structured output schema is active.",
|
|
3264
|
+
"Do NOT output the final JSON directly as an assistant message.",
|
|
3265
|
+
"When completely finished, call tool `response` passing the final JSON matching the schema as its arguments (direct object).",
|
|
3266
|
+
"Call it exactly once then STOP producing further assistant messages."
|
|
3267
|
+
].join("\n");
|
|
3268
|
+
}
|
|
3269
|
+
/**
|
|
3270
|
+
* Build a nudge message for when the model didn't produce structured output.
|
|
3271
|
+
* Used by the agent loop when it needs to retry.
|
|
3272
|
+
*/
|
|
3273
|
+
buildNudgeMessage(isLastAttempt) {
|
|
3274
|
+
if (this.strategy.kind === "native") {
|
|
3275
|
+
return {
|
|
3276
|
+
role: "system",
|
|
3277
|
+
content: [
|
|
3278
|
+
"A structured output schema is active.",
|
|
3279
|
+
"You MUST respond with valid JSON matching the schema.",
|
|
3280
|
+
"Do NOT include any text outside the JSON object.",
|
|
3281
|
+
isLastAttempt ? "This is your FINAL attempt. Produce the JSON NOW." : ""
|
|
3282
|
+
].filter(Boolean).join("\n")
|
|
3283
|
+
};
|
|
3284
|
+
}
|
|
3285
|
+
return {
|
|
3286
|
+
role: "system",
|
|
3287
|
+
content: [
|
|
3288
|
+
"A structured output schema is active.",
|
|
3289
|
+
"You MUST now call tool `response` with the final JSON object that matches the schema.",
|
|
3290
|
+
"Do not write the JSON in the assistant message.",
|
|
3291
|
+
"Call `response` exactly once, then stop.",
|
|
3292
|
+
isLastAttempt ? "Do NOT call any other tools. Produce the response NOW based on whatever information you have gathered so far." : ""
|
|
3293
|
+
].filter(Boolean).join("\n")
|
|
3294
|
+
};
|
|
3295
|
+
}
|
|
3296
|
+
/**
|
|
3297
|
+
* Build a correction message when schema validation failed.
|
|
3298
|
+
*/
|
|
3299
|
+
buildCorrectionMessage(error, _previousAttempt) {
|
|
3300
|
+
if (error.fieldErrors && error.fieldErrors.length > 0) {
|
|
3301
|
+
const fieldIssues = error.fieldErrors.map((f) => `- "${f.path}": ${f.message} (received: ${JSON.stringify(f.received)})`).join("\n");
|
|
3302
|
+
const action = this.strategy.kind === "native" ? "Please output corrected JSON matching the schema." : "Please call `response` again with corrected values matching the schema.";
|
|
3303
|
+
return {
|
|
3304
|
+
role: "system",
|
|
3305
|
+
content: [
|
|
3306
|
+
"Your previous structured output had validation errors:",
|
|
3307
|
+
fieldIssues,
|
|
3308
|
+
"",
|
|
3309
|
+
action
|
|
3310
|
+
].join("\n")
|
|
3311
|
+
};
|
|
3312
|
+
}
|
|
3313
|
+
return this.buildNudgeMessage(false);
|
|
3314
|
+
}
|
|
3315
|
+
// --- Parse Phase ---
|
|
3316
|
+
/**
|
|
3317
|
+
* Try to parse structured output from the tool-based finalization result.
|
|
3318
|
+
* Called when __structuredOutputParsed is available in ctx.
|
|
3319
|
+
*/
|
|
3320
|
+
parseFromToolResult(data) {
|
|
3321
|
+
return { success: true, data, attempts: 1 };
|
|
3322
|
+
}
|
|
3323
|
+
/**
|
|
3324
|
+
* Try to parse structured output from assistant message content.
|
|
3325
|
+
* For native strategy: content should be valid JSON.
|
|
3326
|
+
* For tool-based strategy: last-resort fallback extraction from text.
|
|
3327
|
+
*/
|
|
3328
|
+
parseFromContent(content) {
|
|
3329
|
+
if (this.strategy.kind === "native") {
|
|
3330
|
+
const result2 = this.strategy.extractOutput(content, this.schema);
|
|
3331
|
+
if ("data" in result2) {
|
|
3332
|
+
return { success: true, data: result2.data, attempts: 1 };
|
|
3333
|
+
}
|
|
3334
|
+
return { success: false, error: result2.error, attempts: 1, rawContent: content };
|
|
3335
|
+
}
|
|
3336
|
+
const result = this.strategy.extractFromText(content, this.schema);
|
|
3337
|
+
if ("data" in result) {
|
|
3338
|
+
return { success: true, data: result.data, attempts: 1 };
|
|
3339
|
+
}
|
|
3340
|
+
return { success: false, error: result.error, attempts: 1, rawContent: content };
|
|
3341
|
+
}
|
|
3342
|
+
/**
|
|
3343
|
+
* Create a "no output" error result.
|
|
3344
|
+
*/
|
|
3345
|
+
noOutputResult(attempts, rawContent) {
|
|
3346
|
+
return {
|
|
3347
|
+
success: false,
|
|
3348
|
+
error: {
|
|
3349
|
+
type: "no_output",
|
|
3350
|
+
message: "Model did not produce structured output after all retry attempts.",
|
|
3351
|
+
rawContent
|
|
3352
|
+
},
|
|
3353
|
+
attempts,
|
|
3354
|
+
rawContent
|
|
3355
|
+
};
|
|
3356
|
+
}
|
|
3357
|
+
/**
|
|
3358
|
+
* Create a "max retries" error result.
|
|
3359
|
+
*/
|
|
3360
|
+
maxRetriesResult(lastError, attempts, rawContent) {
|
|
3361
|
+
return {
|
|
3362
|
+
success: false,
|
|
3363
|
+
error: {
|
|
3364
|
+
...lastError,
|
|
3365
|
+
type: "max_retries",
|
|
3366
|
+
message: `Structured output failed after ${attempts} attempts: ${lastError.message}`
|
|
3367
|
+
},
|
|
3368
|
+
attempts,
|
|
3369
|
+
rawContent
|
|
3370
|
+
};
|
|
3371
|
+
}
|
|
3372
|
+
};
|
|
3373
|
+
|
|
2839
3374
|
// src/agent.ts
|
|
2840
3375
|
function isSyntheticSummaryMessage(message) {
|
|
2841
3376
|
if (!message) return false;
|
|
@@ -2887,14 +3422,13 @@ function createAgent(opts) {
|
|
|
2887
3422
|
const resolver = createResolverNode();
|
|
2888
3423
|
const agentCore = createAgentCoreNode(opts);
|
|
2889
3424
|
const toolsBase = [...opts.tools ?? []];
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
}
|
|
2897
|
-
toolsBase.push(responseTool);
|
|
3425
|
+
const soManager = opts.outputSchema ? new StructuredOutputManager(opts.outputSchema, resolveStrategy(opts.model)) : void 0;
|
|
3426
|
+
if (soManager) {
|
|
3427
|
+
const modelCapabilities = getModelCapabilities(opts.model);
|
|
3428
|
+
const responseTool = soManager.getResponseTool();
|
|
3429
|
+
if (responseTool && modelCapabilities.structuredOutput !== "native") {
|
|
3430
|
+
toolsBase.push(responseTool);
|
|
3431
|
+
}
|
|
2898
3432
|
}
|
|
2899
3433
|
const toolsNode = createToolsNode(toolsBase, opts);
|
|
2900
3434
|
const finalizeNode = createToolLimitFinalizeNode();
|
|
@@ -2929,6 +3463,7 @@ function createAgent(opts) {
|
|
|
2929
3463
|
limits: opts.limits,
|
|
2930
3464
|
useTodoList: void 0,
|
|
2931
3465
|
outputSchema: opts.outputSchema,
|
|
3466
|
+
responseFormat: soManager?.getResponseFormat(),
|
|
2932
3467
|
tracing: opts.tracing
|
|
2933
3468
|
};
|
|
2934
3469
|
const summarizationThreshold = getActiveSummarizationThreshold(opts);
|
|
@@ -3067,10 +3602,11 @@ function createAgent(opts) {
|
|
|
3067
3602
|
});
|
|
3068
3603
|
const tokenCount = countMessagesTokens(tokCountMessages);
|
|
3069
3604
|
if (tokenCount > summarizationThreshold) {
|
|
3605
|
+
const summarizationExhausted = !!state.ctx?.__summarizationExhausted;
|
|
3070
3606
|
const hasFreshSummary = (state.messages || []).some(
|
|
3071
3607
|
(m) => m.role === "tool" && typeof m.content === "string" && (m.content === "SUMMARIZED" || m.content.startsWith("SUMMARIZED_TOOL_RESPONSE"))
|
|
3072
3608
|
);
|
|
3073
|
-
if (hasFreshSummary && tokenCount <= summarizationThreshold * 1.15) ; else {
|
|
3609
|
+
if (hasFreshSummary && tokenCount <= summarizationThreshold * 1.15) ; else if (summarizationExhausted) ; else {
|
|
3074
3610
|
const ctx = { ...state.ctx || {}, __needsSummarization: true };
|
|
3075
3611
|
state = { ...state, ctx };
|
|
3076
3612
|
break;
|
|
@@ -3148,18 +3684,10 @@ function createAgent(opts) {
|
|
|
3148
3684
|
continue;
|
|
3149
3685
|
}
|
|
3150
3686
|
if (toolCalls.length === 0) {
|
|
3151
|
-
if (
|
|
3152
|
-
const
|
|
3153
|
-
role: "system",
|
|
3154
|
-
content: [
|
|
3155
|
-
"A structured output schema is active.",
|
|
3156
|
-
"You MUST now call tool `response` with the final JSON object that matches the schema.",
|
|
3157
|
-
"Do not write the JSON in the assistant message.",
|
|
3158
|
-
"Call `response` exactly once, then stop."
|
|
3159
|
-
].join("\n")
|
|
3160
|
-
};
|
|
3687
|
+
if (soManager && !state.ctx?.__finalizedDueToStructuredOutput && !state.ctx?.__structuredOutputForceFinalize) {
|
|
3688
|
+
const nudge = soManager.buildNudgeMessage(false);
|
|
3161
3689
|
const ctx = { ...state.ctx || {}, __structuredOutputForceFinalize: true };
|
|
3162
|
-
state = { ...state, messages: [...state.messages,
|
|
3690
|
+
state = { ...state, messages: [...state.messages, nudge], ctx };
|
|
3163
3691
|
continue;
|
|
3164
3692
|
}
|
|
3165
3693
|
break;
|
|
@@ -3172,23 +3700,16 @@ function createAgent(opts) {
|
|
|
3172
3700
|
if (checkpointIfRequested("after_tools")) break;
|
|
3173
3701
|
if (state.ctx?.__finalizedDueToStructuredOutput) break;
|
|
3174
3702
|
}
|
|
3175
|
-
if (
|
|
3176
|
-
const
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
const
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
"You MUST now call tool `response` with the final JSON object that matches the schema.",
|
|
3186
|
-
"Do not write the JSON in the assistant message.",
|
|
3187
|
-
"Call `response` exactly once, then stop.",
|
|
3188
|
-
"Do NOT call any other tools. Produce the response NOW based on whatever information you have gathered so far."
|
|
3189
|
-
].join("\n")
|
|
3190
|
-
};
|
|
3191
|
-
state = { ...state, messages: [...state.messages, finalNudge] };
|
|
3703
|
+
if (soManager && !state.ctx?.__finalizedDueToStructuredOutput) {
|
|
3704
|
+
const maxPostLoopRetries = soManager.maxRetries;
|
|
3705
|
+
for (let postRetry = 0; postRetry < maxPostLoopRetries; postRetry++) {
|
|
3706
|
+
if (state.ctx?.__finalizedDueToStructuredOutput) break;
|
|
3707
|
+
const last = state.messages[state.messages.length - 1];
|
|
3708
|
+
const lastHasToolCalls = Array.isArray(last?.tool_calls) && last.tool_calls.length > 0;
|
|
3709
|
+
if (!lastHasToolCalls) {
|
|
3710
|
+
const isLastAttempt = postRetry === maxPostLoopRetries - 1;
|
|
3711
|
+
const nudge = soManager.buildNudgeMessage(isLastAttempt);
|
|
3712
|
+
state = { ...state, messages: [...state.messages, nudge] };
|
|
3192
3713
|
}
|
|
3193
3714
|
try {
|
|
3194
3715
|
state = { ...state, ...await agentCore(state) };
|
|
@@ -3290,23 +3811,22 @@ function createAgent(opts) {
|
|
|
3290
3811
|
const finalAssistantMsg = getLastAssistantMessage(res.messages);
|
|
3291
3812
|
const content = getMessageText(finalAssistantMsg);
|
|
3292
3813
|
let parsed = void 0;
|
|
3293
|
-
|
|
3294
|
-
if (
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
} catch {
|
|
3814
|
+
let outputError = void 0;
|
|
3815
|
+
if (soManager) {
|
|
3816
|
+
if (res.ctx?.__structuredOutputParsed) {
|
|
3817
|
+
parsed = res.ctx.__structuredOutputParsed;
|
|
3818
|
+
} else if (content) {
|
|
3819
|
+
const fallbackResult = soManager.parseFromContent(content);
|
|
3820
|
+
if (fallbackResult.success) {
|
|
3821
|
+
parsed = fallbackResult.data;
|
|
3822
|
+
} else {
|
|
3823
|
+
outputError = fallbackResult.error;
|
|
3824
|
+
}
|
|
3825
|
+
} else {
|
|
3826
|
+
const noResult = soManager.noOutputResult(1);
|
|
3827
|
+
if (!noResult.success) {
|
|
3828
|
+
outputError = noResult.error;
|
|
3829
|
+
}
|
|
3310
3830
|
}
|
|
3311
3831
|
}
|
|
3312
3832
|
emit({ type: "finalAnswer", content });
|
|
@@ -3317,6 +3837,7 @@ function createAgent(opts) {
|
|
|
3317
3837
|
return {
|
|
3318
3838
|
content,
|
|
3319
3839
|
output: parsed,
|
|
3840
|
+
outputError,
|
|
3320
3841
|
metadata: { usage: res.usage },
|
|
3321
3842
|
messages: res.messages,
|
|
3322
3843
|
state: res
|
|
@@ -3440,7 +3961,7 @@ function createAgentNode(opts) {
|
|
|
3440
3961
|
...messages,
|
|
3441
3962
|
response
|
|
3442
3963
|
];
|
|
3443
|
-
const rawUsage = response?.usage || response?.response_metadata?.token_usage || response?.response_metadata?.usage;
|
|
3964
|
+
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;
|
|
3444
3965
|
const normalized = normalizeUsage(rawUsage);
|
|
3445
3966
|
const modelName = getModelName(runtime.model || opts.model) || "unknown_model";
|
|
3446
3967
|
const providerName = getProviderName(runtime.model || opts.model);
|
|
@@ -3499,14 +4020,23 @@ function collectRecentTurns(messages, lastTurnsToKeep) {
|
|
|
3499
4020
|
if (lastTurnsToKeep <= 0) return [];
|
|
3500
4021
|
const systemPrefix = messages.filter((message, index) => index === 0 && message.role === "system");
|
|
3501
4022
|
const body = systemPrefix.length > 0 ? messages.slice(1) : [...messages];
|
|
3502
|
-
|
|
4023
|
+
const totalUserTurns = body.filter((m) => m.role === "user").length;
|
|
4024
|
+
const countAssistantTurns = totalUserTurns < lastTurnsToKeep;
|
|
4025
|
+
let turnCount = 0;
|
|
3503
4026
|
const collected = [];
|
|
3504
4027
|
for (let index = body.length - 1; index >= 0; index -= 1) {
|
|
3505
4028
|
const message = body[index];
|
|
3506
4029
|
collected.unshift(message);
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
4030
|
+
const isTurnBoundary = countAssistantTurns ? message.role === "assistant" : message.role === "user";
|
|
4031
|
+
if (isTurnBoundary) {
|
|
4032
|
+
turnCount += 1;
|
|
4033
|
+
if (turnCount >= lastTurnsToKeep) break;
|
|
4034
|
+
}
|
|
4035
|
+
}
|
|
4036
|
+
if (countAssistantTurns && collected.length > 0 && collected[0].role !== "user") {
|
|
4037
|
+
const firstUserMsg = body.find((m) => m.role === "user");
|
|
4038
|
+
if (firstUserMsg && !collected.includes(firstUserMsg)) {
|
|
4039
|
+
collected.unshift(firstUserMsg);
|
|
3510
4040
|
}
|
|
3511
4041
|
}
|
|
3512
4042
|
return [...systemPrefix, ...collected];
|
|
@@ -3546,7 +4076,44 @@ function clampToBudget(messages, maxContextTokens) {
|
|
|
3546
4076
|
if (tokenCount <= maxContextTokens) return working;
|
|
3547
4077
|
const firstNonSystem = working.findIndex((message, index) => !(index === 0 && message.role === "system"));
|
|
3548
4078
|
if (firstNonSystem < 0) break;
|
|
3549
|
-
working
|
|
4079
|
+
const target = working[firstNonSystem];
|
|
4080
|
+
if (target.role === "assistant" && Array.isArray(target.tool_calls) && target.tool_calls.length > 0) {
|
|
4081
|
+
const toolCallIds = new Set(
|
|
4082
|
+
target.tool_calls.map((tc) => tc.id).filter(Boolean)
|
|
4083
|
+
);
|
|
4084
|
+
working = working.filter((m, idx) => {
|
|
4085
|
+
if (idx === firstNonSystem) return false;
|
|
4086
|
+
if (m.role === "tool" && m.tool_call_id && toolCallIds.has(m.tool_call_id)) return false;
|
|
4087
|
+
return true;
|
|
4088
|
+
});
|
|
4089
|
+
} else if (target.role === "tool") {
|
|
4090
|
+
const toolCallId = target.tool_call_id;
|
|
4091
|
+
let parentIdx = -1;
|
|
4092
|
+
if (toolCallId) {
|
|
4093
|
+
for (let i = firstNonSystem - 1; i >= 0; i--) {
|
|
4094
|
+
const m = working[i];
|
|
4095
|
+
if (m.role === "assistant" && Array.isArray(m.tool_calls) && m.tool_calls.some((tc) => tc.id === toolCallId)) {
|
|
4096
|
+
parentIdx = i;
|
|
4097
|
+
break;
|
|
4098
|
+
}
|
|
4099
|
+
}
|
|
4100
|
+
}
|
|
4101
|
+
if (parentIdx >= 0) {
|
|
4102
|
+
const parent = working[parentIdx];
|
|
4103
|
+
const parentToolCallIds = new Set(
|
|
4104
|
+
(parent.tool_calls || []).map((tc) => tc.id).filter(Boolean)
|
|
4105
|
+
);
|
|
4106
|
+
working = working.filter((m, idx) => {
|
|
4107
|
+
if (idx === parentIdx) return false;
|
|
4108
|
+
if (m.role === "tool" && m.tool_call_id && parentToolCallIds.has(m.tool_call_id)) return false;
|
|
4109
|
+
return true;
|
|
4110
|
+
});
|
|
4111
|
+
} else {
|
|
4112
|
+
working.splice(firstNonSystem, 1);
|
|
4113
|
+
}
|
|
4114
|
+
} else {
|
|
4115
|
+
working.splice(firstNonSystem, 1);
|
|
4116
|
+
}
|
|
3550
4117
|
}
|
|
3551
4118
|
return working;
|
|
3552
4119
|
}
|
|
@@ -4196,6 +4763,12 @@ function calculateAdherenceScore(todoList) {
|
|
|
4196
4763
|
const completedCount = todoList.filter((item) => item.status === "completed").length;
|
|
4197
4764
|
return todoList.length === 0 ? 1 : Number((completedCount / todoList.length).toFixed(2));
|
|
4198
4765
|
}
|
|
4766
|
+
function formatTodoListSummary(todoList) {
|
|
4767
|
+
if (!Array.isArray(todoList) || todoList.length === 0) {
|
|
4768
|
+
return "No plan items.";
|
|
4769
|
+
}
|
|
4770
|
+
return todoList.map((item) => `${item.id}. ${item.title || item.step || item.description || `Step ${item.id}`} [${item.status}]`).join("\n");
|
|
4771
|
+
}
|
|
4199
4772
|
function createContextTools(stateRef, opts) {
|
|
4200
4773
|
const tools = [];
|
|
4201
4774
|
if (opts?.planningEnabled) {
|
|
@@ -4208,11 +4781,43 @@ function createContextTools(stateRef, opts) {
|
|
|
4208
4781
|
todoList: z.array(z.union([todoWriteItemSchema, todoUpdateItemSchema])).optional()
|
|
4209
4782
|
}),
|
|
4210
4783
|
func: async ({ operation, expectedVersion, todoList }) => {
|
|
4211
|
-
const
|
|
4212
|
-
const
|
|
4213
|
-
|
|
4784
|
+
const toolStateRef = manageTodo._stateRef;
|
|
4785
|
+
const onEvent = toolStateRef?.__onEvent;
|
|
4786
|
+
const emitPlanEvent = (list) => {
|
|
4787
|
+
const version = stateRef.planVersion || 1;
|
|
4788
|
+
const adherenceScore = stateRef.adherenceScore || 0;
|
|
4789
|
+
const planData = {
|
|
4790
|
+
source: "manage_todo_list",
|
|
4791
|
+
operation,
|
|
4792
|
+
version,
|
|
4793
|
+
adherenceScore,
|
|
4794
|
+
count: Array.isArray(list) ? list.length : 0
|
|
4795
|
+
};
|
|
4796
|
+
onEvent?.({ type: "plan", todoList: list, ...planData });
|
|
4797
|
+
const traceSession = toolStateRef?.ctx?.__traceSession;
|
|
4798
|
+
recordTraceEvent(traceSession, {
|
|
4799
|
+
type: "plan",
|
|
4800
|
+
label: `Plan ${operation}`,
|
|
4801
|
+
actor: { scope: "agent", name: "manage_todo_list", role: "planner" },
|
|
4802
|
+
sections: [
|
|
4803
|
+
{
|
|
4804
|
+
kind: "summary",
|
|
4805
|
+
label: "Todo List",
|
|
4806
|
+
content: formatTodoListSummary(list)
|
|
4807
|
+
},
|
|
4808
|
+
{
|
|
4809
|
+
kind: "metadata",
|
|
4810
|
+
label: "Plan Metadata",
|
|
4811
|
+
data: planData
|
|
4812
|
+
}
|
|
4813
|
+
],
|
|
4814
|
+
debug: planData
|
|
4815
|
+
});
|
|
4816
|
+
};
|
|
4817
|
+
const currentVersion = stateRef.planVersion || 0;
|
|
4818
|
+
if (operation === "read") {
|
|
4214
4819
|
const list = stateRef.todoList || [];
|
|
4215
|
-
|
|
4820
|
+
emitPlanEvent(list);
|
|
4216
4821
|
return list;
|
|
4217
4822
|
}
|
|
4218
4823
|
if (typeof expectedVersion === "number" && expectedVersion !== currentVersion) {
|
|
@@ -4277,9 +4882,10 @@ function createContextTools(stateRef, opts) {
|
|
|
4277
4882
|
adherenceScore: stateRef.adherenceScore || 0
|
|
4278
4883
|
};
|
|
4279
4884
|
}
|
|
4280
|
-
const
|
|
4885
|
+
const currentMap = new Map(currentList.map((item) => [item.id, item]));
|
|
4886
|
+
const patchMap = /* @__PURE__ */ new Map();
|
|
4281
4887
|
for (const item of parsed.data) {
|
|
4282
|
-
if (
|
|
4888
|
+
if (patchMap.has(item.id)) {
|
|
4283
4889
|
return {
|
|
4284
4890
|
status: "error",
|
|
4285
4891
|
operation,
|
|
@@ -4288,10 +4894,7 @@ function createContextTools(stateRef, opts) {
|
|
|
4288
4894
|
adherenceScore: stateRef.adherenceScore || 0
|
|
4289
4895
|
};
|
|
4290
4896
|
}
|
|
4291
|
-
|
|
4292
|
-
}
|
|
4293
|
-
const currentMap = new Map(currentList.map((item) => [item.id, item]));
|
|
4294
|
-
for (const item of parsed.data) {
|
|
4897
|
+
patchMap.set(item.id, item);
|
|
4295
4898
|
if (!currentMap.has(item.id)) {
|
|
4296
4899
|
return {
|
|
4297
4900
|
status: "error",
|
|
@@ -4303,7 +4906,7 @@ function createContextTools(stateRef, opts) {
|
|
|
4303
4906
|
}
|
|
4304
4907
|
}
|
|
4305
4908
|
const mergedList = currentList.map((item) => {
|
|
4306
|
-
const patch =
|
|
4909
|
+
const patch = patchMap.get(item.id);
|
|
4307
4910
|
return patch ? normalizeTodoItem(patch, item) : item;
|
|
4308
4911
|
});
|
|
4309
4912
|
const validation = validatePlanInvariants(mergedList, false);
|
|
@@ -4327,7 +4930,7 @@ function createContextTools(stateRef, opts) {
|
|
|
4327
4930
|
version: stateRef.planVersion || 1,
|
|
4328
4931
|
adherenceScore: stateRef.adherenceScore || 0
|
|
4329
4932
|
};
|
|
4330
|
-
|
|
4933
|
+
emitPlanEvent(stateRef.todoList || []);
|
|
4331
4934
|
return payload;
|
|
4332
4935
|
}
|
|
4333
4936
|
});
|
|
@@ -4352,23 +4955,6 @@ function createContextTools(stateRef, opts) {
|
|
|
4352
4955
|
});
|
|
4353
4956
|
getTool._stateRef = stateRef;
|
|
4354
4957
|
tools.push(getTool);
|
|
4355
|
-
if (opts?.outputSchema) {
|
|
4356
|
-
const responseTool = createTool({
|
|
4357
|
-
name: "response",
|
|
4358
|
-
description: "Finalize the answer by returning the final structured JSON matching the required schema. Call exactly once when you are fully done, then stop.",
|
|
4359
|
-
schema: opts.outputSchema.shape ? opts.outputSchema : z.any(),
|
|
4360
|
-
func: async (data) => {
|
|
4361
|
-
try {
|
|
4362
|
-
const validated = opts.outputSchema.parse ? opts.outputSchema.parse(data) : data;
|
|
4363
|
-
return { __finalStructuredOutput: true, data: validated };
|
|
4364
|
-
} catch (e) {
|
|
4365
|
-
return { error: "Schema validation failed", details: e?.message };
|
|
4366
|
-
}
|
|
4367
|
-
}
|
|
4368
|
-
});
|
|
4369
|
-
responseTool._stateRef = stateRef;
|
|
4370
|
-
tools.push(responseTool);
|
|
4371
|
-
}
|
|
4372
4958
|
return tools;
|
|
4373
4959
|
}
|
|
4374
4960
|
|
|
@@ -4577,7 +5163,7 @@ function createSmartAgent(opts) {
|
|
|
4577
5163
|
useTodoList: planningEnabled
|
|
4578
5164
|
};
|
|
4579
5165
|
const stateRef = { toolHistory: void 0, toolHistoryArchived: void 0, todoList: void 0, planVersion: 0, adherenceScore: 0 };
|
|
4580
|
-
const contextTools = createContextTools(stateRef, { planningEnabled
|
|
5166
|
+
const contextTools = createContextTools(stateRef, { planningEnabled });
|
|
4581
5167
|
const mergedTools = [...opts.tools ?? [], ...contextTools];
|
|
4582
5168
|
const base = createAgent({ ...runtimeOpts, tools: mergedTools });
|
|
4583
5169
|
base.__runtime.runtimeProfile = resolved.runtimeProfile;
|
|
@@ -4586,12 +5172,8 @@ function createSmartAgent(opts) {
|
|
|
4586
5172
|
const summarizer = summarizationEnabled ? createContextSummarizeNode(runtimeOpts) : void 0;
|
|
4587
5173
|
const decideBefore = resolverDecisionFactory(runtimeOpts, summarizationEnabled);
|
|
4588
5174
|
const decideAfter = toolsDecisionFactory(runtimeOpts, summarizationEnabled);
|
|
4589
|
-
const
|
|
4590
|
-
|
|
4591
|
-
"Do NOT output the final JSON directly as an assistant message.",
|
|
4592
|
-
"When completely finished, call tool `response` passing the final JSON matching the schema as its arguments (direct object).",
|
|
4593
|
-
"Call it exactly once then STOP producing further assistant messages."
|
|
4594
|
-
].join("\n") : "";
|
|
5175
|
+
const soManager = opts.outputSchema ? new StructuredOutputManager(opts.outputSchema, resolveStrategy(opts.model)) : void 0;
|
|
5176
|
+
const structuredOutputHint = soManager ? soManager.buildSystemPromptHint() : "";
|
|
4595
5177
|
const runtimeHint = [
|
|
4596
5178
|
resolved.runtimeProfile === resolved.baseProfile ? `Runtime profile: ${resolved.runtimeProfile}.` : `Runtime profile: ${resolved.runtimeProfile} (base: ${resolved.baseProfile}).`,
|
|
4597
5179
|
`Context policy: ${resolved.context.policy}.`,
|
|
@@ -4661,8 +5243,13 @@ function createSmartAgent(opts) {
|
|
|
4661
5243
|
const next = summarizationEnabled ? decideBefore(state) : "agent";
|
|
4662
5244
|
if (next === "contextSummarize" && summarizer) {
|
|
4663
5245
|
const delta = await summarizer(state);
|
|
4664
|
-
if (!delta || Object.keys(delta).length === 0)
|
|
4665
|
-
|
|
5246
|
+
if (!delta || Object.keys(delta).length === 0) {
|
|
5247
|
+
const ctx = { ...state.ctx || {}, __summarizationExhausted: true };
|
|
5248
|
+
state = { ...state, ctx };
|
|
5249
|
+
} else {
|
|
5250
|
+
const ctx = { ...state.ctx || {} };
|
|
5251
|
+
delete ctx.__summarizationExhausted;
|
|
5252
|
+
state = await persistLatestSummary(syncPlanState({ ...state, ...delta, ctx }));
|
|
4666
5253
|
rawMessages = [...state.messages || rawMessages];
|
|
4667
5254
|
continue;
|
|
4668
5255
|
}
|
|
@@ -4695,20 +5282,32 @@ function createSmartAgent(opts) {
|
|
|
4695
5282
|
state = { ...state, ctx };
|
|
4696
5283
|
const delta = await summarizer(state);
|
|
4697
5284
|
if (delta && Object.keys(delta).length > 0) {
|
|
4698
|
-
|
|
5285
|
+
const successCtx = { ...state.ctx || {} };
|
|
5286
|
+
delete successCtx.__summarizationExhausted;
|
|
5287
|
+
state = await persistLatestSummary(syncPlanState({ ...state, ...delta, ctx: successCtx }));
|
|
4699
5288
|
rawMessages = [...state.messages || rawMessages];
|
|
4700
5289
|
continue;
|
|
5290
|
+
} else {
|
|
5291
|
+
const exhaustedCtx = { ...state.ctx || {}, __summarizationExhausted: true };
|
|
5292
|
+
state = { ...state, ctx: exhaustedCtx };
|
|
5293
|
+
continue;
|
|
4701
5294
|
}
|
|
4702
5295
|
}
|
|
4703
5296
|
if (state.ctx?.__finalizedDueToStructuredOutput) break;
|
|
5297
|
+
if (opts.outputSchema && lastResult?.output != null) break;
|
|
4704
5298
|
if (summarizationEnabled) {
|
|
4705
5299
|
const after = decideAfter(state);
|
|
4706
5300
|
if (after === "contextSummarize" && summarizer) {
|
|
4707
5301
|
const delta = await summarizer(state);
|
|
4708
5302
|
if (delta && Object.keys(delta).length > 0) {
|
|
4709
|
-
|
|
5303
|
+
const successCtx = { ...state.ctx || {} };
|
|
5304
|
+
delete successCtx.__summarizationExhausted;
|
|
5305
|
+
state = await persistLatestSummary(syncPlanState({ ...state, ...delta, ctx: successCtx }));
|
|
4710
5306
|
rawMessages = [...state.messages || rawMessages];
|
|
4711
5307
|
continue;
|
|
5308
|
+
} else {
|
|
5309
|
+
const exhaustedCtx = { ...state.ctx || {}, __summarizationExhausted: true };
|
|
5310
|
+
state = { ...state, ctx: exhaustedCtx };
|
|
4712
5311
|
}
|
|
4713
5312
|
}
|
|
4714
5313
|
}
|
|
@@ -5151,6 +5750,1740 @@ function createCodeGuardrail(options) {
|
|
|
5151
5750
|
});
|
|
5152
5751
|
}
|
|
5153
5752
|
|
|
5154
|
-
|
|
5753
|
+
// src/providers/types.ts
|
|
5754
|
+
function emptyUsage() {
|
|
5755
|
+
return {
|
|
5756
|
+
inputTokens: 0,
|
|
5757
|
+
outputTokens: 0,
|
|
5758
|
+
totalTokens: 0,
|
|
5759
|
+
cachedInputTokens: 0,
|
|
5760
|
+
cachedWriteTokens: 0,
|
|
5761
|
+
cachedOutputTokens: 0,
|
|
5762
|
+
reasoningTokens: 0
|
|
5763
|
+
};
|
|
5764
|
+
}
|
|
5765
|
+
var ProviderError = class extends Error {
|
|
5766
|
+
constructor(message, provider, statusCode, responseBody) {
|
|
5767
|
+
super(message);
|
|
5768
|
+
this.provider = provider;
|
|
5769
|
+
this.statusCode = statusCode;
|
|
5770
|
+
this.responseBody = responseBody;
|
|
5771
|
+
this.name = "ProviderError";
|
|
5772
|
+
}
|
|
5773
|
+
};
|
|
5774
|
+
|
|
5775
|
+
// src/providers/base.ts
|
|
5776
|
+
var BaseProvider = class {
|
|
5777
|
+
};
|
|
5778
|
+
|
|
5779
|
+
// src/providers/utils/sse.ts
|
|
5780
|
+
async function* parseSSEStream(body, signal) {
|
|
5781
|
+
const reader = body.getReader();
|
|
5782
|
+
const decoder = new TextDecoder();
|
|
5783
|
+
let buffer = "";
|
|
5784
|
+
try {
|
|
5785
|
+
while (true) {
|
|
5786
|
+
if (signal?.aborted) ;
|
|
5787
|
+
const { done, value } = await reader.read();
|
|
5788
|
+
if (done) break;
|
|
5789
|
+
buffer += decoder.decode(value, { stream: true });
|
|
5790
|
+
const parts = buffer.split(/\r?\n\r?\n/);
|
|
5791
|
+
buffer = parts.pop() ?? "";
|
|
5792
|
+
for (const part of parts) {
|
|
5793
|
+
if (!part.trim()) continue;
|
|
5794
|
+
const event = parseSSEBlock(part);
|
|
5795
|
+
if (event) yield event;
|
|
5796
|
+
}
|
|
5797
|
+
}
|
|
5798
|
+
if (buffer.trim()) {
|
|
5799
|
+
const event = parseSSEBlock(buffer);
|
|
5800
|
+
if (event) yield event;
|
|
5801
|
+
}
|
|
5802
|
+
} finally {
|
|
5803
|
+
reader.releaseLock();
|
|
5804
|
+
}
|
|
5805
|
+
}
|
|
5806
|
+
function parseSSEBlock(block) {
|
|
5807
|
+
let event;
|
|
5808
|
+
let id;
|
|
5809
|
+
let retry;
|
|
5810
|
+
const dataLines = [];
|
|
5811
|
+
for (const line of block.split(/\r?\n/)) {
|
|
5812
|
+
if (line.startsWith(":")) continue;
|
|
5813
|
+
const colonIdx = line.indexOf(":");
|
|
5814
|
+
let field;
|
|
5815
|
+
let value;
|
|
5816
|
+
if (colonIdx === -1) {
|
|
5817
|
+
field = line;
|
|
5818
|
+
value = "";
|
|
5819
|
+
} else {
|
|
5820
|
+
field = line.slice(0, colonIdx);
|
|
5821
|
+
value = line.slice(colonIdx + 1);
|
|
5822
|
+
if (value.startsWith(" ")) value = value.slice(1);
|
|
5823
|
+
}
|
|
5824
|
+
switch (field) {
|
|
5825
|
+
case "event":
|
|
5826
|
+
event = value;
|
|
5827
|
+
break;
|
|
5828
|
+
case "data":
|
|
5829
|
+
dataLines.push(value);
|
|
5830
|
+
break;
|
|
5831
|
+
case "id":
|
|
5832
|
+
id = value;
|
|
5833
|
+
break;
|
|
5834
|
+
case "retry": {
|
|
5835
|
+
const n = parseInt(value, 10);
|
|
5836
|
+
if (!isNaN(n)) retry = n;
|
|
5837
|
+
break;
|
|
5838
|
+
}
|
|
5839
|
+
}
|
|
5840
|
+
}
|
|
5841
|
+
if (dataLines.length === 0) return null;
|
|
5842
|
+
return { event, data: dataLines.join("\n"), id, retry };
|
|
5843
|
+
}
|
|
5844
|
+
|
|
5845
|
+
// src/providers/openai.ts
|
|
5846
|
+
var OpenAIProvider = class extends BaseProvider {
|
|
5847
|
+
constructor(config) {
|
|
5848
|
+
super();
|
|
5849
|
+
this.providerName = "openai";
|
|
5850
|
+
this.apiKey = config.apiKey;
|
|
5851
|
+
this.baseURL = (config.baseURL ?? "https://api.openai.com/v1").replace(/\/$/, "");
|
|
5852
|
+
this.defaultModel = config.defaultModel ?? "gpt-4o";
|
|
5853
|
+
this.defaultHeaders = {
|
|
5854
|
+
...config.defaultHeaders,
|
|
5855
|
+
...config.organization ? { "OpenAI-Organization": config.organization } : {}
|
|
5856
|
+
};
|
|
5857
|
+
}
|
|
5858
|
+
async complete(request) {
|
|
5859
|
+
const body = this.buildRequestBody(request, false);
|
|
5860
|
+
const res = await this.doFetch(body);
|
|
5861
|
+
const json = await res.json();
|
|
5862
|
+
return this.parseResponse(json);
|
|
5863
|
+
}
|
|
5864
|
+
async *completeStream(request) {
|
|
5865
|
+
const body = this.buildRequestBody(request, true);
|
|
5866
|
+
const res = await this.doFetch(body);
|
|
5867
|
+
if (!res.body) throw new ProviderError("No response body for stream", this.providerName);
|
|
5868
|
+
let id = "";
|
|
5869
|
+
let model = "";
|
|
5870
|
+
const toolCallBuffers = /* @__PURE__ */ new Map();
|
|
5871
|
+
let usage;
|
|
5872
|
+
for await (const event of parseSSEStream(res.body)) {
|
|
5873
|
+
if (event.data === "[DONE]") break;
|
|
5874
|
+
let chunk;
|
|
5875
|
+
try {
|
|
5876
|
+
chunk = JSON.parse(event.data);
|
|
5877
|
+
} catch {
|
|
5878
|
+
continue;
|
|
5879
|
+
}
|
|
5880
|
+
id = chunk.id ?? id;
|
|
5881
|
+
model = chunk.model ?? model;
|
|
5882
|
+
const choice = chunk.choices?.[0];
|
|
5883
|
+
if (chunk.usage) {
|
|
5884
|
+
usage = this.extractUsage(chunk.usage);
|
|
5885
|
+
}
|
|
5886
|
+
if (!choice) {
|
|
5887
|
+
if (usage) yield { id, model, delta: {}, usage };
|
|
5888
|
+
continue;
|
|
5889
|
+
}
|
|
5890
|
+
const delta = choice.delta ?? {};
|
|
5891
|
+
const out = {
|
|
5892
|
+
id,
|
|
5893
|
+
model,
|
|
5894
|
+
delta: {},
|
|
5895
|
+
finishReason: mapFinishReason(choice.finish_reason)
|
|
5896
|
+
};
|
|
5897
|
+
if (delta.content) {
|
|
5898
|
+
out.delta.content = delta.content;
|
|
5899
|
+
}
|
|
5900
|
+
if (delta.tool_calls) {
|
|
5901
|
+
const partials = [];
|
|
5902
|
+
for (const tc of delta.tool_calls) {
|
|
5903
|
+
const idx = tc.index ?? 0;
|
|
5904
|
+
if (!toolCallBuffers.has(idx)) {
|
|
5905
|
+
toolCallBuffers.set(idx, { id: tc.id ?? "", name: "", arguments: "" });
|
|
5906
|
+
}
|
|
5907
|
+
const buf = toolCallBuffers.get(idx);
|
|
5908
|
+
if (tc.id) buf.id = tc.id;
|
|
5909
|
+
if (tc.function?.name) buf.name = tc.function.name;
|
|
5910
|
+
if (tc.function?.arguments) buf.arguments += tc.function.arguments;
|
|
5911
|
+
partials.push({ id: buf.id, name: buf.name || void 0, arguments: tc.function?.arguments });
|
|
5912
|
+
}
|
|
5913
|
+
out.delta.toolCalls = partials;
|
|
5914
|
+
}
|
|
5915
|
+
if (usage) out.usage = usage;
|
|
5916
|
+
yield out;
|
|
5917
|
+
}
|
|
5918
|
+
}
|
|
5919
|
+
// ─── Internals ───────────────────────────────────────────────────────────
|
|
5920
|
+
buildRequestBody(request, stream) {
|
|
5921
|
+
const body = {
|
|
5922
|
+
model: request.model || this.defaultModel,
|
|
5923
|
+
messages: request.messages.map((m) => this.toOpenAIMessage(m)),
|
|
5924
|
+
stream
|
|
5925
|
+
};
|
|
5926
|
+
if (stream) {
|
|
5927
|
+
body.stream_options = { include_usage: true };
|
|
5928
|
+
}
|
|
5929
|
+
if (request.temperature != null) body.temperature = request.temperature;
|
|
5930
|
+
if (request.maxTokens != null) body.max_tokens = request.maxTokens;
|
|
5931
|
+
if (request.topP != null) body.top_p = request.topP;
|
|
5932
|
+
if (request.stop) body.stop = request.stop;
|
|
5933
|
+
if (request.tools?.length) {
|
|
5934
|
+
body.tools = request.tools.map((t) => this.toOpenAITool(t));
|
|
5935
|
+
}
|
|
5936
|
+
if (request.toolChoice != null) {
|
|
5937
|
+
if (typeof request.toolChoice === "string") {
|
|
5938
|
+
body.tool_choice = request.toolChoice;
|
|
5939
|
+
} else {
|
|
5940
|
+
body.tool_choice = { type: "function", function: { name: request.toolChoice.name } };
|
|
5941
|
+
}
|
|
5942
|
+
}
|
|
5943
|
+
if (request.responseFormat) {
|
|
5944
|
+
if (request.responseFormat.type === "json_schema") {
|
|
5945
|
+
body.response_format = {
|
|
5946
|
+
type: "json_schema",
|
|
5947
|
+
json_schema: {
|
|
5948
|
+
name: request.responseFormat.name ?? "response",
|
|
5949
|
+
schema: request.responseFormat.schema,
|
|
5950
|
+
strict: true
|
|
5951
|
+
}
|
|
5952
|
+
};
|
|
5953
|
+
} else if (request.responseFormat.type === "json_object") {
|
|
5954
|
+
body.response_format = { type: "json_object" };
|
|
5955
|
+
}
|
|
5956
|
+
}
|
|
5957
|
+
if (request.extra) {
|
|
5958
|
+
Object.assign(body, request.extra);
|
|
5959
|
+
}
|
|
5960
|
+
return body;
|
|
5961
|
+
}
|
|
5962
|
+
toOpenAIMessage(m) {
|
|
5963
|
+
const msg = { role: m.role };
|
|
5964
|
+
if (typeof m.content === "string") {
|
|
5965
|
+
msg.content = m.content;
|
|
5966
|
+
} else if (Array.isArray(m.content)) {
|
|
5967
|
+
msg.content = m.content.map((part) => {
|
|
5968
|
+
if (part.type === "text") return { type: "text", text: part.text };
|
|
5969
|
+
if (part.type === "image") {
|
|
5970
|
+
const src = part.source;
|
|
5971
|
+
const url = src.type === "url" ? src.url : `data:${src.mediaType};base64,${src.data}`;
|
|
5972
|
+
return { type: "image_url", image_url: { url } };
|
|
5973
|
+
}
|
|
5974
|
+
return part;
|
|
5975
|
+
});
|
|
5976
|
+
}
|
|
5977
|
+
if (m.role === "assistant" && m.toolCalls?.length) {
|
|
5978
|
+
msg.tool_calls = m.toolCalls.map((tc) => ({
|
|
5979
|
+
id: tc.id,
|
|
5980
|
+
type: "function",
|
|
5981
|
+
function: { name: tc.name, arguments: tc.arguments }
|
|
5982
|
+
}));
|
|
5983
|
+
}
|
|
5984
|
+
if (m.role === "tool" && m.toolCallId) {
|
|
5985
|
+
msg.tool_call_id = m.toolCallId;
|
|
5986
|
+
}
|
|
5987
|
+
if (m.name) msg.name = m.name;
|
|
5988
|
+
return msg;
|
|
5989
|
+
}
|
|
5990
|
+
toOpenAITool(t) {
|
|
5991
|
+
const fn = {
|
|
5992
|
+
name: t.name,
|
|
5993
|
+
description: t.description,
|
|
5994
|
+
parameters: t.parameters
|
|
5995
|
+
};
|
|
5996
|
+
if (t.strict) {
|
|
5997
|
+
fn.strict = true;
|
|
5998
|
+
}
|
|
5999
|
+
return { type: "function", function: fn };
|
|
6000
|
+
}
|
|
6001
|
+
async doFetch(body) {
|
|
6002
|
+
const url = `${this.baseURL}/chat/completions`;
|
|
6003
|
+
const res = await fetch(url, {
|
|
6004
|
+
method: "POST",
|
|
6005
|
+
headers: {
|
|
6006
|
+
"Content-Type": "application/json",
|
|
6007
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
6008
|
+
...this.defaultHeaders
|
|
6009
|
+
},
|
|
6010
|
+
body: JSON.stringify(body)
|
|
6011
|
+
});
|
|
6012
|
+
if (!res.ok) {
|
|
6013
|
+
const text = await res.text().catch(() => "");
|
|
6014
|
+
throw new ProviderError(
|
|
6015
|
+
`OpenAI API error ${res.status}: ${text}`,
|
|
6016
|
+
this.providerName,
|
|
6017
|
+
res.status,
|
|
6018
|
+
text
|
|
6019
|
+
);
|
|
6020
|
+
}
|
|
6021
|
+
return res;
|
|
6022
|
+
}
|
|
6023
|
+
parseResponse(json) {
|
|
6024
|
+
const choice = json.choices?.[0];
|
|
6025
|
+
const message = choice?.message ?? {};
|
|
6026
|
+
const toolCalls = (message.tool_calls ?? []).map((tc) => ({
|
|
6027
|
+
id: tc.id,
|
|
6028
|
+
name: tc.function?.name ?? "",
|
|
6029
|
+
arguments: tc.function?.arguments ?? "{}"
|
|
6030
|
+
}));
|
|
6031
|
+
return {
|
|
6032
|
+
id: json.id ?? "",
|
|
6033
|
+
model: json.model ?? "",
|
|
6034
|
+
content: message.content ?? null,
|
|
6035
|
+
toolCalls,
|
|
6036
|
+
usage: this.extractUsage(json.usage),
|
|
6037
|
+
finishReason: mapFinishReason(choice?.finish_reason),
|
|
6038
|
+
raw: json
|
|
6039
|
+
};
|
|
6040
|
+
}
|
|
6041
|
+
extractUsage(usage) {
|
|
6042
|
+
if (!usage) return emptyUsage();
|
|
6043
|
+
const promptDetails = usage.prompt_tokens_details ?? {};
|
|
6044
|
+
const completionDetails = usage.completion_tokens_details ?? {};
|
|
6045
|
+
return {
|
|
6046
|
+
inputTokens: usage.prompt_tokens ?? 0,
|
|
6047
|
+
outputTokens: usage.completion_tokens ?? 0,
|
|
6048
|
+
totalTokens: usage.total_tokens ?? (usage.prompt_tokens ?? 0) + (usage.completion_tokens ?? 0),
|
|
6049
|
+
cachedInputTokens: promptDetails.cached_tokens ?? 0,
|
|
6050
|
+
cachedWriteTokens: 0,
|
|
6051
|
+
cachedOutputTokens: 0,
|
|
6052
|
+
reasoningTokens: completionDetails.reasoning_tokens ?? 0
|
|
6053
|
+
};
|
|
6054
|
+
}
|
|
6055
|
+
};
|
|
6056
|
+
function mapFinishReason(reason) {
|
|
6057
|
+
switch (reason) {
|
|
6058
|
+
case "stop":
|
|
6059
|
+
return "stop";
|
|
6060
|
+
case "tool_calls":
|
|
6061
|
+
return "tool_calls";
|
|
6062
|
+
case "length":
|
|
6063
|
+
return "length";
|
|
6064
|
+
case "content_filter":
|
|
6065
|
+
return "content_filter";
|
|
6066
|
+
default:
|
|
6067
|
+
return "stop";
|
|
6068
|
+
}
|
|
6069
|
+
}
|
|
6070
|
+
|
|
6071
|
+
// src/providers/anthropic.ts
|
|
6072
|
+
var AnthropicProvider = class extends BaseProvider {
|
|
6073
|
+
constructor(config) {
|
|
6074
|
+
super();
|
|
6075
|
+
this.providerName = "anthropic";
|
|
6076
|
+
this.apiKey = config.apiKey;
|
|
6077
|
+
this.baseURL = (config.baseURL ?? "https://api.anthropic.com").replace(/\/$/, "");
|
|
6078
|
+
this.defaultModel = config.defaultModel ?? "claude-sonnet-4-20250514";
|
|
6079
|
+
this.defaultHeaders = config.defaultHeaders ?? {};
|
|
6080
|
+
this.anthropicVersion = config.anthropicVersion ?? "2023-06-01";
|
|
6081
|
+
}
|
|
6082
|
+
async complete(request) {
|
|
6083
|
+
const body = this.buildRequestBody(request, false);
|
|
6084
|
+
const res = await this.doFetch(body);
|
|
6085
|
+
const json = await res.json();
|
|
6086
|
+
return this.parseResponse(json);
|
|
6087
|
+
}
|
|
6088
|
+
async *completeStream(request) {
|
|
6089
|
+
const body = this.buildRequestBody(request, true);
|
|
6090
|
+
const res = await this.doFetch(body);
|
|
6091
|
+
if (!res.body) throw new ProviderError("No response body for stream", this.providerName);
|
|
6092
|
+
let id = "";
|
|
6093
|
+
let model = "";
|
|
6094
|
+
let inputTokens = 0;
|
|
6095
|
+
let outputTokens = 0;
|
|
6096
|
+
let cacheReadInputTokens = 0;
|
|
6097
|
+
let cacheCreationInputTokens = 0;
|
|
6098
|
+
const currentToolCalls = /* @__PURE__ */ new Map();
|
|
6099
|
+
for await (const event of parseSSEStream(res.body)) {
|
|
6100
|
+
let data;
|
|
6101
|
+
try {
|
|
6102
|
+
data = JSON.parse(event.data);
|
|
6103
|
+
} catch {
|
|
6104
|
+
continue;
|
|
6105
|
+
}
|
|
6106
|
+
const eventType = event.event ?? data.type;
|
|
6107
|
+
switch (eventType) {
|
|
6108
|
+
case "message_start": {
|
|
6109
|
+
const msg = data.message;
|
|
6110
|
+
id = msg?.id ?? "";
|
|
6111
|
+
model = msg?.model ?? "";
|
|
6112
|
+
if (msg?.usage) {
|
|
6113
|
+
inputTokens = msg.usage.input_tokens ?? 0;
|
|
6114
|
+
cacheReadInputTokens = msg.usage.cache_read_input_tokens ?? 0;
|
|
6115
|
+
cacheCreationInputTokens = msg.usage.cache_creation_input_tokens ?? 0;
|
|
6116
|
+
}
|
|
6117
|
+
break;
|
|
6118
|
+
}
|
|
6119
|
+
case "content_block_start": {
|
|
6120
|
+
const idx = data.index ?? 0;
|
|
6121
|
+
const block = data.content_block;
|
|
6122
|
+
if (block?.type === "tool_use") {
|
|
6123
|
+
currentToolCalls.set(idx, {
|
|
6124
|
+
id: block.id ?? "",
|
|
6125
|
+
name: block.name ?? "",
|
|
6126
|
+
arguments: ""
|
|
6127
|
+
});
|
|
6128
|
+
}
|
|
6129
|
+
break;
|
|
6130
|
+
}
|
|
6131
|
+
case "content_block_delta": {
|
|
6132
|
+
const idx = data.index ?? 0;
|
|
6133
|
+
const delta = data.delta;
|
|
6134
|
+
if (delta?.type === "text_delta" && delta.text) {
|
|
6135
|
+
yield {
|
|
6136
|
+
id,
|
|
6137
|
+
model,
|
|
6138
|
+
delta: { content: delta.text }
|
|
6139
|
+
};
|
|
6140
|
+
} else if (delta?.type === "input_json_delta" && delta.partial_json) {
|
|
6141
|
+
const tc = currentToolCalls.get(idx);
|
|
6142
|
+
if (tc) {
|
|
6143
|
+
tc.arguments += delta.partial_json;
|
|
6144
|
+
yield {
|
|
6145
|
+
id,
|
|
6146
|
+
model,
|
|
6147
|
+
delta: {
|
|
6148
|
+
toolCalls: [{ id: tc.id, name: tc.name, arguments: delta.partial_json }]
|
|
6149
|
+
}
|
|
6150
|
+
};
|
|
6151
|
+
}
|
|
6152
|
+
}
|
|
6153
|
+
break;
|
|
6154
|
+
}
|
|
6155
|
+
case "content_block_stop":
|
|
6156
|
+
break;
|
|
6157
|
+
case "message_delta": {
|
|
6158
|
+
const delta = data.delta;
|
|
6159
|
+
if (data.usage) {
|
|
6160
|
+
outputTokens = data.usage.output_tokens ?? outputTokens;
|
|
6161
|
+
}
|
|
6162
|
+
const finishReason = mapAnthropicStopReason(delta?.stop_reason);
|
|
6163
|
+
const usage = {
|
|
6164
|
+
inputTokens,
|
|
6165
|
+
outputTokens,
|
|
6166
|
+
totalTokens: inputTokens + outputTokens,
|
|
6167
|
+
cachedInputTokens: cacheReadInputTokens,
|
|
6168
|
+
cachedWriteTokens: cacheCreationInputTokens,
|
|
6169
|
+
cachedOutputTokens: 0,
|
|
6170
|
+
reasoningTokens: 0
|
|
6171
|
+
};
|
|
6172
|
+
yield {
|
|
6173
|
+
id,
|
|
6174
|
+
model,
|
|
6175
|
+
delta: {},
|
|
6176
|
+
finishReason,
|
|
6177
|
+
usage
|
|
6178
|
+
};
|
|
6179
|
+
break;
|
|
6180
|
+
}
|
|
6181
|
+
case "message_stop":
|
|
6182
|
+
break;
|
|
6183
|
+
case "error": {
|
|
6184
|
+
const errMsg = data.error?.message ?? "Unknown Anthropic streaming error";
|
|
6185
|
+
throw new ProviderError(errMsg, this.providerName);
|
|
6186
|
+
}
|
|
6187
|
+
}
|
|
6188
|
+
}
|
|
6189
|
+
}
|
|
6190
|
+
// ─── Internals ───────────────────────────────────────────────────────────
|
|
6191
|
+
buildRequestBody(request, stream) {
|
|
6192
|
+
const { system, messages } = this.splitSystemMessages(request.messages);
|
|
6193
|
+
const body = {
|
|
6194
|
+
model: request.model || this.defaultModel,
|
|
6195
|
+
messages: messages.map((m) => this.toAnthropicMessage(m)),
|
|
6196
|
+
max_tokens: request.maxTokens ?? 4096,
|
|
6197
|
+
stream
|
|
6198
|
+
};
|
|
6199
|
+
if (system) body.system = system;
|
|
6200
|
+
if (request.temperature != null) body.temperature = request.temperature;
|
|
6201
|
+
if (request.topP != null) body.top_p = request.topP;
|
|
6202
|
+
if (request.stop) body.stop_sequences = request.stop;
|
|
6203
|
+
if (request.tools?.length) {
|
|
6204
|
+
body.tools = request.tools.map((t) => this.toAnthropicTool(t));
|
|
6205
|
+
}
|
|
6206
|
+
if (request.toolChoice != null) {
|
|
6207
|
+
if (request.toolChoice === "auto") {
|
|
6208
|
+
body.tool_choice = { type: "auto" };
|
|
6209
|
+
} else if (request.toolChoice === "required") {
|
|
6210
|
+
body.tool_choice = { type: "any" };
|
|
6211
|
+
} else if (request.toolChoice === "none") ; else {
|
|
6212
|
+
body.tool_choice = { type: "tool", name: request.toolChoice.name };
|
|
6213
|
+
}
|
|
6214
|
+
}
|
|
6215
|
+
if (request.extra) {
|
|
6216
|
+
Object.assign(body, request.extra);
|
|
6217
|
+
}
|
|
6218
|
+
return body;
|
|
6219
|
+
}
|
|
6220
|
+
splitSystemMessages(messages) {
|
|
6221
|
+
const systemParts = [];
|
|
6222
|
+
const rest = [];
|
|
6223
|
+
for (const m of messages) {
|
|
6224
|
+
if (m.role === "system") {
|
|
6225
|
+
if (typeof m.content === "string") {
|
|
6226
|
+
systemParts.push(m.content);
|
|
6227
|
+
} else {
|
|
6228
|
+
const text = m.content.filter((p) => p.type === "text").map((p) => p.text).join("\n");
|
|
6229
|
+
if (text) systemParts.push(text);
|
|
6230
|
+
}
|
|
6231
|
+
} else {
|
|
6232
|
+
rest.push(m);
|
|
6233
|
+
}
|
|
6234
|
+
}
|
|
6235
|
+
return {
|
|
6236
|
+
system: systemParts.length > 0 ? systemParts.join("\n\n") : void 0,
|
|
6237
|
+
messages: rest
|
|
6238
|
+
};
|
|
6239
|
+
}
|
|
6240
|
+
toAnthropicMessage(m) {
|
|
6241
|
+
if (m.role === "tool" && m.toolCallId) {
|
|
6242
|
+
const content = typeof m.content === "string" ? m.content : JSON.stringify(m.content);
|
|
6243
|
+
return {
|
|
6244
|
+
role: "user",
|
|
6245
|
+
content: [
|
|
6246
|
+
{
|
|
6247
|
+
type: "tool_result",
|
|
6248
|
+
tool_use_id: m.toolCallId,
|
|
6249
|
+
content
|
|
6250
|
+
}
|
|
6251
|
+
]
|
|
6252
|
+
};
|
|
6253
|
+
}
|
|
6254
|
+
if (m.role === "assistant" && m.toolCalls?.length) {
|
|
6255
|
+
const content = [];
|
|
6256
|
+
if (typeof m.content === "string" && m.content) {
|
|
6257
|
+
content.push({ type: "text", text: m.content });
|
|
6258
|
+
}
|
|
6259
|
+
for (const tc of m.toolCalls) {
|
|
6260
|
+
content.push({
|
|
6261
|
+
type: "tool_use",
|
|
6262
|
+
id: tc.id,
|
|
6263
|
+
name: tc.name,
|
|
6264
|
+
input: safeJsonParse(tc.arguments)
|
|
6265
|
+
});
|
|
6266
|
+
}
|
|
6267
|
+
return { role: "assistant", content };
|
|
6268
|
+
}
|
|
6269
|
+
const msg = { role: m.role };
|
|
6270
|
+
if (typeof m.content === "string") {
|
|
6271
|
+
msg.content = m.content;
|
|
6272
|
+
} else if (Array.isArray(m.content)) {
|
|
6273
|
+
msg.content = m.content.map((part) => {
|
|
6274
|
+
if (part.type === "text") return { type: "text", text: part.text };
|
|
6275
|
+
if (part.type === "image") {
|
|
6276
|
+
const src = part.source;
|
|
6277
|
+
if (src.type === "base64") {
|
|
6278
|
+
return {
|
|
6279
|
+
type: "image",
|
|
6280
|
+
source: { type: "base64", media_type: src.mediaType, data: src.data }
|
|
6281
|
+
};
|
|
6282
|
+
}
|
|
6283
|
+
return {
|
|
6284
|
+
type: "image",
|
|
6285
|
+
source: { type: "url", url: src.url }
|
|
6286
|
+
};
|
|
6287
|
+
}
|
|
6288
|
+
return part;
|
|
6289
|
+
});
|
|
6290
|
+
}
|
|
6291
|
+
return msg;
|
|
6292
|
+
}
|
|
6293
|
+
toAnthropicTool(t) {
|
|
6294
|
+
return {
|
|
6295
|
+
name: t.name,
|
|
6296
|
+
description: t.description,
|
|
6297
|
+
input_schema: t.parameters
|
|
6298
|
+
};
|
|
6299
|
+
}
|
|
6300
|
+
async doFetch(body) {
|
|
6301
|
+
const url = `${this.baseURL}/v1/messages`;
|
|
6302
|
+
const res = await fetch(url, {
|
|
6303
|
+
method: "POST",
|
|
6304
|
+
headers: {
|
|
6305
|
+
"Content-Type": "application/json",
|
|
6306
|
+
"x-api-key": this.apiKey,
|
|
6307
|
+
"anthropic-version": this.anthropicVersion,
|
|
6308
|
+
...this.defaultHeaders
|
|
6309
|
+
},
|
|
6310
|
+
body: JSON.stringify(body)
|
|
6311
|
+
});
|
|
6312
|
+
if (!res.ok) {
|
|
6313
|
+
const text = await res.text().catch(() => "");
|
|
6314
|
+
throw new ProviderError(
|
|
6315
|
+
`Anthropic API error ${res.status}: ${text}`,
|
|
6316
|
+
this.providerName,
|
|
6317
|
+
res.status,
|
|
6318
|
+
text
|
|
6319
|
+
);
|
|
6320
|
+
}
|
|
6321
|
+
return res;
|
|
6322
|
+
}
|
|
6323
|
+
parseResponse(json) {
|
|
6324
|
+
const textParts = [];
|
|
6325
|
+
const toolCalls = [];
|
|
6326
|
+
for (const block of json.content ?? []) {
|
|
6327
|
+
if (block.type === "text") {
|
|
6328
|
+
textParts.push(block.text);
|
|
6329
|
+
} else if (block.type === "tool_use") {
|
|
6330
|
+
toolCalls.push({
|
|
6331
|
+
id: block.id,
|
|
6332
|
+
name: block.name,
|
|
6333
|
+
arguments: typeof block.input === "string" ? block.input : JSON.stringify(block.input)
|
|
6334
|
+
});
|
|
6335
|
+
}
|
|
6336
|
+
}
|
|
6337
|
+
const usage = json.usage ?? {};
|
|
6338
|
+
const tokenUsage = {
|
|
6339
|
+
inputTokens: usage.input_tokens ?? 0,
|
|
6340
|
+
outputTokens: usage.output_tokens ?? 0,
|
|
6341
|
+
totalTokens: (usage.input_tokens ?? 0) + (usage.output_tokens ?? 0),
|
|
6342
|
+
cachedInputTokens: usage.cache_read_input_tokens ?? 0,
|
|
6343
|
+
cachedWriteTokens: usage.cache_creation_input_tokens ?? 0,
|
|
6344
|
+
cachedOutputTokens: 0,
|
|
6345
|
+
reasoningTokens: 0
|
|
6346
|
+
};
|
|
6347
|
+
return {
|
|
6348
|
+
id: json.id ?? "",
|
|
6349
|
+
model: json.model ?? "",
|
|
6350
|
+
content: textParts.join("") || null,
|
|
6351
|
+
toolCalls,
|
|
6352
|
+
usage: tokenUsage,
|
|
6353
|
+
finishReason: mapAnthropicStopReason(json.stop_reason),
|
|
6354
|
+
raw: json
|
|
6355
|
+
};
|
|
6356
|
+
}
|
|
6357
|
+
};
|
|
6358
|
+
function mapAnthropicStopReason(reason) {
|
|
6359
|
+
switch (reason) {
|
|
6360
|
+
case "end_turn":
|
|
6361
|
+
case "stop_sequence":
|
|
6362
|
+
return "stop";
|
|
6363
|
+
case "tool_use":
|
|
6364
|
+
return "tool_calls";
|
|
6365
|
+
case "max_tokens":
|
|
6366
|
+
return "length";
|
|
6367
|
+
default:
|
|
6368
|
+
return "stop";
|
|
6369
|
+
}
|
|
6370
|
+
}
|
|
6371
|
+
function safeJsonParse(str) {
|
|
6372
|
+
try {
|
|
6373
|
+
return JSON.parse(str);
|
|
6374
|
+
} catch {
|
|
6375
|
+
return {};
|
|
6376
|
+
}
|
|
6377
|
+
}
|
|
6378
|
+
|
|
6379
|
+
// src/providers/azure.ts
|
|
6380
|
+
var AzureProvider = class extends OpenAIProvider {
|
|
6381
|
+
constructor(config) {
|
|
6382
|
+
super({
|
|
6383
|
+
provider: "openai",
|
|
6384
|
+
apiKey: config.apiKey,
|
|
6385
|
+
defaultModel: config.defaultModel ?? config.deploymentName ?? "gpt-4o",
|
|
6386
|
+
defaultHeaders: config.defaultHeaders
|
|
6387
|
+
});
|
|
6388
|
+
this.providerName = "azure";
|
|
6389
|
+
this.endpoint = config.endpoint.replace(/\/$/, "");
|
|
6390
|
+
this.apiVersion = config.apiVersion ?? "2024-10-21";
|
|
6391
|
+
this.deploymentName = config.deploymentName;
|
|
6392
|
+
this.azureApiKey = config.apiKey;
|
|
6393
|
+
}
|
|
6394
|
+
async doFetch(body) {
|
|
6395
|
+
const deployment = this.deploymentName ?? body.model;
|
|
6396
|
+
if (!deployment) {
|
|
6397
|
+
throw new ProviderError("Azure requires a deploymentName or model", this.providerName);
|
|
6398
|
+
}
|
|
6399
|
+
const url = `${this.endpoint}/openai/deployments/${deployment}/chat/completions?api-version=${this.apiVersion}`;
|
|
6400
|
+
const { model: _model, ...bodyWithoutModel } = body;
|
|
6401
|
+
const res = await fetch(url, {
|
|
6402
|
+
method: "POST",
|
|
6403
|
+
headers: {
|
|
6404
|
+
"Content-Type": "application/json",
|
|
6405
|
+
"api-key": this.azureApiKey,
|
|
6406
|
+
...this.defaultHeaders
|
|
6407
|
+
},
|
|
6408
|
+
body: JSON.stringify(bodyWithoutModel)
|
|
6409
|
+
});
|
|
6410
|
+
if (!res.ok) {
|
|
6411
|
+
const text = await res.text().catch(() => "");
|
|
6412
|
+
throw new ProviderError(
|
|
6413
|
+
`Azure OpenAI API error ${res.status}: ${text}`,
|
|
6414
|
+
this.providerName,
|
|
6415
|
+
res.status,
|
|
6416
|
+
text
|
|
6417
|
+
);
|
|
6418
|
+
}
|
|
6419
|
+
return res;
|
|
6420
|
+
}
|
|
6421
|
+
};
|
|
6422
|
+
|
|
6423
|
+
// src/providers/openaiCompatible.ts
|
|
6424
|
+
var OpenAICompatibleProvider = class extends OpenAIProvider {
|
|
6425
|
+
constructor(config) {
|
|
6426
|
+
super({
|
|
6427
|
+
provider: "openai",
|
|
6428
|
+
apiKey: config.apiKey,
|
|
6429
|
+
baseURL: config.baseURL,
|
|
6430
|
+
defaultModel: config.defaultModel,
|
|
6431
|
+
defaultHeaders: config.defaultHeaders
|
|
6432
|
+
});
|
|
6433
|
+
this.providerName = "openai-compatible";
|
|
6434
|
+
}
|
|
6435
|
+
};
|
|
6436
|
+
function signRequest(req) {
|
|
6437
|
+
const now = /* @__PURE__ */ new Date();
|
|
6438
|
+
const dateStamp = toDateStamp(now);
|
|
6439
|
+
const amzDate = toAmzDate(now);
|
|
6440
|
+
const parsed = new URL(req.url);
|
|
6441
|
+
const host = parsed.host;
|
|
6442
|
+
const canonicalUri = parsed.pathname || "/";
|
|
6443
|
+
const canonicalQuerystring = sortQueryParams(parsed.searchParams);
|
|
6444
|
+
const headers = {
|
|
6445
|
+
...req.headers,
|
|
6446
|
+
host,
|
|
6447
|
+
"x-amz-date": amzDate
|
|
6448
|
+
};
|
|
6449
|
+
if (req.credentials.sessionToken) {
|
|
6450
|
+
headers["x-amz-security-token"] = req.credentials.sessionToken;
|
|
6451
|
+
}
|
|
6452
|
+
const signedHeaderKeys = Object.keys(headers).map((k) => k.toLowerCase()).sort();
|
|
6453
|
+
const canonicalHeaders = signedHeaderKeys.map((k) => `${k}:${headers[Object.keys(headers).find((h) => h.toLowerCase() === k)]?.trim()}`).join("\n") + "\n";
|
|
6454
|
+
const signedHeaders = signedHeaderKeys.join(";");
|
|
6455
|
+
const payloadHash = sha256(req.body);
|
|
6456
|
+
const canonicalRequest = [
|
|
6457
|
+
req.method,
|
|
6458
|
+
canonicalUri,
|
|
6459
|
+
canonicalQuerystring,
|
|
6460
|
+
canonicalHeaders,
|
|
6461
|
+
signedHeaders,
|
|
6462
|
+
payloadHash
|
|
6463
|
+
].join("\n");
|
|
6464
|
+
const credentialScope = `${dateStamp}/${req.region}/${req.service}/aws4_request`;
|
|
6465
|
+
const stringToSign = [
|
|
6466
|
+
"AWS4-HMAC-SHA256",
|
|
6467
|
+
amzDate,
|
|
6468
|
+
credentialScope,
|
|
6469
|
+
sha256(canonicalRequest)
|
|
6470
|
+
].join("\n");
|
|
6471
|
+
const signingKey = getSignatureKey(
|
|
6472
|
+
req.credentials.secretAccessKey,
|
|
6473
|
+
dateStamp,
|
|
6474
|
+
req.region,
|
|
6475
|
+
req.service
|
|
6476
|
+
);
|
|
6477
|
+
const signature = hmacHex(signingKey, stringToSign);
|
|
6478
|
+
const authorization = `AWS4-HMAC-SHA256 Credential=${req.credentials.accessKeyId}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signature}`;
|
|
6479
|
+
const result = {
|
|
6480
|
+
...req.headers,
|
|
6481
|
+
"x-amz-date": amzDate,
|
|
6482
|
+
Authorization: authorization
|
|
6483
|
+
};
|
|
6484
|
+
if (req.credentials.sessionToken) {
|
|
6485
|
+
result["x-amz-security-token"] = req.credentials.sessionToken;
|
|
6486
|
+
}
|
|
6487
|
+
return result;
|
|
6488
|
+
}
|
|
6489
|
+
function toDateStamp(d) {
|
|
6490
|
+
return d.toISOString().replace(/[-:T]/g, "").slice(0, 8);
|
|
6491
|
+
}
|
|
6492
|
+
function toAmzDate(d) {
|
|
6493
|
+
return d.toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "Z");
|
|
6494
|
+
}
|
|
6495
|
+
function sha256(data) {
|
|
6496
|
+
return createHash("sha256").update(data, "utf8").digest("hex");
|
|
6497
|
+
}
|
|
6498
|
+
function hmac(key, data) {
|
|
6499
|
+
return createHmac("sha256", key).update(data, "utf8").digest();
|
|
6500
|
+
}
|
|
6501
|
+
function hmacHex(key, data) {
|
|
6502
|
+
return createHmac("sha256", key).update(data, "utf8").digest("hex");
|
|
6503
|
+
}
|
|
6504
|
+
function getSignatureKey(secret, dateStamp, region, service) {
|
|
6505
|
+
const kDate = hmac(`AWS4${secret}`, dateStamp);
|
|
6506
|
+
const kRegion = hmac(kDate, region);
|
|
6507
|
+
const kService = hmac(kRegion, service);
|
|
6508
|
+
return hmac(kService, "aws4_request");
|
|
6509
|
+
}
|
|
6510
|
+
function sortQueryParams(params) {
|
|
6511
|
+
const sorted = [...params.entries()].sort(([a], [b]) => a.localeCompare(b));
|
|
6512
|
+
return sorted.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&");
|
|
6513
|
+
}
|
|
6514
|
+
|
|
6515
|
+
// src/providers/bedrock.ts
|
|
6516
|
+
var BedrockProvider = class extends BaseProvider {
|
|
6517
|
+
constructor(config) {
|
|
6518
|
+
super();
|
|
6519
|
+
this.providerName = "bedrock";
|
|
6520
|
+
this.region = config.region;
|
|
6521
|
+
this.defaultModel = config.defaultModel ?? "anthropic.claude-sonnet-4-20250514-v1:0";
|
|
6522
|
+
this.defaultHeaders = config.defaultHeaders ?? {};
|
|
6523
|
+
this.credentials = {
|
|
6524
|
+
accessKeyId: config.accessKeyId ?? process.env.AWS_ACCESS_KEY_ID ?? "",
|
|
6525
|
+
secretAccessKey: config.secretAccessKey ?? process.env.AWS_SECRET_ACCESS_KEY ?? "",
|
|
6526
|
+
sessionToken: config.sessionToken ?? process.env.AWS_SESSION_TOKEN
|
|
6527
|
+
};
|
|
6528
|
+
if (!this.credentials.accessKeyId || !this.credentials.secretAccessKey) {
|
|
6529
|
+
throw new ProviderError(
|
|
6530
|
+
"AWS credentials required: provide accessKeyId/secretAccessKey or set AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY env vars",
|
|
6531
|
+
"bedrock"
|
|
6532
|
+
);
|
|
6533
|
+
}
|
|
6534
|
+
}
|
|
6535
|
+
async complete(request) {
|
|
6536
|
+
const modelId = request.model || this.defaultModel;
|
|
6537
|
+
const body = this.buildConverseBody(request);
|
|
6538
|
+
const json = await this.doFetch(modelId, body);
|
|
6539
|
+
return this.parseResponse(json, modelId);
|
|
6540
|
+
}
|
|
6541
|
+
async *completeStream(request) {
|
|
6542
|
+
const response = await this.complete(request);
|
|
6543
|
+
yield {
|
|
6544
|
+
id: response.id,
|
|
6545
|
+
model: response.model,
|
|
6546
|
+
delta: {
|
|
6547
|
+
content: response.content ?? void 0,
|
|
6548
|
+
toolCalls: response.toolCalls.length > 0 ? response.toolCalls : void 0
|
|
6549
|
+
},
|
|
6550
|
+
usage: response.usage,
|
|
6551
|
+
finishReason: response.finishReason
|
|
6552
|
+
};
|
|
6553
|
+
}
|
|
6554
|
+
// ─── Internals ───────────────────────────────────────────────────────────
|
|
6555
|
+
buildConverseBody(request) {
|
|
6556
|
+
const { system, messages } = this.splitSystemMessages(request.messages);
|
|
6557
|
+
const body = {
|
|
6558
|
+
messages: messages.map((m) => this.toBedrockMessage(m))
|
|
6559
|
+
};
|
|
6560
|
+
if (system.length > 0) {
|
|
6561
|
+
body.system = system.map((s) => ({ text: s }));
|
|
6562
|
+
}
|
|
6563
|
+
const inferenceConfig = {};
|
|
6564
|
+
if (request.maxTokens != null) inferenceConfig.maxTokens = request.maxTokens;
|
|
6565
|
+
if (request.temperature != null) inferenceConfig.temperature = request.temperature;
|
|
6566
|
+
if (request.topP != null) inferenceConfig.topP = request.topP;
|
|
6567
|
+
if (request.stop) inferenceConfig.stopSequences = request.stop;
|
|
6568
|
+
if (Object.keys(inferenceConfig).length > 0) body.inferenceConfig = inferenceConfig;
|
|
6569
|
+
if (request.tools?.length) {
|
|
6570
|
+
body.toolConfig = {
|
|
6571
|
+
tools: request.tools.map((t) => this.toBedrockTool(t))
|
|
6572
|
+
};
|
|
6573
|
+
if (request.toolChoice != null) {
|
|
6574
|
+
if (request.toolChoice === "auto") {
|
|
6575
|
+
body.toolConfig.toolChoice = { auto: {} };
|
|
6576
|
+
} else if (request.toolChoice === "required") {
|
|
6577
|
+
body.toolConfig.toolChoice = { any: {} };
|
|
6578
|
+
} else if (typeof request.toolChoice === "object") {
|
|
6579
|
+
body.toolConfig.toolChoice = { tool: { name: request.toolChoice.name } };
|
|
6580
|
+
}
|
|
6581
|
+
}
|
|
6582
|
+
}
|
|
6583
|
+
if (request.extra) {
|
|
6584
|
+
Object.assign(body, request.extra);
|
|
6585
|
+
}
|
|
6586
|
+
return body;
|
|
6587
|
+
}
|
|
6588
|
+
splitSystemMessages(messages) {
|
|
6589
|
+
const system = [];
|
|
6590
|
+
const rest = [];
|
|
6591
|
+
for (const m of messages) {
|
|
6592
|
+
if (m.role === "system") {
|
|
6593
|
+
const text = typeof m.content === "string" ? m.content : m.content.filter((p) => p.type === "text").map((p) => p.text).join("\n");
|
|
6594
|
+
if (text) system.push(text);
|
|
6595
|
+
} else {
|
|
6596
|
+
rest.push(m);
|
|
6597
|
+
}
|
|
6598
|
+
}
|
|
6599
|
+
return { system, messages: rest };
|
|
6600
|
+
}
|
|
6601
|
+
toBedrockMessage(m) {
|
|
6602
|
+
if (m.role === "tool" && m.toolCallId) {
|
|
6603
|
+
const text = typeof m.content === "string" ? m.content : JSON.stringify(m.content);
|
|
6604
|
+
return {
|
|
6605
|
+
role: "user",
|
|
6606
|
+
content: [
|
|
6607
|
+
{
|
|
6608
|
+
toolResult: {
|
|
6609
|
+
toolUseId: m.toolCallId,
|
|
6610
|
+
content: [{ text }],
|
|
6611
|
+
status: "success"
|
|
6612
|
+
}
|
|
6613
|
+
}
|
|
6614
|
+
]
|
|
6615
|
+
};
|
|
6616
|
+
}
|
|
6617
|
+
if (m.role === "assistant" && m.toolCalls?.length) {
|
|
6618
|
+
const content2 = [];
|
|
6619
|
+
if (typeof m.content === "string" && m.content) {
|
|
6620
|
+
content2.push({ text: m.content });
|
|
6621
|
+
}
|
|
6622
|
+
for (const tc of m.toolCalls) {
|
|
6623
|
+
content2.push({
|
|
6624
|
+
toolUse: {
|
|
6625
|
+
toolUseId: tc.id,
|
|
6626
|
+
name: tc.name,
|
|
6627
|
+
input: safeJsonParse2(tc.arguments)
|
|
6628
|
+
}
|
|
6629
|
+
});
|
|
6630
|
+
}
|
|
6631
|
+
return { role: "assistant", content: content2 };
|
|
6632
|
+
}
|
|
6633
|
+
const content = [];
|
|
6634
|
+
if (typeof m.content === "string") {
|
|
6635
|
+
content.push({ text: m.content });
|
|
6636
|
+
} else if (Array.isArray(m.content)) {
|
|
6637
|
+
for (const part of m.content) {
|
|
6638
|
+
if (part.type === "text") {
|
|
6639
|
+
content.push({ text: part.text });
|
|
6640
|
+
} else if (part.type === "image") {
|
|
6641
|
+
const src = part.source;
|
|
6642
|
+
if (src.type === "base64") {
|
|
6643
|
+
content.push({
|
|
6644
|
+
image: {
|
|
6645
|
+
format: mediaTypeToFormat(src.mediaType),
|
|
6646
|
+
source: { bytes: src.data }
|
|
6647
|
+
}
|
|
6648
|
+
});
|
|
6649
|
+
}
|
|
6650
|
+
}
|
|
6651
|
+
}
|
|
6652
|
+
}
|
|
6653
|
+
return { role: m.role === "user" ? "user" : "assistant", content };
|
|
6654
|
+
}
|
|
6655
|
+
toBedrockTool(t) {
|
|
6656
|
+
return {
|
|
6657
|
+
toolSpec: {
|
|
6658
|
+
name: t.name,
|
|
6659
|
+
description: t.description,
|
|
6660
|
+
inputSchema: { json: t.parameters }
|
|
6661
|
+
}
|
|
6662
|
+
};
|
|
6663
|
+
}
|
|
6664
|
+
async doFetch(modelId, body) {
|
|
6665
|
+
const host = `bedrock-runtime.${this.region}.amazonaws.com`;
|
|
6666
|
+
const url = `https://${host}/model/${encodeURIComponent(modelId)}/converse`;
|
|
6667
|
+
const bodyStr = JSON.stringify(body);
|
|
6668
|
+
const headers = signRequest({
|
|
6669
|
+
method: "POST",
|
|
6670
|
+
url,
|
|
6671
|
+
headers: {
|
|
6672
|
+
"Content-Type": "application/json",
|
|
6673
|
+
Accept: "application/json",
|
|
6674
|
+
...this.defaultHeaders
|
|
6675
|
+
},
|
|
6676
|
+
body: bodyStr,
|
|
6677
|
+
region: this.region,
|
|
6678
|
+
service: "bedrock",
|
|
6679
|
+
credentials: this.credentials
|
|
6680
|
+
});
|
|
6681
|
+
const res = await fetch(url, {
|
|
6682
|
+
method: "POST",
|
|
6683
|
+
headers,
|
|
6684
|
+
body: bodyStr
|
|
6685
|
+
});
|
|
6686
|
+
if (!res.ok) {
|
|
6687
|
+
const text = await res.text().catch(() => "");
|
|
6688
|
+
throw new ProviderError(
|
|
6689
|
+
`Bedrock API error ${res.status}: ${text}`,
|
|
6690
|
+
this.providerName,
|
|
6691
|
+
res.status,
|
|
6692
|
+
text
|
|
6693
|
+
);
|
|
6694
|
+
}
|
|
6695
|
+
return res.json();
|
|
6696
|
+
}
|
|
6697
|
+
parseResponse(json, modelId) {
|
|
6698
|
+
const output = json.output?.message;
|
|
6699
|
+
const textParts = [];
|
|
6700
|
+
const toolCalls = [];
|
|
6701
|
+
for (const block of output?.content ?? []) {
|
|
6702
|
+
if (block.text) {
|
|
6703
|
+
textParts.push(block.text);
|
|
6704
|
+
} else if (block.toolUse) {
|
|
6705
|
+
toolCalls.push({
|
|
6706
|
+
id: block.toolUse.toolUseId,
|
|
6707
|
+
name: block.toolUse.name,
|
|
6708
|
+
arguments: typeof block.toolUse.input === "string" ? block.toolUse.input : JSON.stringify(block.toolUse.input ?? {})
|
|
6709
|
+
});
|
|
6710
|
+
}
|
|
6711
|
+
}
|
|
6712
|
+
const usage = json.usage ?? {};
|
|
6713
|
+
const tokenUsage = {
|
|
6714
|
+
inputTokens: usage.inputTokens ?? 0,
|
|
6715
|
+
outputTokens: usage.outputTokens ?? 0,
|
|
6716
|
+
totalTokens: usage.totalTokens ?? (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0),
|
|
6717
|
+
cachedInputTokens: usage.cacheReadInputTokenCount ?? 0,
|
|
6718
|
+
cachedWriteTokens: usage.cacheWriteInputTokenCount ?? 0,
|
|
6719
|
+
cachedOutputTokens: 0,
|
|
6720
|
+
reasoningTokens: 0
|
|
6721
|
+
};
|
|
6722
|
+
return {
|
|
6723
|
+
id: json.requestId ?? json.$metadata?.requestId ?? "",
|
|
6724
|
+
model: modelId,
|
|
6725
|
+
content: textParts.join("") || null,
|
|
6726
|
+
toolCalls,
|
|
6727
|
+
usage: tokenUsage,
|
|
6728
|
+
finishReason: mapBedrockStopReason(json.stopReason),
|
|
6729
|
+
raw: json
|
|
6730
|
+
};
|
|
6731
|
+
}
|
|
6732
|
+
};
|
|
6733
|
+
function mapBedrockStopReason(reason) {
|
|
6734
|
+
switch (reason) {
|
|
6735
|
+
case "end_turn":
|
|
6736
|
+
case "stop_sequence":
|
|
6737
|
+
return "stop";
|
|
6738
|
+
case "tool_use":
|
|
6739
|
+
return "tool_calls";
|
|
6740
|
+
case "max_tokens":
|
|
6741
|
+
return "length";
|
|
6742
|
+
case "content_filtered":
|
|
6743
|
+
case "guardrail":
|
|
6744
|
+
return "content_filter";
|
|
6745
|
+
default:
|
|
6746
|
+
return "stop";
|
|
6747
|
+
}
|
|
6748
|
+
}
|
|
6749
|
+
function mediaTypeToFormat(mediaType) {
|
|
6750
|
+
const map = {
|
|
6751
|
+
"image/jpeg": "jpeg",
|
|
6752
|
+
"image/png": "png",
|
|
6753
|
+
"image/gif": "gif",
|
|
6754
|
+
"image/webp": "webp"
|
|
6755
|
+
};
|
|
6756
|
+
return map[mediaType] ?? "png";
|
|
6757
|
+
}
|
|
6758
|
+
function safeJsonParse2(str) {
|
|
6759
|
+
try {
|
|
6760
|
+
return JSON.parse(str);
|
|
6761
|
+
} catch {
|
|
6762
|
+
return {};
|
|
6763
|
+
}
|
|
6764
|
+
}
|
|
6765
|
+
|
|
6766
|
+
// src/providers/vertex.ts
|
|
6767
|
+
var VertexProvider = class extends BaseProvider {
|
|
6768
|
+
constructor(config) {
|
|
6769
|
+
super();
|
|
6770
|
+
this.providerName = "vertex";
|
|
6771
|
+
this.tokenExpiry = 0;
|
|
6772
|
+
this.projectId = config.projectId;
|
|
6773
|
+
this.location = config.location ?? "us-central1";
|
|
6774
|
+
this.defaultModel = config.defaultModel ?? "gemini-2.0-flash";
|
|
6775
|
+
this.defaultHeaders = config.defaultHeaders ?? {};
|
|
6776
|
+
if (config.accessToken) {
|
|
6777
|
+
this.accessToken = config.accessToken;
|
|
6778
|
+
this.tokenExpiry = Date.now() + 36e5;
|
|
6779
|
+
}
|
|
6780
|
+
if (config.serviceAccountJson) {
|
|
6781
|
+
this.serviceAccountJson = typeof config.serviceAccountJson === "string" ? JSON.parse(config.serviceAccountJson) : config.serviceAccountJson;
|
|
6782
|
+
}
|
|
6783
|
+
}
|
|
6784
|
+
async complete(request) {
|
|
6785
|
+
const modelId = request.model || this.defaultModel;
|
|
6786
|
+
const body = this.buildGeminiBody(request);
|
|
6787
|
+
const url = this.buildUrl(modelId, false);
|
|
6788
|
+
const res = await this.doFetch(url, body);
|
|
6789
|
+
const json = await res.json();
|
|
6790
|
+
return this.parseResponse(json, modelId);
|
|
6791
|
+
}
|
|
6792
|
+
async *completeStream(request) {
|
|
6793
|
+
const modelId = request.model || this.defaultModel;
|
|
6794
|
+
const body = this.buildGeminiBody(request);
|
|
6795
|
+
const url = this.buildUrl(modelId, true);
|
|
6796
|
+
const res = await this.doFetch(url, body);
|
|
6797
|
+
if (!res.body) throw new ProviderError("No response body for stream", this.providerName);
|
|
6798
|
+
const contentType = res.headers.get("content-type") ?? "";
|
|
6799
|
+
if (contentType.includes("text/event-stream")) {
|
|
6800
|
+
for await (const event of parseSSEStream(res.body)) {
|
|
6801
|
+
if (event.data === "[DONE]") break;
|
|
6802
|
+
let chunk;
|
|
6803
|
+
try {
|
|
6804
|
+
chunk = JSON.parse(event.data);
|
|
6805
|
+
} catch {
|
|
6806
|
+
continue;
|
|
6807
|
+
}
|
|
6808
|
+
yield this.parseStreamChunk(chunk, modelId);
|
|
6809
|
+
}
|
|
6810
|
+
} else {
|
|
6811
|
+
const reader = res.body.getReader();
|
|
6812
|
+
const decoder = new TextDecoder();
|
|
6813
|
+
let buffer = "";
|
|
6814
|
+
try {
|
|
6815
|
+
while (true) {
|
|
6816
|
+
const { done, value } = await reader.read();
|
|
6817
|
+
if (done) break;
|
|
6818
|
+
buffer += decoder.decode(value, { stream: true });
|
|
6819
|
+
const lines = buffer.split("\n");
|
|
6820
|
+
buffer = lines.pop() ?? "";
|
|
6821
|
+
for (const line of lines) {
|
|
6822
|
+
const trimmed = line.trim().replace(/^,/, "").replace(/^\[/, "").replace(/\]$/, "");
|
|
6823
|
+
if (!trimmed || trimmed === "[" || trimmed === "]") continue;
|
|
6824
|
+
try {
|
|
6825
|
+
const chunk = JSON.parse(trimmed);
|
|
6826
|
+
yield this.parseStreamChunk(chunk, modelId);
|
|
6827
|
+
} catch {
|
|
6828
|
+
buffer = line + "\n" + buffer;
|
|
6829
|
+
break;
|
|
6830
|
+
}
|
|
6831
|
+
}
|
|
6832
|
+
}
|
|
6833
|
+
const remaining = buffer.trim().replace(/^,/, "").replace(/^\[/, "").replace(/\]$/, "");
|
|
6834
|
+
if (remaining && remaining !== "[" && remaining !== "]") {
|
|
6835
|
+
try {
|
|
6836
|
+
const chunk = JSON.parse(remaining);
|
|
6837
|
+
yield this.parseStreamChunk(chunk, modelId);
|
|
6838
|
+
} catch {
|
|
6839
|
+
}
|
|
6840
|
+
}
|
|
6841
|
+
} finally {
|
|
6842
|
+
reader.releaseLock();
|
|
6843
|
+
}
|
|
6844
|
+
}
|
|
6845
|
+
}
|
|
6846
|
+
// ─── Internals ───────────────────────────────────────────────────────────
|
|
6847
|
+
buildUrl(modelId, stream) {
|
|
6848
|
+
const base = `https://${this.location}-aiplatform.googleapis.com/v1`;
|
|
6849
|
+
const path2 = `projects/${this.projectId}/locations/${this.location}/publishers/google/models/${modelId}`;
|
|
6850
|
+
const action = stream ? "streamGenerateContent?alt=sse" : "generateContent";
|
|
6851
|
+
return `${base}/${path2}:${action}`;
|
|
6852
|
+
}
|
|
6853
|
+
buildGeminiBody(request) {
|
|
6854
|
+
const { system, contents } = this.buildContents(request.messages);
|
|
6855
|
+
const body = { contents };
|
|
6856
|
+
if (system) {
|
|
6857
|
+
body.systemInstruction = { parts: [{ text: system }] };
|
|
6858
|
+
}
|
|
6859
|
+
const generationConfig = {};
|
|
6860
|
+
if (request.maxTokens != null) generationConfig.maxOutputTokens = request.maxTokens;
|
|
6861
|
+
if (request.temperature != null) generationConfig.temperature = request.temperature;
|
|
6862
|
+
if (request.topP != null) generationConfig.topP = request.topP;
|
|
6863
|
+
if (request.stop) generationConfig.stopSequences = request.stop;
|
|
6864
|
+
if (request.responseFormat) {
|
|
6865
|
+
if (request.responseFormat.type === "json_schema" || request.responseFormat.type === "json_object") {
|
|
6866
|
+
generationConfig.responseMimeType = "application/json";
|
|
6867
|
+
if (request.responseFormat.schema) {
|
|
6868
|
+
generationConfig.responseSchema = request.responseFormat.schema;
|
|
6869
|
+
}
|
|
6870
|
+
}
|
|
6871
|
+
}
|
|
6872
|
+
if (Object.keys(generationConfig).length > 0) body.generationConfig = generationConfig;
|
|
6873
|
+
if (request.tools?.length) {
|
|
6874
|
+
body.tools = [
|
|
6875
|
+
{
|
|
6876
|
+
functionDeclarations: request.tools.map((t) => this.toGeminiTool(t))
|
|
6877
|
+
}
|
|
6878
|
+
];
|
|
6879
|
+
if (request.toolChoice != null) {
|
|
6880
|
+
const mode = request.toolChoice === "auto" ? "AUTO" : request.toolChoice === "required" ? "ANY" : request.toolChoice === "none" ? "NONE" : "AUTO";
|
|
6881
|
+
body.toolConfig = { functionCallingConfig: { mode } };
|
|
6882
|
+
}
|
|
6883
|
+
}
|
|
6884
|
+
if (request.extra) {
|
|
6885
|
+
Object.assign(body, request.extra);
|
|
6886
|
+
}
|
|
6887
|
+
return body;
|
|
6888
|
+
}
|
|
6889
|
+
buildContents(messages) {
|
|
6890
|
+
const systemParts = [];
|
|
6891
|
+
const contents = [];
|
|
6892
|
+
for (const m of messages) {
|
|
6893
|
+
if (m.role === "system") {
|
|
6894
|
+
const text = typeof m.content === "string" ? m.content : m.content.filter((p) => p.type === "text").map((p) => p.text).join("\n");
|
|
6895
|
+
if (text) systemParts.push(text);
|
|
6896
|
+
continue;
|
|
6897
|
+
}
|
|
6898
|
+
contents.push(this.toGeminiContent(m));
|
|
6899
|
+
}
|
|
6900
|
+
return {
|
|
6901
|
+
system: systemParts.length > 0 ? systemParts.join("\n\n") : void 0,
|
|
6902
|
+
contents
|
|
6903
|
+
};
|
|
6904
|
+
}
|
|
6905
|
+
toGeminiContent(m) {
|
|
6906
|
+
if (m.role === "tool" && m.toolCallId) {
|
|
6907
|
+
const text = typeof m.content === "string" ? m.content : JSON.stringify(m.content);
|
|
6908
|
+
let response;
|
|
6909
|
+
try {
|
|
6910
|
+
response = JSON.parse(text);
|
|
6911
|
+
} catch {
|
|
6912
|
+
response = { result: text };
|
|
6913
|
+
}
|
|
6914
|
+
return {
|
|
6915
|
+
role: "user",
|
|
6916
|
+
parts: [
|
|
6917
|
+
{
|
|
6918
|
+
functionResponse: {
|
|
6919
|
+
name: m.name ?? m.toolCallId,
|
|
6920
|
+
response
|
|
6921
|
+
}
|
|
6922
|
+
}
|
|
6923
|
+
]
|
|
6924
|
+
};
|
|
6925
|
+
}
|
|
6926
|
+
if (m.role === "assistant" && m.toolCalls?.length) {
|
|
6927
|
+
const parts2 = [];
|
|
6928
|
+
if (typeof m.content === "string" && m.content) {
|
|
6929
|
+
parts2.push({ text: m.content });
|
|
6930
|
+
}
|
|
6931
|
+
for (const tc of m.toolCalls) {
|
|
6932
|
+
parts2.push({
|
|
6933
|
+
functionCall: {
|
|
6934
|
+
name: tc.name,
|
|
6935
|
+
args: safeJsonParse3(tc.arguments)
|
|
6936
|
+
}
|
|
6937
|
+
});
|
|
6938
|
+
}
|
|
6939
|
+
return { role: "model", parts: parts2 };
|
|
6940
|
+
}
|
|
6941
|
+
const role = m.role === "assistant" ? "model" : "user";
|
|
6942
|
+
const parts = [];
|
|
6943
|
+
if (typeof m.content === "string") {
|
|
6944
|
+
parts.push({ text: m.content });
|
|
6945
|
+
} else if (Array.isArray(m.content)) {
|
|
6946
|
+
for (const part of m.content) {
|
|
6947
|
+
if (part.type === "text") {
|
|
6948
|
+
parts.push({ text: part.text });
|
|
6949
|
+
} else if (part.type === "image") {
|
|
6950
|
+
const src = part.source;
|
|
6951
|
+
if (src.type === "base64") {
|
|
6952
|
+
parts.push({
|
|
6953
|
+
inlineData: { mimeType: src.mediaType, data: src.data }
|
|
6954
|
+
});
|
|
6955
|
+
} else {
|
|
6956
|
+
parts.push({
|
|
6957
|
+
fileData: { mimeType: "image/jpeg", fileUri: src.url }
|
|
6958
|
+
});
|
|
6959
|
+
}
|
|
6960
|
+
}
|
|
6961
|
+
}
|
|
6962
|
+
}
|
|
6963
|
+
return { role, parts };
|
|
6964
|
+
}
|
|
6965
|
+
toGeminiTool(t) {
|
|
6966
|
+
return {
|
|
6967
|
+
name: t.name,
|
|
6968
|
+
description: t.description,
|
|
6969
|
+
parameters: t.parameters
|
|
6970
|
+
};
|
|
6971
|
+
}
|
|
6972
|
+
async getAccessToken() {
|
|
6973
|
+
if (this.accessToken && Date.now() < this.tokenExpiry) {
|
|
6974
|
+
return this.accessToken;
|
|
6975
|
+
}
|
|
6976
|
+
if (this.serviceAccountJson) {
|
|
6977
|
+
const token = await this.getTokenFromServiceAccount(this.serviceAccountJson);
|
|
6978
|
+
this.accessToken = token;
|
|
6979
|
+
this.tokenExpiry = Date.now() + 35e5;
|
|
6980
|
+
return token;
|
|
6981
|
+
}
|
|
6982
|
+
try {
|
|
6983
|
+
const { execSync } = await import('child_process');
|
|
6984
|
+
const token = execSync("gcloud auth print-access-token", { encoding: "utf8" }).trim();
|
|
6985
|
+
this.accessToken = token;
|
|
6986
|
+
this.tokenExpiry = Date.now() + 35e5;
|
|
6987
|
+
return token;
|
|
6988
|
+
} catch {
|
|
6989
|
+
throw new ProviderError(
|
|
6990
|
+
"No access token available. Provide accessToken, serviceAccountJson, or ensure gcloud CLI is authenticated.",
|
|
6991
|
+
this.providerName
|
|
6992
|
+
);
|
|
6993
|
+
}
|
|
6994
|
+
}
|
|
6995
|
+
async getTokenFromServiceAccount(sa) {
|
|
6996
|
+
const { createSign } = await import('crypto');
|
|
6997
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
6998
|
+
const header = { alg: "RS256", typ: "JWT" };
|
|
6999
|
+
const claims = {
|
|
7000
|
+
iss: sa.client_email,
|
|
7001
|
+
scope: "https://www.googleapis.com/auth/cloud-platform",
|
|
7002
|
+
aud: "https://oauth2.googleapis.com/token",
|
|
7003
|
+
iat: now,
|
|
7004
|
+
exp: now + 3600
|
|
7005
|
+
};
|
|
7006
|
+
const segments = [
|
|
7007
|
+
base64url(JSON.stringify(header)),
|
|
7008
|
+
base64url(JSON.stringify(claims))
|
|
7009
|
+
];
|
|
7010
|
+
const signingInput = segments.join(".");
|
|
7011
|
+
const sign = createSign("RSA-SHA256");
|
|
7012
|
+
sign.update(signingInput);
|
|
7013
|
+
const signature = sign.sign(sa.private_key, "base64url");
|
|
7014
|
+
const jwt = `${signingInput}.${signature}`;
|
|
7015
|
+
const tokenRes = await fetch("https://oauth2.googleapis.com/token", {
|
|
7016
|
+
method: "POST",
|
|
7017
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
7018
|
+
body: `grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=${jwt}`
|
|
7019
|
+
});
|
|
7020
|
+
if (!tokenRes.ok) {
|
|
7021
|
+
const text = await tokenRes.text().catch(() => "");
|
|
7022
|
+
throw new ProviderError(`Failed to get Vertex access token: ${text}`, this.providerName);
|
|
7023
|
+
}
|
|
7024
|
+
const tokenJson = await tokenRes.json();
|
|
7025
|
+
return tokenJson.access_token;
|
|
7026
|
+
}
|
|
7027
|
+
async doFetch(url, body) {
|
|
7028
|
+
const token = await this.getAccessToken();
|
|
7029
|
+
const res = await fetch(url, {
|
|
7030
|
+
method: "POST",
|
|
7031
|
+
headers: {
|
|
7032
|
+
"Content-Type": "application/json",
|
|
7033
|
+
Authorization: `Bearer ${token}`,
|
|
7034
|
+
...this.defaultHeaders
|
|
7035
|
+
},
|
|
7036
|
+
body: JSON.stringify(body)
|
|
7037
|
+
});
|
|
7038
|
+
if (!res.ok) {
|
|
7039
|
+
const text = await res.text().catch(() => "");
|
|
7040
|
+
throw new ProviderError(
|
|
7041
|
+
`Vertex AI API error ${res.status}: ${text}`,
|
|
7042
|
+
this.providerName,
|
|
7043
|
+
res.status,
|
|
7044
|
+
text
|
|
7045
|
+
);
|
|
7046
|
+
}
|
|
7047
|
+
return res;
|
|
7048
|
+
}
|
|
7049
|
+
parseResponse(json, modelId) {
|
|
7050
|
+
const candidate = json.candidates?.[0];
|
|
7051
|
+
const parts = candidate?.content?.parts ?? [];
|
|
7052
|
+
const textParts = [];
|
|
7053
|
+
const toolCalls = [];
|
|
7054
|
+
let toolCallIdx = 0;
|
|
7055
|
+
for (const part of parts) {
|
|
7056
|
+
if (part.text) {
|
|
7057
|
+
textParts.push(part.text);
|
|
7058
|
+
} else if (part.functionCall) {
|
|
7059
|
+
toolCalls.push({
|
|
7060
|
+
id: `call_${toolCallIdx++}`,
|
|
7061
|
+
name: part.functionCall.name,
|
|
7062
|
+
arguments: JSON.stringify(part.functionCall.args ?? {})
|
|
7063
|
+
});
|
|
7064
|
+
}
|
|
7065
|
+
}
|
|
7066
|
+
const usageMeta = json.usageMetadata ?? {};
|
|
7067
|
+
const tokenUsage = {
|
|
7068
|
+
inputTokens: usageMeta.promptTokenCount ?? 0,
|
|
7069
|
+
outputTokens: usageMeta.candidatesTokenCount ?? 0,
|
|
7070
|
+
totalTokens: usageMeta.totalTokenCount ?? 0,
|
|
7071
|
+
cachedInputTokens: usageMeta.cachedContentTokenCount ?? 0,
|
|
7072
|
+
cachedWriteTokens: 0,
|
|
7073
|
+
cachedOutputTokens: 0,
|
|
7074
|
+
reasoningTokens: usageMeta.thoughtsTokenCount ?? 0
|
|
7075
|
+
};
|
|
7076
|
+
return {
|
|
7077
|
+
id: json.responseId ?? "",
|
|
7078
|
+
model: modelId,
|
|
7079
|
+
content: textParts.join("") || null,
|
|
7080
|
+
toolCalls,
|
|
7081
|
+
usage: tokenUsage,
|
|
7082
|
+
finishReason: mapGeminiFinishReason(candidate?.finishReason),
|
|
7083
|
+
raw: json
|
|
7084
|
+
};
|
|
7085
|
+
}
|
|
7086
|
+
parseStreamChunk(chunk, modelId) {
|
|
7087
|
+
const candidate = chunk.candidates?.[0];
|
|
7088
|
+
const parts = candidate?.content?.parts ?? [];
|
|
7089
|
+
const delta = {};
|
|
7090
|
+
for (const part of parts) {
|
|
7091
|
+
if (part.text) {
|
|
7092
|
+
delta.content = (delta.content ?? "") + part.text;
|
|
7093
|
+
} else if (part.functionCall) {
|
|
7094
|
+
if (!delta.toolCalls) delta.toolCalls = [];
|
|
7095
|
+
delta.toolCalls.push({
|
|
7096
|
+
id: `call_${delta.toolCalls.length}`,
|
|
7097
|
+
name: part.functionCall.name,
|
|
7098
|
+
arguments: JSON.stringify(part.functionCall.args ?? {})
|
|
7099
|
+
});
|
|
7100
|
+
}
|
|
7101
|
+
}
|
|
7102
|
+
let usage;
|
|
7103
|
+
if (chunk.usageMetadata) {
|
|
7104
|
+
const u = chunk.usageMetadata;
|
|
7105
|
+
usage = {
|
|
7106
|
+
inputTokens: u.promptTokenCount ?? 0,
|
|
7107
|
+
outputTokens: u.candidatesTokenCount ?? 0,
|
|
7108
|
+
totalTokens: u.totalTokenCount ?? 0,
|
|
7109
|
+
cachedInputTokens: u.cachedContentTokenCount ?? 0,
|
|
7110
|
+
cachedWriteTokens: 0,
|
|
7111
|
+
cachedOutputTokens: 0,
|
|
7112
|
+
reasoningTokens: u.thoughtsTokenCount ?? 0
|
|
7113
|
+
};
|
|
7114
|
+
}
|
|
7115
|
+
return {
|
|
7116
|
+
id: chunk.responseId ?? "",
|
|
7117
|
+
model: modelId,
|
|
7118
|
+
delta,
|
|
7119
|
+
usage,
|
|
7120
|
+
finishReason: mapGeminiFinishReason(candidate?.finishReason)
|
|
7121
|
+
};
|
|
7122
|
+
}
|
|
7123
|
+
};
|
|
7124
|
+
function mapGeminiFinishReason(reason) {
|
|
7125
|
+
switch (reason) {
|
|
7126
|
+
case "STOP":
|
|
7127
|
+
return "stop";
|
|
7128
|
+
case "MAX_TOKENS":
|
|
7129
|
+
return "length";
|
|
7130
|
+
case "SAFETY":
|
|
7131
|
+
case "RECITATION":
|
|
7132
|
+
return "content_filter";
|
|
7133
|
+
case "TOOL_CALLS":
|
|
7134
|
+
return "tool_calls";
|
|
7135
|
+
default:
|
|
7136
|
+
return "stop";
|
|
7137
|
+
}
|
|
7138
|
+
}
|
|
7139
|
+
function base64url(str) {
|
|
7140
|
+
return Buffer.from(str).toString("base64url");
|
|
7141
|
+
}
|
|
7142
|
+
function safeJsonParse3(str) {
|
|
7143
|
+
try {
|
|
7144
|
+
return JSON.parse(str);
|
|
7145
|
+
} catch {
|
|
7146
|
+
return {};
|
|
7147
|
+
}
|
|
7148
|
+
}
|
|
7149
|
+
function fromNativeProvider(provider, options) {
|
|
7150
|
+
let boundTools = options?.__tools;
|
|
7151
|
+
const modelName = options?.model ?? provider.defaultModel ?? provider.providerName;
|
|
7152
|
+
const adapted = {
|
|
7153
|
+
modelName,
|
|
7154
|
+
capabilities: resolveCapabilities(provider.providerName),
|
|
7155
|
+
invoke: async (messages, _invokeOptions) => {
|
|
7156
|
+
const request = buildRequest(messages, boundTools, options, _invokeOptions);
|
|
7157
|
+
const response = await provider.complete(request);
|
|
7158
|
+
return toBaseChatMessage(response);
|
|
7159
|
+
},
|
|
7160
|
+
stream: async function* (messages, _invokeOptions) {
|
|
7161
|
+
const request = buildRequest(messages, boundTools, options, _invokeOptions);
|
|
7162
|
+
request.stream = true;
|
|
7163
|
+
let fullContent = "";
|
|
7164
|
+
let allToolCalls = [];
|
|
7165
|
+
let lastUsage;
|
|
7166
|
+
const toolCallBuffers = /* @__PURE__ */ new Map();
|
|
7167
|
+
for await (const chunk of provider.completeStream(request)) {
|
|
7168
|
+
if (chunk.delta.content) {
|
|
7169
|
+
fullContent += chunk.delta.content;
|
|
7170
|
+
yield chunk.delta.content;
|
|
7171
|
+
}
|
|
7172
|
+
if (chunk.delta.toolCalls) {
|
|
7173
|
+
for (const tc of chunk.delta.toolCalls) {
|
|
7174
|
+
if (tc.id) {
|
|
7175
|
+
const existing = toolCallBuffers.get(tc.id);
|
|
7176
|
+
if (existing) {
|
|
7177
|
+
if (tc.arguments) existing.arguments += tc.arguments;
|
|
7178
|
+
} else {
|
|
7179
|
+
toolCallBuffers.set(tc.id, {
|
|
7180
|
+
id: tc.id,
|
|
7181
|
+
name: tc.name ?? "",
|
|
7182
|
+
arguments: tc.arguments ?? ""
|
|
7183
|
+
});
|
|
7184
|
+
}
|
|
7185
|
+
}
|
|
7186
|
+
}
|
|
7187
|
+
}
|
|
7188
|
+
if (chunk.usage) lastUsage = chunk.usage;
|
|
7189
|
+
}
|
|
7190
|
+
allToolCalls = [...toolCallBuffers.values()];
|
|
7191
|
+
const finalMessage = assembleMessage(fullContent, allToolCalls, lastUsage);
|
|
7192
|
+
yield finalMessage;
|
|
7193
|
+
},
|
|
7194
|
+
bindTools: (tools, _bindOptions) => {
|
|
7195
|
+
const strict = _bindOptions?.strict ?? false;
|
|
7196
|
+
const toolDefs = tools.map((t) => toToolDefinition(t, strict));
|
|
7197
|
+
return fromNativeProvider(provider, { ...options, __tools: toolDefs });
|
|
7198
|
+
}
|
|
7199
|
+
};
|
|
7200
|
+
return adapted;
|
|
7201
|
+
function buildRequest(messages, tools, opts, invokeOptions) {
|
|
7202
|
+
const req = {
|
|
7203
|
+
model: opts?.model ?? modelName,
|
|
7204
|
+
messages: messages.map(toUnifiedMessage)
|
|
7205
|
+
};
|
|
7206
|
+
if (opts?.temperature != null) req.temperature = opts.temperature;
|
|
7207
|
+
if (opts?.maxTokens != null) req.maxTokens = opts.maxTokens;
|
|
7208
|
+
if (tools?.length) req.tools = tools;
|
|
7209
|
+
if (opts?.extra) req.extra = opts.extra;
|
|
7210
|
+
const rf = invokeOptions?.response_format;
|
|
7211
|
+
if (rf && typeof rf === "object" && rf.type) {
|
|
7212
|
+
if (rf.type === "json_schema" && rf.json_schema) {
|
|
7213
|
+
req.responseFormat = {
|
|
7214
|
+
type: "json_schema",
|
|
7215
|
+
schema: rf.json_schema.schema,
|
|
7216
|
+
name: rf.json_schema.name
|
|
7217
|
+
};
|
|
7218
|
+
} else if (rf.type === "json_object") {
|
|
7219
|
+
req.responseFormat = { type: "json_object" };
|
|
7220
|
+
} else if (rf.type === "text") {
|
|
7221
|
+
req.responseFormat = { type: "text" };
|
|
7222
|
+
}
|
|
7223
|
+
}
|
|
7224
|
+
if (!req.responseFormat) {
|
|
7225
|
+
const lastMsg = messages[messages.length - 1];
|
|
7226
|
+
if (lastMsg && lastMsg.__responseFormat) {
|
|
7227
|
+
req.responseFormat = lastMsg.__responseFormat;
|
|
7228
|
+
}
|
|
7229
|
+
}
|
|
7230
|
+
return req;
|
|
7231
|
+
}
|
|
7232
|
+
}
|
|
7233
|
+
function toUnifiedMessage(msg) {
|
|
7234
|
+
const unified = {
|
|
7235
|
+
role: msg.role,
|
|
7236
|
+
content: convertContent(msg.content)
|
|
7237
|
+
};
|
|
7238
|
+
if (msg.name) unified.name = msg.name;
|
|
7239
|
+
if (msg.tool_call_id) unified.toolCallId = msg.tool_call_id;
|
|
7240
|
+
if (msg.tool_calls?.length) {
|
|
7241
|
+
unified.toolCalls = msg.tool_calls.map((tc) => ({
|
|
7242
|
+
id: tc.id,
|
|
7243
|
+
name: tc.function?.name ?? tc.name ?? "",
|
|
7244
|
+
arguments: tc.function?.arguments ?? tc.arguments ?? "{}"
|
|
7245
|
+
}));
|
|
7246
|
+
}
|
|
7247
|
+
return unified;
|
|
7248
|
+
}
|
|
7249
|
+
function convertContent(content) {
|
|
7250
|
+
if (typeof content === "string") return content;
|
|
7251
|
+
if (!Array.isArray(content)) return String(content ?? "");
|
|
7252
|
+
return content.map((part) => {
|
|
7253
|
+
if (part.type === "text" || !part.type && part.text) {
|
|
7254
|
+
return { type: "text", text: part.text ?? part.content ?? "" };
|
|
7255
|
+
}
|
|
7256
|
+
if (part.type === "image_url") {
|
|
7257
|
+
const img = part.image_url;
|
|
7258
|
+
if (typeof img === "string" || img?.url?.startsWith("http")) {
|
|
7259
|
+
return {
|
|
7260
|
+
type: "image",
|
|
7261
|
+
source: { type: "url", url: typeof img === "string" ? img : img.url }
|
|
7262
|
+
};
|
|
7263
|
+
}
|
|
7264
|
+
if (img?.url?.startsWith("data:")) {
|
|
7265
|
+
const match = img.url.match(/^data:([^;]+);base64,(.+)$/);
|
|
7266
|
+
if (match) {
|
|
7267
|
+
return {
|
|
7268
|
+
type: "image",
|
|
7269
|
+
source: { type: "base64", mediaType: match[1], data: match[2] }
|
|
7270
|
+
};
|
|
7271
|
+
}
|
|
7272
|
+
}
|
|
7273
|
+
}
|
|
7274
|
+
return { type: "text", text: part.text ?? part.content ?? JSON.stringify(part) };
|
|
7275
|
+
});
|
|
7276
|
+
}
|
|
7277
|
+
function isZodSchema(obj) {
|
|
7278
|
+
return obj != null && typeof obj === "object" && typeof obj.safeParse === "function" && "_def" in obj;
|
|
7279
|
+
}
|
|
7280
|
+
function convertToJsonSchema(schema, strict = false) {
|
|
7281
|
+
if (!schema || typeof schema !== "object") {
|
|
7282
|
+
return { type: "object", properties: {}, required: [], additionalProperties: false };
|
|
7283
|
+
}
|
|
7284
|
+
if (isZodSchema(schema)) {
|
|
7285
|
+
const converted = zodToJsonSchema(schema, {
|
|
7286
|
+
$refStrategy: "none",
|
|
7287
|
+
...strict ? { openaiStrictMode: true } : {}
|
|
7288
|
+
});
|
|
7289
|
+
delete converted["$schema"];
|
|
7290
|
+
if (!converted.type) converted.type = "object";
|
|
7291
|
+
return converted;
|
|
7292
|
+
}
|
|
7293
|
+
if (!schema.type && schema.properties) {
|
|
7294
|
+
return { type: "object", ...schema };
|
|
7295
|
+
}
|
|
7296
|
+
return schema;
|
|
7297
|
+
}
|
|
7298
|
+
function normalizeStrictSchema(schema) {
|
|
7299
|
+
if (!schema || typeof schema !== "object") return schema;
|
|
7300
|
+
const clone2 = { ...schema };
|
|
7301
|
+
if (clone2.type === "object" || clone2.properties) {
|
|
7302
|
+
if (!clone2.type) clone2.type = "object";
|
|
7303
|
+
if (clone2.properties && typeof clone2.properties === "object") {
|
|
7304
|
+
const normalized = {};
|
|
7305
|
+
for (const [k, v] of Object.entries(clone2.properties)) {
|
|
7306
|
+
normalized[k] = normalizeStrictSchema(v);
|
|
7307
|
+
}
|
|
7308
|
+
clone2.properties = normalized;
|
|
7309
|
+
clone2.required = Object.keys(normalized);
|
|
7310
|
+
} else {
|
|
7311
|
+
clone2.properties = {};
|
|
7312
|
+
clone2.required = [];
|
|
7313
|
+
}
|
|
7314
|
+
clone2.additionalProperties = false;
|
|
7315
|
+
}
|
|
7316
|
+
if (clone2.items) clone2.items = normalizeStrictSchema(clone2.items);
|
|
7317
|
+
for (const key of ["anyOf", "oneOf", "allOf"]) {
|
|
7318
|
+
if (Array.isArray(clone2[key])) {
|
|
7319
|
+
clone2[key] = clone2[key].map((s) => normalizeStrictSchema(s));
|
|
7320
|
+
}
|
|
7321
|
+
}
|
|
7322
|
+
if (clone2.definitions && typeof clone2.definitions === "object") {
|
|
7323
|
+
const nd = {};
|
|
7324
|
+
for (const [k, v] of Object.entries(clone2.definitions)) {
|
|
7325
|
+
nd[k] = normalizeStrictSchema(v);
|
|
7326
|
+
}
|
|
7327
|
+
clone2.definitions = nd;
|
|
7328
|
+
}
|
|
7329
|
+
if (clone2.$defs && typeof clone2.$defs === "object") {
|
|
7330
|
+
const nd = {};
|
|
7331
|
+
for (const [k, v] of Object.entries(clone2.$defs)) {
|
|
7332
|
+
nd[k] = normalizeStrictSchema(v);
|
|
7333
|
+
}
|
|
7334
|
+
clone2.$defs = nd;
|
|
7335
|
+
}
|
|
7336
|
+
return clone2;
|
|
7337
|
+
}
|
|
7338
|
+
function toToolDefinition(tool, strict) {
|
|
7339
|
+
let name;
|
|
7340
|
+
let description;
|
|
7341
|
+
let parameters;
|
|
7342
|
+
if (tool.type === "function" && tool.function) {
|
|
7343
|
+
name = tool.function.name;
|
|
7344
|
+
description = tool.function.description ?? "";
|
|
7345
|
+
parameters = tool.function.parameters ?? { type: "object", properties: {}, additionalProperties: false };
|
|
7346
|
+
} else {
|
|
7347
|
+
name = tool.name ?? "unknown";
|
|
7348
|
+
description = tool.description ?? "";
|
|
7349
|
+
const rawSchema = tool.schema ?? tool.parameters;
|
|
7350
|
+
parameters = convertToJsonSchema(rawSchema, strict);
|
|
7351
|
+
}
|
|
7352
|
+
if (strict) {
|
|
7353
|
+
parameters = normalizeStrictSchema(parameters);
|
|
7354
|
+
}
|
|
7355
|
+
return { name, description, parameters, ...strict ? { strict: true } : {} };
|
|
7356
|
+
}
|
|
7357
|
+
function toBaseChatMessage(response) {
|
|
7358
|
+
const msg = {
|
|
7359
|
+
role: "assistant",
|
|
7360
|
+
content: response.content ?? "",
|
|
7361
|
+
usage: {
|
|
7362
|
+
prompt_tokens: response.usage.inputTokens,
|
|
7363
|
+
completion_tokens: response.usage.outputTokens,
|
|
7364
|
+
total_tokens: response.usage.totalTokens,
|
|
7365
|
+
prompt_tokens_details: {
|
|
7366
|
+
cached_tokens: response.usage.cachedInputTokens
|
|
7367
|
+
},
|
|
7368
|
+
completion_tokens_details: {
|
|
7369
|
+
reasoning_tokens: response.usage.reasoningTokens
|
|
7370
|
+
}
|
|
7371
|
+
},
|
|
7372
|
+
response_metadata: {
|
|
7373
|
+
token_usage: {
|
|
7374
|
+
prompt_tokens: response.usage.inputTokens,
|
|
7375
|
+
completion_tokens: response.usage.outputTokens,
|
|
7376
|
+
total_tokens: response.usage.totalTokens,
|
|
7377
|
+
cached_tokens: response.usage.cachedInputTokens,
|
|
7378
|
+
cache_write_tokens: response.usage.cachedWriteTokens
|
|
7379
|
+
},
|
|
7380
|
+
finish_reason: response.finishReason,
|
|
7381
|
+
model_name: response.model
|
|
7382
|
+
}
|
|
7383
|
+
};
|
|
7384
|
+
if (response.toolCalls.length > 0) {
|
|
7385
|
+
msg.tool_calls = response.toolCalls.map((tc) => ({
|
|
7386
|
+
id: tc.id,
|
|
7387
|
+
type: "function",
|
|
7388
|
+
function: { name: tc.name, arguments: tc.arguments }
|
|
7389
|
+
}));
|
|
7390
|
+
}
|
|
7391
|
+
return msg;
|
|
7392
|
+
}
|
|
7393
|
+
function assembleMessage(content, toolCalls, usage) {
|
|
7394
|
+
const msg = {
|
|
7395
|
+
role: "assistant",
|
|
7396
|
+
content
|
|
7397
|
+
};
|
|
7398
|
+
if (usage) {
|
|
7399
|
+
msg.usage = {
|
|
7400
|
+
prompt_tokens: usage.inputTokens,
|
|
7401
|
+
completion_tokens: usage.outputTokens,
|
|
7402
|
+
total_tokens: usage.totalTokens,
|
|
7403
|
+
prompt_tokens_details: {
|
|
7404
|
+
cached_tokens: usage.cachedInputTokens
|
|
7405
|
+
},
|
|
7406
|
+
completion_tokens_details: {
|
|
7407
|
+
reasoning_tokens: usage.reasoningTokens
|
|
7408
|
+
}
|
|
7409
|
+
};
|
|
7410
|
+
msg.response_metadata = {
|
|
7411
|
+
token_usage: {
|
|
7412
|
+
prompt_tokens: usage.inputTokens,
|
|
7413
|
+
completion_tokens: usage.outputTokens,
|
|
7414
|
+
total_tokens: usage.totalTokens,
|
|
7415
|
+
cached_tokens: usage.cachedInputTokens,
|
|
7416
|
+
cache_write_tokens: usage.cachedWriteTokens
|
|
7417
|
+
}
|
|
7418
|
+
};
|
|
7419
|
+
}
|
|
7420
|
+
if (toolCalls.length > 0) {
|
|
7421
|
+
msg.tool_calls = toolCalls.map((tc) => ({
|
|
7422
|
+
id: tc.id,
|
|
7423
|
+
type: "function",
|
|
7424
|
+
function: { name: tc.name, arguments: tc.arguments }
|
|
7425
|
+
}));
|
|
7426
|
+
}
|
|
7427
|
+
return msg;
|
|
7428
|
+
}
|
|
7429
|
+
function resolveCapabilities(provider) {
|
|
7430
|
+
switch (provider) {
|
|
7431
|
+
case "openai":
|
|
7432
|
+
case "azure":
|
|
7433
|
+
case "openai-compatible":
|
|
7434
|
+
return {
|
|
7435
|
+
structuredOutput: "native",
|
|
7436
|
+
strictToolCalling: true,
|
|
7437
|
+
streaming: true,
|
|
7438
|
+
provider
|
|
7439
|
+
};
|
|
7440
|
+
case "anthropic":
|
|
7441
|
+
return {
|
|
7442
|
+
structuredOutput: "tool_based",
|
|
7443
|
+
strictToolCalling: false,
|
|
7444
|
+
streaming: true,
|
|
7445
|
+
provider: "anthropic"
|
|
7446
|
+
};
|
|
7447
|
+
case "bedrock":
|
|
7448
|
+
return {
|
|
7449
|
+
structuredOutput: "tool_based",
|
|
7450
|
+
strictToolCalling: false,
|
|
7451
|
+
streaming: false,
|
|
7452
|
+
// streaming uses binary protocol
|
|
7453
|
+
provider: "bedrock"
|
|
7454
|
+
};
|
|
7455
|
+
case "vertex":
|
|
7456
|
+
return {
|
|
7457
|
+
structuredOutput: "native",
|
|
7458
|
+
strictToolCalling: false,
|
|
7459
|
+
streaming: true,
|
|
7460
|
+
provider: "google"
|
|
7461
|
+
};
|
|
7462
|
+
default:
|
|
7463
|
+
return { provider };
|
|
7464
|
+
}
|
|
7465
|
+
}
|
|
7466
|
+
|
|
7467
|
+
// src/providers/index.ts
|
|
7468
|
+
function createProvider(config) {
|
|
7469
|
+
switch (config.provider) {
|
|
7470
|
+
case "openai":
|
|
7471
|
+
return new OpenAIProvider(config);
|
|
7472
|
+
case "anthropic":
|
|
7473
|
+
return new AnthropicProvider(config);
|
|
7474
|
+
case "azure":
|
|
7475
|
+
return new AzureProvider(config);
|
|
7476
|
+
case "openai-compatible":
|
|
7477
|
+
return new OpenAICompatibleProvider(config);
|
|
7478
|
+
case "bedrock":
|
|
7479
|
+
return new BedrockProvider(config);
|
|
7480
|
+
case "vertex":
|
|
7481
|
+
return new VertexProvider(config);
|
|
7482
|
+
default:
|
|
7483
|
+
throw new Error(`Unknown provider: ${config.provider}`);
|
|
7484
|
+
}
|
|
7485
|
+
}
|
|
7486
|
+
|
|
7487
|
+
export { AnthropicProvider, AzureProvider, BaseProvider, BedrockProvider, DEFAULT_PROFILE_CONFIGS, GuardrailPhase, InMemoryMemoryStore, NativeJsonSchemaStrategy, OpenAICompatibleProvider, OpenAIProvider, ProviderError, StructuredOutputManager, ToolBasedStrategy, VertexProvider, agentVerdictRule, applyTokenLimits, buildSystemPrompt, captureSnapshot, codePresenceRule, cognipeerSink, countApproxTokens, countMessagesTokens, createAgent, createAgentNode, createCodeGuardrail, createContextSummarizeNode, createContextTools, createGuardrail, createJsonGuardrail, createProvider, createRegexGuardrail, createResolverNode, createSmartAgent, createTool, createToolLimitFinalizeNode, createToolsNode, customCallbackRule, customSink, emptyUsage, evaluateGuardrails, fileSink, formatZodError, fromLangchainModel, fromLangchainTools, fromNativeProvider, generateSpanId, generateTraceId, getModelCapabilities, getNestedValue, getResolvedSmartConfig, httpSink, isSmartModel, jsonSchemaRule, normalizeSmartAgentOptions, otlpSink, readMemoryFacts, regexRule, renderMemoryFacts, resolveMemoryStore, resolveStrategy, resolveToolApprovalState, restoreSnapshot, runSmartAgentEvalHarness, startStreamingSession, traceSessionToOtlp, withTools, writeSummaryFactsToMemory };
|
|
5155
7488
|
//# sourceMappingURL=index.mjs.map
|
|
5156
7489
|
//# sourceMappingURL=index.mjs.map
|