@cognipeer/agent-sdk 0.2.2 → 0.2.4
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/dist/index.cjs +693 -164
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +108 -6
- package/dist/index.d.ts +108 -6
- package/dist/index.mjs +693 -165
- package/dist/index.mjs.map +1 -1
- package/package.json +13 -2
package/dist/index.mjs
CHANGED
|
@@ -134,7 +134,7 @@ function withTools(model, tools) {
|
|
|
134
134
|
function fromLangchainModel(lcModel) {
|
|
135
135
|
if (!lcModel) throw new Error("fromLangchainModel: model is undefined/null");
|
|
136
136
|
const adapted = {
|
|
137
|
-
invoke: async (messages) => {
|
|
137
|
+
invoke: async (messages, options) => {
|
|
138
138
|
const normalizeContent = (content) => {
|
|
139
139
|
if (content == null) return content;
|
|
140
140
|
if (typeof content === "string") return content;
|
|
@@ -167,7 +167,7 @@ function fromLangchainModel(lcModel) {
|
|
|
167
167
|
return { role, content: normalizeContent(m.content), name: m.name, tool_calls: m.tool_calls, tool_call_id: m.tool_call_id };
|
|
168
168
|
};
|
|
169
169
|
const lcMessages = messages.map(toLC);
|
|
170
|
-
const response = await lcModel.invoke(lcMessages);
|
|
170
|
+
const response = await lcModel.invoke(lcMessages, options);
|
|
171
171
|
if (response && typeof response === "object") {
|
|
172
172
|
const content = response.content ?? response.text ?? "";
|
|
173
173
|
return {
|
|
@@ -181,6 +181,43 @@ function fromLangchainModel(lcModel) {
|
|
|
181
181
|
}
|
|
182
182
|
return { role: "assistant", content: String(response ?? "") };
|
|
183
183
|
},
|
|
184
|
+
stream: async function* (messages, options) {
|
|
185
|
+
if (typeof lcModel.stream !== "function") return;
|
|
186
|
+
const normalizeContent = (content) => {
|
|
187
|
+
if (content == null) return content;
|
|
188
|
+
if (typeof content === "string") return content;
|
|
189
|
+
if (Array.isArray(content)) {
|
|
190
|
+
return content.map((part) => {
|
|
191
|
+
if (!part || typeof part !== "object") return part;
|
|
192
|
+
if (part.type === "text") return { type: "text", text: String(part.text ?? "") };
|
|
193
|
+
if (part.type === "image_url") {
|
|
194
|
+
const img = part.image_url;
|
|
195
|
+
if (typeof img === "string") return { type: "image_url", image_url: { url: img } };
|
|
196
|
+
if (img && typeof img === "object") {
|
|
197
|
+
if ("url" in img) return { type: "image_url", image_url: img };
|
|
198
|
+
if ("base64" in img) {
|
|
199
|
+
const media = img.media_type || "image/jpeg";
|
|
200
|
+
const dataUrl = `data:${media};base64,${img.base64}`;
|
|
201
|
+
const detail = img.detail;
|
|
202
|
+
return { type: "image_url", image_url: { url: dataUrl, detail } };
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return part;
|
|
206
|
+
}
|
|
207
|
+
return part;
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
return content;
|
|
211
|
+
};
|
|
212
|
+
const toLC = (m) => {
|
|
213
|
+
if (m._getType || m.lc_serializable) return m;
|
|
214
|
+
return { role: m.role, content: normalizeContent(m.content), name: m.name, tool_calls: m.tool_calls, tool_call_id: m.tool_call_id };
|
|
215
|
+
};
|
|
216
|
+
const lcMessages = messages.map(toLC);
|
|
217
|
+
for await (const chunk of lcModel.stream(lcMessages, options)) {
|
|
218
|
+
yield chunk;
|
|
219
|
+
}
|
|
220
|
+
},
|
|
184
221
|
bindTools: (tools) => {
|
|
185
222
|
const lcReady = toLangchainTools(tools);
|
|
186
223
|
if (typeof lcModel.bindTools === "function") {
|
|
@@ -334,7 +371,7 @@ function safe(v) {
|
|
|
334
371
|
const n = Number(v);
|
|
335
372
|
return Number.isFinite(n) && n >= 0 ? n : 0;
|
|
336
373
|
}
|
|
337
|
-
var DEFAULT_COGNIPEER_URL = "https://
|
|
374
|
+
var DEFAULT_COGNIPEER_URL = "https://console.cognipeer.com/api/client/v1/tracing/sessions";
|
|
338
375
|
function fileSink(path2) {
|
|
339
376
|
return { type: "file", path: path2 };
|
|
340
377
|
}
|
|
@@ -392,133 +429,113 @@ function coerceToString(value) {
|
|
|
392
429
|
}
|
|
393
430
|
return void 0;
|
|
394
431
|
}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
432
|
+
var METADATA_PATHS = {
|
|
433
|
+
model: [
|
|
434
|
+
"model",
|
|
435
|
+
"options.model",
|
|
436
|
+
"params.model",
|
|
437
|
+
"config.model",
|
|
438
|
+
"configuration.model",
|
|
439
|
+
"metadata.model",
|
|
440
|
+
"lc_kwargs.model",
|
|
441
|
+
"metadata.modelName",
|
|
442
|
+
"modelName",
|
|
443
|
+
"lc_alias",
|
|
444
|
+
"model_id",
|
|
445
|
+
"id",
|
|
446
|
+
"_model",
|
|
447
|
+
"_modelName",
|
|
448
|
+
"__model",
|
|
449
|
+
"_modelId",
|
|
450
|
+
"_llmType"
|
|
451
|
+
],
|
|
452
|
+
provider: [
|
|
453
|
+
"provider",
|
|
454
|
+
"options.provider",
|
|
455
|
+
"params.provider",
|
|
456
|
+
"config.provider",
|
|
457
|
+
"configuration.provider",
|
|
458
|
+
"metadata.provider",
|
|
459
|
+
"lc_kwargs.provider"
|
|
460
|
+
]
|
|
461
|
+
};
|
|
462
|
+
function getNestedValue(obj, path2) {
|
|
463
|
+
if (!obj || typeof path2 !== "string") return void 0;
|
|
464
|
+
return path2.split(".").reduce((current, key) => current?.[key], obj);
|
|
401
465
|
}
|
|
402
|
-
function
|
|
466
|
+
function extractMetadata(model, paths) {
|
|
403
467
|
if (!model) return void 0;
|
|
468
|
+
for (const path2 of paths) {
|
|
469
|
+
const value = getNestedValue(model, path2);
|
|
470
|
+
const str = coerceToString(value);
|
|
471
|
+
if (str) return str;
|
|
472
|
+
}
|
|
404
473
|
const maybeLc = model?._lc;
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
model?.lc_kwargs?.model,
|
|
413
|
-
maybeLc?.model,
|
|
414
|
-
maybeLc?.options?.model,
|
|
415
|
-
maybeLc?.params?.model,
|
|
416
|
-
maybeLc?.config?.model,
|
|
417
|
-
maybeLc?.configuration?.model,
|
|
418
|
-
maybeLc?.metadata?.model,
|
|
419
|
-
maybeLc?.lc_kwargs?.model,
|
|
420
|
-
model?.metadata?.modelName,
|
|
421
|
-
maybeLc?.metadata?.modelName,
|
|
422
|
-
model?.modelName,
|
|
423
|
-
maybeLc?.modelName,
|
|
424
|
-
model?.lc_alias,
|
|
425
|
-
model?.model_id,
|
|
426
|
-
model?.id,
|
|
427
|
-
maybeLc?.model_id,
|
|
428
|
-
maybeLc?.id,
|
|
429
|
-
model?._model,
|
|
430
|
-
model?._modelName,
|
|
431
|
-
model?.__model
|
|
432
|
-
);
|
|
433
|
-
if (direct) return direct;
|
|
474
|
+
if (maybeLc) {
|
|
475
|
+
for (const path2 of paths) {
|
|
476
|
+
const value = getNestedValue(maybeLc, path2);
|
|
477
|
+
const str = coerceToString(value);
|
|
478
|
+
if (str) return str;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
434
481
|
const maybeClient = model?.client || model?.api || model?.service;
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
const
|
|
443
|
-
|
|
482
|
+
if (maybeClient) {
|
|
483
|
+
for (const path2 of paths) {
|
|
484
|
+
const value = getNestedValue(maybeClient, path2);
|
|
485
|
+
const str = coerceToString(value);
|
|
486
|
+
if (str) return str;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
const constructorName = model?.constructor?.name;
|
|
490
|
+
if (constructorName && !["Object", "Function"].includes(constructorName)) {
|
|
491
|
+
return constructorName;
|
|
492
|
+
}
|
|
493
|
+
return void 0;
|
|
494
|
+
}
|
|
495
|
+
function getModelName(model) {
|
|
496
|
+
return extractMetadata(model, METADATA_PATHS.model);
|
|
444
497
|
}
|
|
445
498
|
function getProviderName(model) {
|
|
446
|
-
|
|
447
|
-
const direct = firstNonEmptyString(
|
|
448
|
-
model?.provider,
|
|
449
|
-
model?.options?.provider,
|
|
450
|
-
model?.params?.provider,
|
|
451
|
-
model?.config?.provider,
|
|
452
|
-
model?.configuration?.provider,
|
|
453
|
-
model?.metadata?.provider,
|
|
454
|
-
model?.lc_kwargs?.provider,
|
|
455
|
-
model?.lc_alias,
|
|
456
|
-
model?.__provider
|
|
457
|
-
);
|
|
458
|
-
if (direct) return direct;
|
|
459
|
-
const maybeLc = model?._lc;
|
|
460
|
-
const lcValue = maybeLc ? firstNonEmptyString(
|
|
461
|
-
maybeLc?.provider,
|
|
462
|
-
maybeLc?.options?.provider,
|
|
463
|
-
maybeLc?.config?.provider,
|
|
464
|
-
maybeLc?.configuration?.provider,
|
|
465
|
-
maybeLc?.metadata?.provider,
|
|
466
|
-
maybeLc?.lc_kwargs?.provider,
|
|
467
|
-
maybeLc?.client?.config?.provider
|
|
468
|
-
) : void 0;
|
|
469
|
-
if (lcValue) return lcValue;
|
|
470
|
-
const maybeClient = model?.client || model?.api || model?.service;
|
|
471
|
-
return firstNonEmptyString(
|
|
472
|
-
maybeClient?.provider,
|
|
473
|
-
maybeClient?.config?.provider,
|
|
474
|
-
maybeClient?.options?.provider,
|
|
475
|
-
maybeClient?.metadata?.provider
|
|
476
|
-
);
|
|
499
|
+
return extractMetadata(model, METADATA_PATHS.provider);
|
|
477
500
|
}
|
|
478
501
|
function inferModelFromMessages(messageList) {
|
|
479
502
|
if (!Array.isArray(messageList)) return void 0;
|
|
480
|
-
|
|
503
|
+
const messagePaths = [
|
|
504
|
+
"metadata.model",
|
|
505
|
+
"metadata.modelName",
|
|
506
|
+
"response_metadata.model",
|
|
507
|
+
"response_metadata.modelName",
|
|
508
|
+
"usage_metadata.model",
|
|
509
|
+
"model",
|
|
510
|
+
"modelName"
|
|
511
|
+
];
|
|
512
|
+
for (let i = messageList.length - 1; i >= 0; i--) {
|
|
481
513
|
const message = messageList[i];
|
|
482
514
|
if (!message) continue;
|
|
483
|
-
const
|
|
484
|
-
message
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
message?.response_metadata?.model,
|
|
489
|
-
message?.response_metadata?.modelName,
|
|
490
|
-
message?.response_metadata?.modelNames,
|
|
491
|
-
message?.response_metadata?.model_id,
|
|
492
|
-
message?.usage_metadata?.model,
|
|
493
|
-
message?.model,
|
|
494
|
-
message?.modelName,
|
|
495
|
-
message?.model_id,
|
|
496
|
-
message?.additional_kwargs?.model,
|
|
497
|
-
message?.additional_kwargs?.modelName,
|
|
498
|
-
message?.info?.model,
|
|
499
|
-
message?.annotations?.model
|
|
500
|
-
);
|
|
501
|
-
if (candidate) return candidate;
|
|
515
|
+
for (const path2 of messagePaths) {
|
|
516
|
+
const value = getNestedValue(message, path2);
|
|
517
|
+
const str = coerceToString(value);
|
|
518
|
+
if (str) return str;
|
|
519
|
+
}
|
|
502
520
|
}
|
|
503
521
|
return void 0;
|
|
504
522
|
}
|
|
505
523
|
function inferProviderFromMessages(messageList) {
|
|
506
524
|
if (!Array.isArray(messageList)) return void 0;
|
|
507
|
-
|
|
525
|
+
const messagePaths = [
|
|
526
|
+
"metadata.provider",
|
|
527
|
+
"response_metadata.provider",
|
|
528
|
+
"usage_metadata.provider",
|
|
529
|
+
"provider"
|
|
530
|
+
];
|
|
531
|
+
for (let i = messageList.length - 1; i >= 0; i--) {
|
|
508
532
|
const message = messageList[i];
|
|
509
533
|
if (!message) continue;
|
|
510
|
-
const
|
|
511
|
-
message
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
message?.usage_metadata?.provider,
|
|
516
|
-
message?.provider,
|
|
517
|
-
message?.additional_kwargs?.provider,
|
|
518
|
-
message?.info?.provider,
|
|
519
|
-
message?.annotations?.provider
|
|
520
|
-
);
|
|
521
|
-
if (candidate) return candidate;
|
|
534
|
+
for (const path2 of messagePaths) {
|
|
535
|
+
const value = getNestedValue(message, path2);
|
|
536
|
+
const str = coerceToString(value);
|
|
537
|
+
if (str) return str;
|
|
538
|
+
}
|
|
522
539
|
}
|
|
523
540
|
return void 0;
|
|
524
541
|
}
|
|
@@ -533,6 +550,7 @@ function ensureDir(p) {
|
|
|
533
550
|
function withDefaults(config) {
|
|
534
551
|
const enabled = Boolean(config?.enabled);
|
|
535
552
|
const logData = config?.logData ?? DEFAULT_TRACE_CONFIG.logData;
|
|
553
|
+
const mode = config?.mode === "streaming" ? "streaming" : "batched";
|
|
536
554
|
let sink;
|
|
537
555
|
try {
|
|
538
556
|
sink = resolveSink(config?.sink);
|
|
@@ -545,7 +563,7 @@ function withDefaults(config) {
|
|
|
545
563
|
return {
|
|
546
564
|
enabled,
|
|
547
565
|
logData,
|
|
548
|
-
mode
|
|
566
|
+
mode,
|
|
549
567
|
sink
|
|
550
568
|
};
|
|
551
569
|
}
|
|
@@ -616,20 +634,134 @@ async function postTraceSession(url, headers, payload) {
|
|
|
616
634
|
if (!Object.keys(finalHeaders).some((key) => key.toLowerCase() === "content-type")) {
|
|
617
635
|
finalHeaders["content-type"] = "application/json";
|
|
618
636
|
}
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
637
|
+
try {
|
|
638
|
+
const response = await fetchFn(url, {
|
|
639
|
+
method: "POST",
|
|
640
|
+
headers: finalHeaders,
|
|
641
|
+
body: JSON.stringify(payload)
|
|
642
|
+
});
|
|
643
|
+
if (!response.ok) {
|
|
644
|
+
let responseText = "";
|
|
645
|
+
try {
|
|
646
|
+
responseText = await response.text();
|
|
647
|
+
} catch {
|
|
648
|
+
}
|
|
649
|
+
const statusLine = `HTTP ${response.status}${response.statusText ? ` ${response.statusText}` : ""}`;
|
|
650
|
+
const bodyPreview = responseText ? ` - ${responseText.slice(0, 200)}` : "";
|
|
651
|
+
const errorMsg = `${statusLine}${bodyPreview}`;
|
|
652
|
+
if (typeof console !== "undefined" && typeof console.error === "function") {
|
|
653
|
+
console.error("[Tracing] Error posting to", url, ":", errorMsg);
|
|
654
|
+
}
|
|
655
|
+
throw new Error(errorMsg);
|
|
656
|
+
}
|
|
657
|
+
} catch (err) {
|
|
658
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
659
|
+
if (typeof console !== "undefined" && typeof console.error === "function") {
|
|
660
|
+
console.error("[Tracing] Failed to post:", errMsg);
|
|
661
|
+
}
|
|
662
|
+
throw err;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
async function postStreamingSessionStart(baseUrl, headers, payload) {
|
|
666
|
+
const fetchFn = typeof globalThis.fetch === "function" ? globalThis.fetch.bind(globalThis) : void 0;
|
|
667
|
+
if (!fetchFn) {
|
|
668
|
+
throw new Error("HTTP sink requires fetch to be available in this runtime.");
|
|
669
|
+
}
|
|
670
|
+
const url = `${baseUrl.replace(/\/$/, "")}/${payload.sessionId}/start`;
|
|
671
|
+
const finalHeaders = { ...headers || {} };
|
|
672
|
+
if (!Object.keys(finalHeaders).some((key) => key.toLowerCase() === "content-type")) {
|
|
673
|
+
finalHeaders["content-type"] = "application/json";
|
|
674
|
+
}
|
|
675
|
+
try {
|
|
676
|
+
const response = await fetchFn(url, {
|
|
677
|
+
method: "POST",
|
|
678
|
+
headers: finalHeaders,
|
|
679
|
+
body: JSON.stringify(payload)
|
|
680
|
+
});
|
|
681
|
+
if (!response.ok) {
|
|
682
|
+
let responseText = "";
|
|
683
|
+
try {
|
|
684
|
+
responseText = await response.text();
|
|
685
|
+
} catch {
|
|
686
|
+
}
|
|
687
|
+
const statusLine = `HTTP ${response.status}${response.statusText ? ` ${response.statusText}` : ""}`;
|
|
688
|
+
const bodyPreview = responseText ? ` - ${responseText.slice(0, 200)}` : "";
|
|
689
|
+
throw new Error(`${statusLine}${bodyPreview}`);
|
|
690
|
+
}
|
|
691
|
+
} catch (err) {
|
|
692
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
693
|
+
if (typeof console !== "undefined" && typeof console.error === "function") {
|
|
694
|
+
console.error("[Tracing] Failed to post session start:", errMsg);
|
|
695
|
+
}
|
|
696
|
+
throw err;
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
async function postStreamingEvent(baseUrl, headers, sessionId, event) {
|
|
700
|
+
const fetchFn = typeof globalThis.fetch === "function" ? globalThis.fetch.bind(globalThis) : void 0;
|
|
701
|
+
if (!fetchFn) {
|
|
702
|
+
throw new Error("HTTP sink requires fetch to be available in this runtime.");
|
|
703
|
+
}
|
|
704
|
+
const url = `${baseUrl.replace(/\/$/, "")}/${sessionId}/events`;
|
|
705
|
+
const finalHeaders = { ...headers || {} };
|
|
706
|
+
if (!Object.keys(finalHeaders).some((key) => key.toLowerCase() === "content-type")) {
|
|
707
|
+
finalHeaders["content-type"] = "application/json";
|
|
708
|
+
}
|
|
709
|
+
try {
|
|
710
|
+
const response = await fetchFn(url, {
|
|
711
|
+
method: "POST",
|
|
712
|
+
headers: finalHeaders,
|
|
713
|
+
body: JSON.stringify({ event })
|
|
714
|
+
});
|
|
715
|
+
if (!response.ok) {
|
|
716
|
+
let responseText = "";
|
|
717
|
+
try {
|
|
718
|
+
responseText = await response.text();
|
|
719
|
+
} catch {
|
|
720
|
+
}
|
|
721
|
+
const statusLine = `HTTP ${response.status}${response.statusText ? ` ${response.statusText}` : ""}`;
|
|
722
|
+
const bodyPreview = responseText ? ` - ${responseText.slice(0, 200)}` : "";
|
|
723
|
+
throw new Error(`${statusLine}${bodyPreview}`);
|
|
724
|
+
}
|
|
725
|
+
} catch (err) {
|
|
726
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
727
|
+
if (typeof console !== "undefined" && typeof console.error === "function") {
|
|
728
|
+
console.error("[Tracing] Failed to post streaming event:", errMsg);
|
|
729
|
+
}
|
|
730
|
+
throw err;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
async function postStreamingSessionEnd(baseUrl, headers, payload) {
|
|
734
|
+
const fetchFn = typeof globalThis.fetch === "function" ? globalThis.fetch.bind(globalThis) : void 0;
|
|
735
|
+
if (!fetchFn) {
|
|
736
|
+
throw new Error("HTTP sink requires fetch to be available in this runtime.");
|
|
737
|
+
}
|
|
738
|
+
const url = `${baseUrl.replace(/\/$/, "")}/${payload.sessionId}/end`;
|
|
739
|
+
const finalHeaders = { ...headers || {} };
|
|
740
|
+
if (!Object.keys(finalHeaders).some((key) => key.toLowerCase() === "content-type")) {
|
|
741
|
+
finalHeaders["content-type"] = "application/json";
|
|
742
|
+
}
|
|
743
|
+
try {
|
|
744
|
+
const response = await fetchFn(url, {
|
|
745
|
+
method: "POST",
|
|
746
|
+
headers: finalHeaders,
|
|
747
|
+
body: JSON.stringify(payload)
|
|
748
|
+
});
|
|
749
|
+
if (!response.ok) {
|
|
750
|
+
let responseText = "";
|
|
751
|
+
try {
|
|
752
|
+
responseText = await response.text();
|
|
753
|
+
} catch {
|
|
754
|
+
}
|
|
755
|
+
const statusLine = `HTTP ${response.status}${response.statusText ? ` ${response.statusText}` : ""}`;
|
|
756
|
+
const bodyPreview = responseText ? ` - ${responseText.slice(0, 200)}` : "";
|
|
757
|
+
throw new Error(`${statusLine}${bodyPreview}`);
|
|
758
|
+
}
|
|
759
|
+
} catch (err) {
|
|
760
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
761
|
+
if (typeof console !== "undefined" && typeof console.error === "function") {
|
|
762
|
+
console.error("[Tracing] Failed to post session end:", errMsg);
|
|
629
763
|
}
|
|
630
|
-
|
|
631
|
-
const bodyPreview = responseText ? ` - ${responseText.slice(0, 200)}` : "";
|
|
632
|
-
throw new Error(`${statusLine}${bodyPreview}`);
|
|
764
|
+
throw err;
|
|
633
765
|
}
|
|
634
766
|
}
|
|
635
767
|
function createEmptySummary() {
|
|
@@ -808,7 +940,8 @@ function createTraceSession(opts) {
|
|
|
808
940
|
events: [],
|
|
809
941
|
summary: createEmptySummary(),
|
|
810
942
|
status: "in_progress",
|
|
811
|
-
errors: []
|
|
943
|
+
errors: [],
|
|
944
|
+
sessionStarted: false
|
|
812
945
|
};
|
|
813
946
|
if (cfg.sink.type === "file") {
|
|
814
947
|
const baseDir = cfg.sink.baseDir;
|
|
@@ -820,6 +953,49 @@ function createTraceSession(opts) {
|
|
|
820
953
|
}
|
|
821
954
|
return runtime;
|
|
822
955
|
}
|
|
956
|
+
async function startStreamingSession(session, agentRuntime) {
|
|
957
|
+
if (!session) return;
|
|
958
|
+
if (session.resolvedConfig.mode !== "streaming") return;
|
|
959
|
+
if (session.sessionStarted) return;
|
|
960
|
+
const sink = session.resolvedConfig.sink;
|
|
961
|
+
const agentInfo = agentRuntime ? {
|
|
962
|
+
name: agentRuntime.name || "unknown-agent",
|
|
963
|
+
version: agentRuntime.version,
|
|
964
|
+
model: getModelName(agentRuntime.model) || "unknown-model",
|
|
965
|
+
provider: getProviderName(agentRuntime.model)
|
|
966
|
+
} : void 0;
|
|
967
|
+
session.agentInfo = agentInfo;
|
|
968
|
+
const configSnapshot = buildConfigSnapshot(session);
|
|
969
|
+
const startedAtIso = new Date(session.startedAt).toISOString();
|
|
970
|
+
const payload = {
|
|
971
|
+
sessionId: session.sessionId,
|
|
972
|
+
startedAt: startedAtIso,
|
|
973
|
+
agent: agentInfo,
|
|
974
|
+
config: configSnapshot
|
|
975
|
+
};
|
|
976
|
+
if (sink.type === "cognipeer" || sink.type === "http") {
|
|
977
|
+
try {
|
|
978
|
+
const headers = sink.type === "cognipeer" ? { Authorization: `Bearer ${sink.apiKey}` } : sink.headers;
|
|
979
|
+
await postStreamingSessionStart(sink.url, headers, payload);
|
|
980
|
+
session.sessionStarted = true;
|
|
981
|
+
} catch (err) {
|
|
982
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
983
|
+
session.errors.push({
|
|
984
|
+
eventId: "session_start",
|
|
985
|
+
message,
|
|
986
|
+
type: "sink",
|
|
987
|
+
timestamp: startedAtIso
|
|
988
|
+
});
|
|
989
|
+
if (typeof console !== "undefined" && typeof console.warn === "function") {
|
|
990
|
+
console.warn("[Tracing] Failed to start streaming session, falling back to batched:", message);
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
} else if (sink.type === "custom" && typeof sink.onEvent === "function") {
|
|
994
|
+
session.sessionStarted = true;
|
|
995
|
+
} else if (sink.type === "file") {
|
|
996
|
+
session.sessionStarted = true;
|
|
997
|
+
}
|
|
998
|
+
}
|
|
823
999
|
function recordTraceEvent(session, event) {
|
|
824
1000
|
if (!session) return void 0;
|
|
825
1001
|
const sequence = session.events.length + 1;
|
|
@@ -922,6 +1098,23 @@ function recordTraceEvent(session, event) {
|
|
|
922
1098
|
}
|
|
923
1099
|
}
|
|
924
1100
|
}
|
|
1101
|
+
if (session.resolvedConfig.mode === "streaming" && session.sessionStarted) {
|
|
1102
|
+
if (sink.type === "cognipeer" || sink.type === "http") {
|
|
1103
|
+
const headers = sink.type === "cognipeer" ? { Authorization: `Bearer ${sink.apiKey}` } : sink.headers;
|
|
1104
|
+
postStreamingEvent(sink.url, headers, session.sessionId, record).catch((err) => {
|
|
1105
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
1106
|
+
if (typeof console !== "undefined" && typeof console.warn === "function") {
|
|
1107
|
+
console.warn("[Tracing] Failed to send streaming event:", errMsg);
|
|
1108
|
+
}
|
|
1109
|
+
session.errors.push({
|
|
1110
|
+
eventId: record.id,
|
|
1111
|
+
message: errMsg,
|
|
1112
|
+
type: "sink",
|
|
1113
|
+
timestamp: timestampIso
|
|
1114
|
+
});
|
|
1115
|
+
});
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
925
1118
|
return record;
|
|
926
1119
|
}
|
|
927
1120
|
async function finalizeTraceSession(session, params = {}) {
|
|
@@ -930,12 +1123,16 @@ async function finalizeTraceSession(session, params = {}) {
|
|
|
930
1123
|
const endedAtIso = new Date(endedAtMs).toISOString();
|
|
931
1124
|
const durationMs = endedAtMs - session.startedAt;
|
|
932
1125
|
const startedAtIso = new Date(session.startedAt).toISOString();
|
|
933
|
-
const
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
1126
|
+
const buildAgentInfo = (runtime) => {
|
|
1127
|
+
if (!runtime) return void 0;
|
|
1128
|
+
return {
|
|
1129
|
+
name: runtime.name || "unknown-agent",
|
|
1130
|
+
version: runtime.version,
|
|
1131
|
+
model: getModelName(runtime.model) || "unknown-model",
|
|
1132
|
+
provider: getProviderName(runtime.model)
|
|
1133
|
+
};
|
|
1134
|
+
};
|
|
1135
|
+
const agentInfo = buildAgentInfo(params.agentRuntime);
|
|
939
1136
|
if (params.error) {
|
|
940
1137
|
session.errors.push({
|
|
941
1138
|
eventId: "session",
|
|
@@ -952,7 +1149,7 @@ async function finalizeTraceSession(session, params = {}) {
|
|
|
952
1149
|
startedAt: startedAtIso,
|
|
953
1150
|
endedAt: endedAtIso,
|
|
954
1151
|
durationMs,
|
|
955
|
-
agent: agentInfo,
|
|
1152
|
+
agent: agentInfo || session.agentInfo,
|
|
956
1153
|
config: configSnapshot,
|
|
957
1154
|
summary: session.summary,
|
|
958
1155
|
events: session.events,
|
|
@@ -962,10 +1159,22 @@ async function finalizeTraceSession(session, params = {}) {
|
|
|
962
1159
|
const payloadForSink = buildPayload(initialStatus);
|
|
963
1160
|
let sinkFailed = false;
|
|
964
1161
|
const sink = session.resolvedConfig.sink;
|
|
1162
|
+
const isStreamingMode = session.resolvedConfig.mode === "streaming";
|
|
965
1163
|
if (sink.type === "cognipeer" || sink.type === "http") {
|
|
966
1164
|
try {
|
|
967
1165
|
const headers = sink.type === "cognipeer" ? { Authorization: `Bearer ${sink.apiKey}` } : sink.headers;
|
|
968
|
-
|
|
1166
|
+
if (isStreamingMode && session.sessionStarted) {
|
|
1167
|
+
await postStreamingSessionEnd(sink.url, headers, {
|
|
1168
|
+
sessionId: session.sessionId,
|
|
1169
|
+
endedAt: endedAtIso,
|
|
1170
|
+
durationMs,
|
|
1171
|
+
status: initialStatus,
|
|
1172
|
+
summary: session.summary,
|
|
1173
|
+
errors: session.errors
|
|
1174
|
+
});
|
|
1175
|
+
} else {
|
|
1176
|
+
await postTraceSession(sink.url, headers, payloadForSink);
|
|
1177
|
+
}
|
|
969
1178
|
} catch (err) {
|
|
970
1179
|
sinkFailed = true;
|
|
971
1180
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -1103,12 +1312,29 @@ function createAgentCoreNode(opts) {
|
|
|
1103
1312
|
};
|
|
1104
1313
|
const normalizeBedrockToolPairing = (input) => {
|
|
1105
1314
|
const msgs = Array.isArray(input) ? [...input] : [];
|
|
1106
|
-
|
|
1107
|
-
|
|
1315
|
+
const validToolCallIds = /* @__PURE__ */ new Set();
|
|
1316
|
+
for (const m of msgs) {
|
|
1317
|
+
if (m?.role === "assistant") {
|
|
1318
|
+
for (const tu of extractToolUses(m)) {
|
|
1319
|
+
if (tu.id) validToolCallIds.add(tu.id);
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
const filteredMsgs = msgs.filter((m) => {
|
|
1324
|
+
if (m?.role === "tool") {
|
|
1325
|
+
const toolCallId = m.tool_call_id;
|
|
1326
|
+
if (!toolCallId || !validToolCallIds.has(toolCallId)) {
|
|
1327
|
+
return false;
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
return true;
|
|
1331
|
+
});
|
|
1332
|
+
for (let i = 0; i < filteredMsgs.length; i++) {
|
|
1333
|
+
const m = filteredMsgs[i];
|
|
1108
1334
|
if (m?.role !== "assistant") continue;
|
|
1109
1335
|
const toolUses = extractToolUses(m);
|
|
1110
1336
|
if (toolUses.length === 0) continue;
|
|
1111
|
-
const next =
|
|
1337
|
+
const next = filteredMsgs[i + 1];
|
|
1112
1338
|
const firstId = toolUses[0]?.id;
|
|
1113
1339
|
const nextIsToolForFirst = !!(next && next.role === "tool" && next.tool_call_id === firstId);
|
|
1114
1340
|
if (nextIsToolForFirst) continue;
|
|
@@ -1116,12 +1342,12 @@ function createAgentCoreNode(opts) {
|
|
|
1116
1342
|
role: "tool",
|
|
1117
1343
|
name: tu.name || "unknown_tool",
|
|
1118
1344
|
tool_call_id: tu.id,
|
|
1119
|
-
content: "SUMMARIZED/DEFERRED: tool result missing in transcript; inserted placeholder for
|
|
1345
|
+
content: "SUMMARIZED/DEFERRED: tool result missing in transcript; inserted placeholder for tool_result adjacency."
|
|
1120
1346
|
}));
|
|
1121
|
-
|
|
1347
|
+
filteredMsgs.splice(i + 1, 0, ...placeholders);
|
|
1122
1348
|
i += placeholders.length;
|
|
1123
1349
|
}
|
|
1124
|
-
return
|
|
1350
|
+
return filteredMsgs;
|
|
1125
1351
|
};
|
|
1126
1352
|
const debugToolPairing = (messagesToSend) => {
|
|
1127
1353
|
try {
|
|
@@ -1143,11 +1369,54 @@ function createAgentCoreNode(opts) {
|
|
|
1143
1369
|
} catch (e) {
|
|
1144
1370
|
}
|
|
1145
1371
|
};
|
|
1372
|
+
const onEvent = state.ctx?.__onEvent;
|
|
1373
|
+
const onStream = state.ctx?.__onStream;
|
|
1374
|
+
const streamingEnabled = Boolean(state.ctx?.__streaming);
|
|
1375
|
+
const cancellationToken = state.ctx?.__cancellationToken;
|
|
1376
|
+
const abortSignal = state.ctx?.__abortSignal;
|
|
1377
|
+
const extractText = (chunk) => {
|
|
1378
|
+
if (chunk == null) return "";
|
|
1379
|
+
if (typeof chunk === "string") return chunk;
|
|
1380
|
+
if (typeof chunk?.content === "string") return chunk.content;
|
|
1381
|
+
if (Array.isArray(chunk?.content)) {
|
|
1382
|
+
return chunk.content.map((c) => typeof c === "string" ? c : c?.text ?? c?.content ?? "").join("");
|
|
1383
|
+
}
|
|
1384
|
+
if (typeof chunk?.text === "string") return chunk.text;
|
|
1385
|
+
if (typeof chunk?.delta?.content === "string") return chunk.delta.content;
|
|
1386
|
+
return "";
|
|
1387
|
+
};
|
|
1146
1388
|
let response;
|
|
1147
1389
|
try {
|
|
1148
1390
|
const normalizedMessages = normalizeBedrockToolPairing([...state.messages]);
|
|
1149
1391
|
debugToolPairing(normalizedMessages);
|
|
1150
|
-
|
|
1392
|
+
if (streamingEnabled && typeof modelWithTools.stream === "function") {
|
|
1393
|
+
let streamedText = "";
|
|
1394
|
+
let streamedMessage;
|
|
1395
|
+
for await (const chunk of modelWithTools.stream(normalizedMessages, { signal: abortSignal, cancellationToken })) {
|
|
1396
|
+
if (cancellationToken && cancellationToken.isCancellationRequested || abortSignal?.aborted) {
|
|
1397
|
+
break;
|
|
1398
|
+
}
|
|
1399
|
+
if (chunk && typeof chunk === "object" && chunk.role) {
|
|
1400
|
+
streamedMessage = chunk;
|
|
1401
|
+
}
|
|
1402
|
+
const text = extractText(chunk);
|
|
1403
|
+
if (text) {
|
|
1404
|
+
streamedText += text;
|
|
1405
|
+
onStream?.({ text });
|
|
1406
|
+
onEvent?.({ type: "stream", text });
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
if (streamedMessage) {
|
|
1410
|
+
response = { ...streamedMessage };
|
|
1411
|
+
if (response.content == null || response.content === "") {
|
|
1412
|
+
response.content = streamedText;
|
|
1413
|
+
}
|
|
1414
|
+
} else {
|
|
1415
|
+
response = { role: "assistant", content: streamedText };
|
|
1416
|
+
}
|
|
1417
|
+
} else {
|
|
1418
|
+
response = await modelWithTools.invoke(normalizedMessages, { signal: abortSignal, cancellationToken });
|
|
1419
|
+
}
|
|
1151
1420
|
} catch (err) {
|
|
1152
1421
|
const durationMs2 = Date.now() - start;
|
|
1153
1422
|
recordTraceEvent(traceSession, {
|
|
@@ -1168,7 +1437,7 @@ function createAgentCoreNode(opts) {
|
|
|
1168
1437
|
...state.messages,
|
|
1169
1438
|
response
|
|
1170
1439
|
];
|
|
1171
|
-
const rawUsage = response?.usage || response?.response_metadata?.token_usage || response?.response_metadata?.usage;
|
|
1440
|
+
const rawUsage = response?.usage || response?.response_metadata?.token_usage || response?.response_metadata?.tokenUsage || response?.response_metadata?.usage;
|
|
1172
1441
|
const normalized = normalizeUsage(rawUsage);
|
|
1173
1442
|
const modelName = getModelName(runtime.model || opts.model) || "unknown_model";
|
|
1174
1443
|
const durationMs = Date.now() - start;
|
|
@@ -1235,6 +1504,9 @@ function createToolsNode(initialTools, opts) {
|
|
|
1235
1504
|
};
|
|
1236
1505
|
const appended = [];
|
|
1237
1506
|
const onEvent = state.ctx?.__onEvent;
|
|
1507
|
+
const onProgress = state.ctx?.__onProgress;
|
|
1508
|
+
const cancellationToken = state.ctx?.__cancellationToken;
|
|
1509
|
+
const abortSignal = state.ctx?.__abortSignal;
|
|
1238
1510
|
const traceSession = state.ctx?.__traceSession;
|
|
1239
1511
|
const pendingApprovals = Array.isArray(state.pendingApprovals) ? state.pendingApprovals.map((entry) => ({ ...entry })) : [];
|
|
1240
1512
|
const pendingByCallId = new Map(pendingApprovals.map((entry) => [entry.toolCallId, entry]));
|
|
@@ -1291,7 +1563,20 @@ function createToolsNode(initialTools, opts) {
|
|
|
1291
1563
|
name: tc.name
|
|
1292
1564
|
});
|
|
1293
1565
|
}
|
|
1566
|
+
const isCancelled = () => {
|
|
1567
|
+
if (abortSignal?.aborted) return { cancelled: true, reason: "aborted" };
|
|
1568
|
+
if (cancellationToken && cancellationToken.isCancellationRequested) return { cancelled: true, reason: "cancelled" };
|
|
1569
|
+
return { cancelled: false, reason: void 0 };
|
|
1570
|
+
};
|
|
1294
1571
|
const runOne = async (tc) => {
|
|
1572
|
+
const cancelState = isCancelled();
|
|
1573
|
+
if (cancelState.cancelled) {
|
|
1574
|
+
const ctx = state.ctx = state.ctx || {};
|
|
1575
|
+
ctx.__cancelled = { stage: "tools", reason: cancelState.reason, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
|
|
1576
|
+
onEvent?.({ type: "cancelled", stage: "tools", reason: cancelState.reason });
|
|
1577
|
+
onProgress?.({ stage: "tools", message: "Cancelled", detail: { reason: cancelState.reason } });
|
|
1578
|
+
return { status: "error" };
|
|
1579
|
+
}
|
|
1295
1580
|
const t = toolByName.get(tc.name);
|
|
1296
1581
|
if (!t) {
|
|
1297
1582
|
onEvent?.({ type: "tool_call", phase: "error", name: tc.name, id: tc.id, args: tc.args, error: { message: "Tool not found" } });
|
|
@@ -1370,15 +1655,17 @@ function createToolsNode(initialTools, opts) {
|
|
|
1370
1655
|
const start = Date.now();
|
|
1371
1656
|
try {
|
|
1372
1657
|
onEvent?.({ type: "tool_call", phase: "start", name: t.name, id: tc.id, args });
|
|
1658
|
+
onProgress?.({ stage: "tools", message: `Running tool ${tc.name}` });
|
|
1373
1659
|
const sanitizedArgs = sanitizeTracePayload(args);
|
|
1374
1660
|
const inputBytes = traceSession?.resolvedConfig.logData ? estimatePayloadBytes(sanitizedArgs) : void 0;
|
|
1375
1661
|
let output;
|
|
1376
1662
|
const anyTool = t;
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
else if (typeof anyTool.
|
|
1380
|
-
else if (typeof anyTool.
|
|
1381
|
-
else if (typeof anyTool.
|
|
1663
|
+
const callOptions = { cancellationToken, signal: abortSignal };
|
|
1664
|
+
if (typeof anyTool.func === "function") output = await anyTool.func(args, callOptions);
|
|
1665
|
+
else if (typeof anyTool.invoke === "function") output = await anyTool.invoke(args, callOptions);
|
|
1666
|
+
else if (typeof anyTool.call === "function") output = await anyTool.call(args, callOptions);
|
|
1667
|
+
else if (typeof anyTool._call === "function") output = await anyTool._call(args, callOptions);
|
|
1668
|
+
else if (typeof anyTool.run === "function") output = await anyTool.run(args, callOptions);
|
|
1382
1669
|
else throw new Error("Tool is not invokable");
|
|
1383
1670
|
if (output && typeof output === "object" && output.__handoff && output.__handoff.runtime) {
|
|
1384
1671
|
const newRuntime = output.__handoff.runtime;
|
|
@@ -1408,6 +1695,7 @@ function createToolsNode(initialTools, opts) {
|
|
|
1408
1695
|
toolHistory.push({ executionId, toolName: t.name, args, output, rawOutput: output, timestamp: (/* @__PURE__ */ new Date()).toISOString(), tool_call_id: tc.id });
|
|
1409
1696
|
appended.push({ role: "tool", content, tool_call_id: tc.id || `${tc.name}_${appended.length}`, name: tc.name });
|
|
1410
1697
|
onEvent?.({ type: "tool_call", phase: "success", name: t.name, id: tc.id, args, result: output, durationMs });
|
|
1698
|
+
onProgress?.({ stage: "tools", message: `Tool ${tc.name} completed`, detail: { durationMs } });
|
|
1411
1699
|
if (needsApproval && approvalEntry) {
|
|
1412
1700
|
approvalEntry.status = "executed";
|
|
1413
1701
|
approvalEntry.resolvedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -1457,6 +1745,7 @@ function createToolsNode(initialTools, opts) {
|
|
|
1457
1745
|
toolHistory.push({ executionId, toolName: t.name, args, output: `Error executing tool: ${e?.message || String(e)}`, rawOutput: null, timestamp: (/* @__PURE__ */ new Date()).toISOString(), tool_call_id: tc.id });
|
|
1458
1746
|
appended.push({ role: "tool", content: `Error executing tool: ${e?.message || String(e)}`, tool_call_id: tc.id || `${tc.name}_${appended.length}`, name: tc.name });
|
|
1459
1747
|
onEvent?.({ type: "tool_call", phase: "error", name: t.name, id: tc.id, args, error: { message: e?.message || String(e) } });
|
|
1748
|
+
onProgress?.({ stage: "tools", message: `Tool ${tc.name} failed`, detail: { error: e?.message || String(e) } });
|
|
1460
1749
|
const sanitizedArgs = sanitizeTracePayload(args);
|
|
1461
1750
|
const inputBytes = traceSession?.resolvedConfig.logData ? estimatePayloadBytes(sanitizedArgs) : void 0;
|
|
1462
1751
|
const toolName = t.name || tc.name;
|
|
@@ -1500,6 +1789,14 @@ function createToolsNode(initialTools, opts) {
|
|
|
1500
1789
|
};
|
|
1501
1790
|
for (const tc of planned) {
|
|
1502
1791
|
if (awaitingApproval) break;
|
|
1792
|
+
const cancelState = isCancelled();
|
|
1793
|
+
if (cancelState.cancelled) {
|
|
1794
|
+
const ctx = state.ctx = state.ctx || {};
|
|
1795
|
+
ctx.__cancelled = { stage: "tools", reason: cancelState.reason, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
|
|
1796
|
+
onEvent?.({ type: "cancelled", stage: "tools", reason: cancelState.reason });
|
|
1797
|
+
onProgress?.({ stage: "tools", message: "Cancelled", detail: { reason: cancelState.reason } });
|
|
1798
|
+
break;
|
|
1799
|
+
}
|
|
1503
1800
|
const result = await runOne(tc);
|
|
1504
1801
|
if (result?.status === "awaiting_approval") {
|
|
1505
1802
|
awaitingApproval = true;
|
|
@@ -1527,7 +1824,7 @@ function createToolsNode(initialTools, opts) {
|
|
|
1527
1824
|
// src/nodes/toolLimitFinalize.ts
|
|
1528
1825
|
function createToolLimitFinalizeNode(_opts) {
|
|
1529
1826
|
return async (state) => {
|
|
1530
|
-
const notice = { role: "
|
|
1827
|
+
const notice = { role: "user", content: "[System Notice] Tool-call limit reached. Produce the best possible final answer using the available context and prior tool outputs. Do not call any more tools." };
|
|
1531
1828
|
const ctx = { ...state.ctx || {}, __finalizedDueToToolLimit: true };
|
|
1532
1829
|
return { messages: [...state.messages, notice], ctx };
|
|
1533
1830
|
};
|
|
@@ -1804,12 +2101,37 @@ function createAgent(opts) {
|
|
|
1804
2101
|
delete nextCtx.__resumeStage;
|
|
1805
2102
|
state = { ...state, ctx: Object.keys(nextCtx).length > 0 ? nextCtx : void 0 };
|
|
1806
2103
|
}
|
|
1807
|
-
const
|
|
2104
|
+
const mergedLimits = {
|
|
2105
|
+
...opts.limits || {},
|
|
2106
|
+
...config?.limits || {}
|
|
2107
|
+
};
|
|
2108
|
+
const maxToolCalls = mergedLimits?.maxToolCalls === void 0 ? 50 : mergedLimits?.maxToolCalls;
|
|
1808
2109
|
const iterationLimit = maxToolCalls === Infinity ? 100 : Math.max(maxToolCalls * 3 + 10, 40);
|
|
1809
2110
|
let iterations = 0;
|
|
1810
2111
|
const onStateChange = config?.onStateChange;
|
|
1811
2112
|
const checkpointReason = config?.checkpointReason;
|
|
1812
2113
|
let pausedStage = null;
|
|
2114
|
+
const onProgress = state.ctx?.__onProgress;
|
|
2115
|
+
const isCancelled = () => {
|
|
2116
|
+
const ctx = state.ctx || {};
|
|
2117
|
+
const token = ctx.__cancellationToken;
|
|
2118
|
+
const signal = ctx.__abortSignal;
|
|
2119
|
+
const deadline = ctx.__deadline;
|
|
2120
|
+
if (signal?.aborted) return { cancelled: true, reason: "aborted" };
|
|
2121
|
+
if (token && token.isCancellationRequested) return { cancelled: true, reason: "cancelled" };
|
|
2122
|
+
if (deadline && Date.now() > deadline) return { cancelled: true, reason: "timeout" };
|
|
2123
|
+
return { cancelled: false, reason: void 0 };
|
|
2124
|
+
};
|
|
2125
|
+
const cancelIfRequested = (stage) => {
|
|
2126
|
+
const result = isCancelled();
|
|
2127
|
+
if (!result.cancelled) return false;
|
|
2128
|
+
const ctx = { ...state.ctx || {} };
|
|
2129
|
+
ctx.__cancelled = { stage, reason: result.reason, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
|
|
2130
|
+
state = { ...state, ctx };
|
|
2131
|
+
emit?.({ type: "cancelled", stage, reason: result.reason });
|
|
2132
|
+
onProgress?.({ stage, message: "Cancelled", detail: { reason: result.reason } });
|
|
2133
|
+
return true;
|
|
2134
|
+
};
|
|
1813
2135
|
const checkpointIfRequested = (stage) => {
|
|
1814
2136
|
if (typeof onStateChange !== "function") return false;
|
|
1815
2137
|
let result = false;
|
|
@@ -1832,8 +2154,10 @@ function createAgent(opts) {
|
|
|
1832
2154
|
};
|
|
1833
2155
|
while (iterations < iterationLimit) {
|
|
1834
2156
|
iterations++;
|
|
2157
|
+
if (cancelIfRequested("loop")) break;
|
|
1835
2158
|
const skippingAgent = resumeStage === "tools";
|
|
1836
2159
|
if (!skippingAgent) {
|
|
2160
|
+
if (cancelIfRequested("before_guardrails")) break;
|
|
1837
2161
|
if (checkpointIfRequested("before_guardrails")) break;
|
|
1838
2162
|
const preGuardrails = getGuardrailConfig(state);
|
|
1839
2163
|
if (preGuardrails.length > 0) {
|
|
@@ -1888,7 +2212,10 @@ function createAgent(opts) {
|
|
|
1888
2212
|
break;
|
|
1889
2213
|
}
|
|
1890
2214
|
}
|
|
2215
|
+
onProgress?.({ stage: "agent", message: "Invoking model" });
|
|
2216
|
+
if (cancelIfRequested("before_agent")) break;
|
|
1891
2217
|
state = { ...state, ...await agentCore(state) };
|
|
2218
|
+
onProgress?.({ stage: "agent", message: "Model response received" });
|
|
1892
2219
|
if (checkpointIfRequested("after_agent")) break;
|
|
1893
2220
|
const postGuardrails = getGuardrailConfig(state);
|
|
1894
2221
|
if (postGuardrails.length > 0) {
|
|
@@ -1956,7 +2283,10 @@ function createAgent(opts) {
|
|
|
1956
2283
|
continue;
|
|
1957
2284
|
}
|
|
1958
2285
|
if (toolCalls.length === 0) break;
|
|
2286
|
+
onProgress?.({ stage: "tools", message: "Running tools" });
|
|
2287
|
+
if (cancelIfRequested("before_tools")) break;
|
|
1959
2288
|
state = { ...state, ...await toolsNode(state) };
|
|
2289
|
+
onProgress?.({ stage: "tools", message: "Tools finished" });
|
|
1960
2290
|
if (state.ctx?.__awaitingApproval) break;
|
|
1961
2291
|
if (checkpointIfRequested("after_tools")) break;
|
|
1962
2292
|
if (state.ctx?.__finalizedDueToStructuredOutput) break;
|
|
@@ -1997,15 +2327,39 @@ function createAgent(opts) {
|
|
|
1997
2327
|
}
|
|
1998
2328
|
const invokeAgent = async (input, config) => {
|
|
1999
2329
|
const onEvent = config?.onEvent;
|
|
2330
|
+
const onProgress = config?.onProgress;
|
|
2331
|
+
const onStream = config?.onStream;
|
|
2332
|
+
const streamEnabled = config?.stream === true;
|
|
2000
2333
|
const emit = (e) => {
|
|
2001
2334
|
try {
|
|
2002
2335
|
onEvent?.(e);
|
|
2003
2336
|
} catch {
|
|
2004
2337
|
}
|
|
2005
2338
|
};
|
|
2339
|
+
const emitProgress = (progress) => {
|
|
2340
|
+
try {
|
|
2341
|
+
onProgress?.(progress);
|
|
2342
|
+
} catch {
|
|
2343
|
+
}
|
|
2344
|
+
emit({ type: "progress", ...progress });
|
|
2345
|
+
};
|
|
2006
2346
|
const traceSession = createTraceSession(opts);
|
|
2007
|
-
const ctx = {
|
|
2347
|
+
const ctx = {
|
|
2348
|
+
...input.ctx || {},
|
|
2349
|
+
__onEvent: onEvent,
|
|
2350
|
+
__onProgress: emitProgress,
|
|
2351
|
+
__onStream: onStream,
|
|
2352
|
+
__streaming: streamEnabled
|
|
2353
|
+
};
|
|
2008
2354
|
if (traceSession) ctx.__traceSession = traceSession;
|
|
2355
|
+
if (config?.cancellationToken) ctx.__cancellationToken = config.cancellationToken;
|
|
2356
|
+
if (config?.cancellationToken?.aborted !== void 0) {
|
|
2357
|
+
ctx.__abortSignal = config?.cancellationToken;
|
|
2358
|
+
}
|
|
2359
|
+
if (config?.timeoutMs && config.timeoutMs > 0) {
|
|
2360
|
+
ctx.__deadline = Date.now() + config.timeoutMs;
|
|
2361
|
+
}
|
|
2362
|
+
const runtimeWithInvokeLimits = config?.limits ? { ...runtime, limits: { ...runtime.limits || {}, ...config.limits } } : runtime;
|
|
2009
2363
|
const initial = {
|
|
2010
2364
|
messages: input.messages || [],
|
|
2011
2365
|
toolCallCount: input.toolCallCount || 0,
|
|
@@ -2014,7 +2368,7 @@ function createAgent(opts) {
|
|
|
2014
2368
|
metadata: input.metadata,
|
|
2015
2369
|
ctx,
|
|
2016
2370
|
pendingApprovals: input.pendingApprovals || [],
|
|
2017
|
-
agent: input.agent ||
|
|
2371
|
+
agent: input.agent || runtimeWithInvokeLimits,
|
|
2018
2372
|
usage: input.usage || { perRequest: [], totals: {} }
|
|
2019
2373
|
};
|
|
2020
2374
|
let res;
|
|
@@ -2057,6 +2411,10 @@ function createAgent(opts) {
|
|
|
2057
2411
|
}
|
|
2058
2412
|
}
|
|
2059
2413
|
emit({ type: "finalAnswer", content: typeof finalMsg?.content === "string" ? finalMsg.content : content });
|
|
2414
|
+
if (streamEnabled && content) {
|
|
2415
|
+
onStream?.({ text: content, isFinal: true });
|
|
2416
|
+
emit({ type: "stream", text: content, isFinal: true });
|
|
2417
|
+
}
|
|
2060
2418
|
return {
|
|
2061
2419
|
content,
|
|
2062
2420
|
output: parsed,
|
|
@@ -2227,6 +2585,31 @@ function createAgentNode(opts) {
|
|
|
2227
2585
|
// src/nodes/contextSummarize.ts
|
|
2228
2586
|
var systemMessage = (content) => ({ role: "system", content });
|
|
2229
2587
|
var humanMessage = (content) => ({ role: "user", content });
|
|
2588
|
+
function validateMessageSequence(messages) {
|
|
2589
|
+
const validated = [];
|
|
2590
|
+
const pendingToolCallIds = /* @__PURE__ */ new Set();
|
|
2591
|
+
for (const m of messages) {
|
|
2592
|
+
if (m.role === "assistant") {
|
|
2593
|
+
if (!m.tool_calls || m.tool_calls.length === 0) {
|
|
2594
|
+
pendingToolCallIds.clear();
|
|
2595
|
+
} else {
|
|
2596
|
+
m.tool_calls.forEach((tc) => {
|
|
2597
|
+
if (tc.id) pendingToolCallIds.add(tc.id);
|
|
2598
|
+
});
|
|
2599
|
+
}
|
|
2600
|
+
validated.push(m);
|
|
2601
|
+
} else if (m.role === "tool") {
|
|
2602
|
+
const toolCallId = m.tool_call_id;
|
|
2603
|
+
if (toolCallId && pendingToolCallIds.has(toolCallId)) {
|
|
2604
|
+
pendingToolCallIds.delete(toolCallId);
|
|
2605
|
+
validated.push(m);
|
|
2606
|
+
}
|
|
2607
|
+
} else {
|
|
2608
|
+
validated.push(m);
|
|
2609
|
+
}
|
|
2610
|
+
}
|
|
2611
|
+
return validated;
|
|
2612
|
+
}
|
|
2230
2613
|
function getSummarizationConfig(opts) {
|
|
2231
2614
|
if (typeof opts.summarization === "object") {
|
|
2232
2615
|
return {
|
|
@@ -2234,13 +2617,15 @@ function getSummarizationConfig(opts) {
|
|
|
2234
2617
|
maxTokens: opts.summarization.maxTokens ?? 5e4,
|
|
2235
2618
|
// Max tokens we can send to the model for summarization prompt itself
|
|
2236
2619
|
// This should be well under the model's context limit
|
|
2237
|
-
summaryPromptMaxTokens: opts.summarization.summaryPromptMaxTokens ?? 8e3
|
|
2620
|
+
summaryPromptMaxTokens: opts.summarization.summaryPromptMaxTokens ?? 8e3,
|
|
2621
|
+
promptTemplate: opts.summarization.promptTemplate
|
|
2238
2622
|
};
|
|
2239
2623
|
}
|
|
2240
2624
|
return {
|
|
2241
2625
|
enabled: opts.summarization !== false,
|
|
2242
2626
|
maxTokens: 5e4,
|
|
2243
|
-
summaryPromptMaxTokens: 8e3
|
|
2627
|
+
summaryPromptMaxTokens: 8e3,
|
|
2628
|
+
promptTemplate: void 0
|
|
2244
2629
|
};
|
|
2245
2630
|
}
|
|
2246
2631
|
function createContextSummarizeNode(opts) {
|
|
@@ -2249,10 +2634,14 @@ function createContextSummarizeNode(opts) {
|
|
|
2249
2634
|
if (!config.enabled) return {};
|
|
2250
2635
|
const model = opts.model;
|
|
2251
2636
|
const messages = state.messages || [];
|
|
2637
|
+
const onEvent = state.ctx?.__onEvent;
|
|
2638
|
+
const traceSession = state.ctx?.__traceSession;
|
|
2252
2639
|
const compressableMessages = messages.filter((m) => m.role === "tool" && m.content !== "SUMMARIZED");
|
|
2253
2640
|
if (compressableMessages.length === 0) {
|
|
2254
2641
|
return {};
|
|
2255
2642
|
}
|
|
2643
|
+
const allTextBefore = messages.map((m) => typeof m.content === "string" ? m.content : Array.isArray(m.content) ? m.content.map((c) => typeof c === "string" ? c : c?.text ?? c?.content ?? "").join("") : "").join("\n");
|
|
2644
|
+
const tokenCountBefore = countApproxTokens(allTextBefore);
|
|
2256
2645
|
const summaryPromptMaxTokens = config.summaryPromptMaxTokens;
|
|
2257
2646
|
const availableTokensForConversation = summaryPromptMaxTokens - 500;
|
|
2258
2647
|
const messageTexts = [];
|
|
@@ -2280,32 +2669,93 @@ function createContextSummarizeNode(opts) {
|
|
|
2280
2669
|
accumulatedTokens += messageTokens;
|
|
2281
2670
|
}
|
|
2282
2671
|
const conversationText = messageTexts.join("\n\n");
|
|
2283
|
-
const
|
|
2284
|
-
|
|
2285
|
-
humanMessage(
|
|
2286
|
-
`Please summarize the following conversation history.
|
|
2672
|
+
const previousSummary = Array.isArray(state.summaries) && state.summaries.length > 0 ? state.summaries[state.summaries.length - 1] : "";
|
|
2673
|
+
const defaultPrompt = `Please summarize the following conversation history and update any previous summary.
|
|
2287
2674
|
Focus on:
|
|
2288
2675
|
- User goals and intent.
|
|
2289
2676
|
- Key decisions made and actions taken.
|
|
2290
2677
|
- Important tool outputs and data retrieved.
|
|
2291
2678
|
- Current state of the task.
|
|
2292
2679
|
|
|
2680
|
+
Previous summary (if any):
|
|
2681
|
+
${previousSummary || "(none)"}
|
|
2682
|
+
|
|
2293
2683
|
Conversation:
|
|
2294
2684
|
${conversationText}
|
|
2295
2685
|
|
|
2296
|
-
Summary
|
|
2297
|
-
|
|
2686
|
+
Summary:`;
|
|
2687
|
+
const template = config.promptTemplate || defaultPrompt;
|
|
2688
|
+
const promptBody = template.replace(/\{\{\s*conversation\s*\}\}/g, conversationText).replace(/\{\{\s*previousSummary\s*\}\}/g, previousSummary || "");
|
|
2689
|
+
const summaryPrompt = [
|
|
2690
|
+
systemMessage("You are a helpful assistant that summarizes conversation history efficiently."),
|
|
2691
|
+
humanMessage(promptBody)
|
|
2298
2692
|
];
|
|
2693
|
+
const inputTokensEstimate = countApproxTokens(summaryPrompt.map((m) => m.content).join("\n"));
|
|
2299
2694
|
let summaryText = "Summary unavailable.";
|
|
2695
|
+
let outputTokensEstimate = 0;
|
|
2696
|
+
let durationMs = 0;
|
|
2697
|
+
let inputTokensActual;
|
|
2698
|
+
let outputTokensActual;
|
|
2699
|
+
let cachedInputTokens;
|
|
2700
|
+
let totalTokensActual;
|
|
2701
|
+
const startTime = Date.now();
|
|
2300
2702
|
try {
|
|
2301
2703
|
const response = await model.invoke(summaryPrompt);
|
|
2704
|
+
durationMs = Date.now() - startTime;
|
|
2302
2705
|
summaryText = typeof response.content === "string" ? response.content : Array.isArray(response.content) ? response.content.map((c) => c.text || "").join("") : JSON.stringify(response.content);
|
|
2706
|
+
outputTokensEstimate = countApproxTokens(summaryText);
|
|
2707
|
+
const rawUsage = response?.usage || response?.response_metadata?.token_usage || response?.response_metadata?.tokenUsage || response?.response_metadata?.usage;
|
|
2708
|
+
const normalized = normalizeUsage(rawUsage);
|
|
2709
|
+
if (normalized) {
|
|
2710
|
+
inputTokensActual = normalized.prompt_tokens;
|
|
2711
|
+
outputTokensActual = normalized.completion_tokens;
|
|
2712
|
+
totalTokensActual = normalized.total_tokens;
|
|
2713
|
+
cachedInputTokens = normalized.prompt_tokens_details?.cached_tokens;
|
|
2714
|
+
}
|
|
2303
2715
|
} catch (err) {
|
|
2716
|
+
durationMs = Date.now() - startTime;
|
|
2304
2717
|
console.error("[ContextSummarize] Failed to generate summary:", err);
|
|
2718
|
+
const errorEvent = {
|
|
2719
|
+
type: "summarization",
|
|
2720
|
+
summary: "",
|
|
2721
|
+
messagesCompressed: 0,
|
|
2722
|
+
inputTokens: inputTokensEstimate,
|
|
2723
|
+
outputTokens: 0,
|
|
2724
|
+
durationMs,
|
|
2725
|
+
previousSummary: previousSummary || void 0,
|
|
2726
|
+
tokenCountBefore,
|
|
2727
|
+
tokenCountAfter: tokenCountBefore
|
|
2728
|
+
};
|
|
2729
|
+
onEvent?.(errorEvent);
|
|
2730
|
+
recordTraceEvent(traceSession, {
|
|
2731
|
+
type: "summarization",
|
|
2732
|
+
label: "Context Summarization Failed",
|
|
2733
|
+
actor: { scope: "system", name: "summarizer", role: "summarization" },
|
|
2734
|
+
status: "error",
|
|
2735
|
+
durationMs,
|
|
2736
|
+
inputTokens: inputTokensEstimate,
|
|
2737
|
+
outputTokens: 0,
|
|
2738
|
+
error: { message: err?.message || String(err), stack: err?.stack },
|
|
2739
|
+
messageList: sanitizeTracePayload(summaryPrompt)
|
|
2740
|
+
});
|
|
2305
2741
|
return {};
|
|
2306
2742
|
}
|
|
2743
|
+
const toolCallIdToAssistantIdx = /* @__PURE__ */ new Map();
|
|
2744
|
+
messages.forEach((m, idx) => {
|
|
2745
|
+
if (m.role === "assistant" && m.tool_calls && Array.isArray(m.tool_calls)) {
|
|
2746
|
+
m.tool_calls.forEach((tc) => {
|
|
2747
|
+
if (tc.id) {
|
|
2748
|
+
toolCallIdToAssistantIdx.set(tc.id, idx);
|
|
2749
|
+
}
|
|
2750
|
+
});
|
|
2751
|
+
}
|
|
2752
|
+
});
|
|
2307
2753
|
const newMessages = messages.map((m) => {
|
|
2308
2754
|
if (m.role === "tool") {
|
|
2755
|
+
const toolCallId = m.tool_call_id;
|
|
2756
|
+
if (!toolCallId || !toolCallIdToAssistantIdx.has(toolCallId)) {
|
|
2757
|
+
return null;
|
|
2758
|
+
}
|
|
2309
2759
|
if (m.content === "SUMMARIZED") return m;
|
|
2310
2760
|
return {
|
|
2311
2761
|
...m,
|
|
@@ -2313,7 +2763,8 @@ Summary:`
|
|
|
2313
2763
|
};
|
|
2314
2764
|
}
|
|
2315
2765
|
return m;
|
|
2316
|
-
});
|
|
2766
|
+
}).filter((m) => m !== null);
|
|
2767
|
+
const validatedMessages = validateMessageSequence(newMessages);
|
|
2317
2768
|
const summaryToolCallId = `call_summary_${Date.now()}`;
|
|
2318
2769
|
const assistantSummaryCall = {
|
|
2319
2770
|
role: "assistant",
|
|
@@ -2337,8 +2788,53 @@ Summary:`
|
|
|
2337
2788
|
// Matches the tool name in the call
|
|
2338
2789
|
content: summaryText
|
|
2339
2790
|
};
|
|
2791
|
+
const summaries = Array.isArray(state.summaries) ? [...state.summaries, summaryText] : [summaryText];
|
|
2792
|
+
const finalMessages = [...validatedMessages, assistantSummaryCall, toolSummaryResponse];
|
|
2793
|
+
const allTextAfter = finalMessages.map((m) => typeof m.content === "string" ? m.content : Array.isArray(m.content) ? m.content.map((c) => typeof c === "string" ? c : c?.text ?? c?.content ?? "").join("") : "").join("\n");
|
|
2794
|
+
const tokenCountAfter = countApproxTokens(allTextAfter);
|
|
2795
|
+
const summarizationEvent = {
|
|
2796
|
+
type: "summarization",
|
|
2797
|
+
summary: summaryText,
|
|
2798
|
+
messagesCompressed: compressableMessages.length,
|
|
2799
|
+
inputTokens: inputTokensActual ?? inputTokensEstimate,
|
|
2800
|
+
outputTokens: outputTokensActual ?? outputTokensEstimate,
|
|
2801
|
+
cachedInputTokens,
|
|
2802
|
+
totalTokens: totalTokensActual ?? (inputTokensActual ?? inputTokensEstimate) + (outputTokensActual ?? outputTokensEstimate),
|
|
2803
|
+
durationMs,
|
|
2804
|
+
previousSummary: previousSummary || void 0,
|
|
2805
|
+
tokenCountBefore,
|
|
2806
|
+
tokenCountAfter,
|
|
2807
|
+
archivedCount: compressableMessages.length
|
|
2808
|
+
// deprecated field for backward compatibility
|
|
2809
|
+
};
|
|
2810
|
+
onEvent?.(summarizationEvent);
|
|
2811
|
+
recordTraceEvent(traceSession, {
|
|
2812
|
+
type: "summarization",
|
|
2813
|
+
label: "Context Summarization",
|
|
2814
|
+
actor: { scope: "system", name: "summarizer", role: "summarization" },
|
|
2815
|
+
status: "success",
|
|
2816
|
+
durationMs,
|
|
2817
|
+
inputTokens: inputTokensActual ?? inputTokensEstimate,
|
|
2818
|
+
outputTokens: outputTokensActual ?? outputTokensEstimate,
|
|
2819
|
+
cachedInputTokens,
|
|
2820
|
+
totalTokens: totalTokensActual,
|
|
2821
|
+
messageList: sanitizeTracePayload([
|
|
2822
|
+
...summaryPrompt,
|
|
2823
|
+
{ role: "assistant", content: summaryText, name: "summarization_result" }
|
|
2824
|
+
]),
|
|
2825
|
+
debug: {
|
|
2826
|
+
messagesCompressed: compressableMessages.length,
|
|
2827
|
+
tokenCountBefore,
|
|
2828
|
+
tokenCountAfter,
|
|
2829
|
+
tokensSaved: tokenCountBefore - tokenCountAfter,
|
|
2830
|
+
previousSummaryLength: previousSummary?.length || 0,
|
|
2831
|
+
usageFromModel: inputTokensActual !== void 0,
|
|
2832
|
+
cachedInputTokens
|
|
2833
|
+
}
|
|
2834
|
+
});
|
|
2340
2835
|
return {
|
|
2341
|
-
messages:
|
|
2836
|
+
messages: finalMessages,
|
|
2837
|
+
summaries
|
|
2342
2838
|
};
|
|
2343
2839
|
};
|
|
2344
2840
|
}
|
|
@@ -2443,9 +2939,29 @@ function needsSummarization(state, opts, summarizationEnabled) {
|
|
|
2443
2939
|
maxTok = 5e4;
|
|
2444
2940
|
}
|
|
2445
2941
|
try {
|
|
2446
|
-
const allText = (state.messages || []).map((m) =>
|
|
2942
|
+
const allText = (state.messages || []).map((m) => {
|
|
2943
|
+
if (typeof m.content === "string") return m.content;
|
|
2944
|
+
if (Array.isArray(m.content)) {
|
|
2945
|
+
return m.content.map((c) => typeof c === "string" ? c : c?.text ?? c?.content ?? JSON.stringify(c)).join("");
|
|
2946
|
+
}
|
|
2947
|
+
if (m.content && typeof m.content === "object") {
|
|
2948
|
+
return JSON.stringify(m.content);
|
|
2949
|
+
}
|
|
2950
|
+
return "";
|
|
2951
|
+
}).join("\n");
|
|
2447
2952
|
const tokenCount = countApproxTokens(allText);
|
|
2448
2953
|
const needsSum = tokenCount > maxTok;
|
|
2954
|
+
if (process.env.DEBUG_SUMMARIZATION) {
|
|
2955
|
+
const msgCount = (state.messages || []).length;
|
|
2956
|
+
const toolMsgs = (state.messages || []).filter((m) => m.role === "tool");
|
|
2957
|
+
const toolDetails = toolMsgs.map((m, i) => {
|
|
2958
|
+
const contentStr = typeof m.content === "string" ? m.content : JSON.stringify(m.content);
|
|
2959
|
+
return `[${i}] id:${m.tool_call_id?.slice(-8) || "none"} len:${contentStr.length} preview:"${contentStr.slice(0, 30)}..."`;
|
|
2960
|
+
});
|
|
2961
|
+
console.log(`[Summarization] Messages: ${msgCount}, Tool messages: ${toolMsgs.length}`);
|
|
2962
|
+
toolDetails.forEach((d) => console.log(` ${d}`));
|
|
2963
|
+
console.log(`[Summarization] Token count: ${tokenCount}, Max: ${maxTok}, Needs summarization: ${needsSum}`);
|
|
2964
|
+
}
|
|
2449
2965
|
return needsSum;
|
|
2450
2966
|
} catch (e) {
|
|
2451
2967
|
return false;
|
|
@@ -2503,7 +3019,8 @@ function createSmartAgent(opts) {
|
|
|
2503
3019
|
const seedMessages = alreadyHasSystem ? [...input.messages || []] : [systemMessage2(), ...input.messages || []];
|
|
2504
3020
|
let state = { ...input, messages: seedMessages };
|
|
2505
3021
|
let lastResult = null;
|
|
2506
|
-
const
|
|
3022
|
+
const effectiveMaxToolCalls = config?.limits?.maxToolCalls ?? opts.limits?.maxToolCalls ?? 10;
|
|
3023
|
+
const iterationLimit = Math.max(effectiveMaxToolCalls * 3 + 5, 30);
|
|
2507
3024
|
for (let i = 0; i < iterationLimit; i++) {
|
|
2508
3025
|
const next = summarizationEnabled ? decideBefore(state) : "agent";
|
|
2509
3026
|
if (next === "contextSummarize" && summarizer) {
|
|
@@ -2515,7 +3032,11 @@ function createSmartAgent(opts) {
|
|
|
2515
3032
|
}
|
|
2516
3033
|
const res = await base.invoke(state, config);
|
|
2517
3034
|
lastResult = res;
|
|
3035
|
+
const currentSummaries = state.summaries;
|
|
2518
3036
|
state = res.state || { ...state, messages: res.messages };
|
|
3037
|
+
if (currentSummaries && currentSummaries.length > 0 && (!state.summaries || state.summaries.length === 0)) {
|
|
3038
|
+
state = { ...state, summaries: currentSummaries };
|
|
3039
|
+
}
|
|
2519
3040
|
if (state.ctx?.__needsSummarization && summarizer) {
|
|
2520
3041
|
const ctx = { ...state.ctx || {} };
|
|
2521
3042
|
delete ctx.__needsSummarization;
|
|
@@ -2543,6 +3064,13 @@ function createSmartAgent(opts) {
|
|
|
2543
3064
|
const res = await base.invoke(state, config);
|
|
2544
3065
|
lastResult = res;
|
|
2545
3066
|
}
|
|
3067
|
+
if (state.summaries && state.summaries.length > 0) {
|
|
3068
|
+
if (lastResult.state) {
|
|
3069
|
+
lastResult = { ...lastResult, state: { ...lastResult.state, summaries: state.summaries } };
|
|
3070
|
+
} else {
|
|
3071
|
+
lastResult = { ...lastResult, state: { ...state, summaries: state.summaries } };
|
|
3072
|
+
}
|
|
3073
|
+
}
|
|
2546
3074
|
return lastResult;
|
|
2547
3075
|
},
|
|
2548
3076
|
snapshot: base.snapshot,
|
|
@@ -2832,6 +3360,6 @@ function createCodeGuardrail(options) {
|
|
|
2832
3360
|
});
|
|
2833
3361
|
}
|
|
2834
3362
|
|
|
2835
|
-
export { GuardrailPhase, agentVerdictRule, applyTokenLimits, buildSystemPrompt, captureSnapshot, codePresenceRule, cognipeerSink, countApproxTokens, createAgent, createAgentNode, createCodeGuardrail, createContextSummarizeNode, createContextTools, createGuardrail, createJsonGuardrail, createRegexGuardrail, createResolverNode, createSmartAgent, createTool, createToolLimitFinalizeNode, createToolsNode, customCallbackRule, customSink, evaluateGuardrails, fileSink, fromLangchainModel, fromLangchainTools, httpSink, isSmartModel, jsonSchemaRule, regexRule, resolveToolApprovalState, restoreSnapshot, withTools };
|
|
3363
|
+
export { GuardrailPhase, agentVerdictRule, applyTokenLimits, buildSystemPrompt, captureSnapshot, codePresenceRule, cognipeerSink, countApproxTokens, createAgent, createAgentNode, createCodeGuardrail, createContextSummarizeNode, createContextTools, createGuardrail, createJsonGuardrail, createRegexGuardrail, createResolverNode, createSmartAgent, createTool, createToolLimitFinalizeNode, createToolsNode, customCallbackRule, customSink, evaluateGuardrails, fileSink, fromLangchainModel, fromLangchainTools, httpSink, isSmartModel, jsonSchemaRule, regexRule, resolveToolApprovalState, restoreSnapshot, startStreamingSession, withTools };
|
|
2836
3364
|
//# sourceMappingURL=index.mjs.map
|
|
2837
3365
|
//# sourceMappingURL=index.mjs.map
|