@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.cjs
CHANGED
|
@@ -143,7 +143,7 @@ function withTools(model, tools) {
|
|
|
143
143
|
function fromLangchainModel(lcModel) {
|
|
144
144
|
if (!lcModel) throw new Error("fromLangchainModel: model is undefined/null");
|
|
145
145
|
const adapted = {
|
|
146
|
-
invoke: async (messages) => {
|
|
146
|
+
invoke: async (messages, options) => {
|
|
147
147
|
const normalizeContent = (content) => {
|
|
148
148
|
if (content == null) return content;
|
|
149
149
|
if (typeof content === "string") return content;
|
|
@@ -176,7 +176,7 @@ function fromLangchainModel(lcModel) {
|
|
|
176
176
|
return { role, content: normalizeContent(m.content), name: m.name, tool_calls: m.tool_calls, tool_call_id: m.tool_call_id };
|
|
177
177
|
};
|
|
178
178
|
const lcMessages = messages.map(toLC);
|
|
179
|
-
const response = await lcModel.invoke(lcMessages);
|
|
179
|
+
const response = await lcModel.invoke(lcMessages, options);
|
|
180
180
|
if (response && typeof response === "object") {
|
|
181
181
|
const content = response.content ?? response.text ?? "";
|
|
182
182
|
return {
|
|
@@ -190,6 +190,43 @@ function fromLangchainModel(lcModel) {
|
|
|
190
190
|
}
|
|
191
191
|
return { role: "assistant", content: String(response ?? "") };
|
|
192
192
|
},
|
|
193
|
+
stream: async function* (messages, options) {
|
|
194
|
+
if (typeof lcModel.stream !== "function") return;
|
|
195
|
+
const normalizeContent = (content) => {
|
|
196
|
+
if (content == null) return content;
|
|
197
|
+
if (typeof content === "string") return content;
|
|
198
|
+
if (Array.isArray(content)) {
|
|
199
|
+
return content.map((part) => {
|
|
200
|
+
if (!part || typeof part !== "object") return part;
|
|
201
|
+
if (part.type === "text") return { type: "text", text: String(part.text ?? "") };
|
|
202
|
+
if (part.type === "image_url") {
|
|
203
|
+
const img = part.image_url;
|
|
204
|
+
if (typeof img === "string") return { type: "image_url", image_url: { url: img } };
|
|
205
|
+
if (img && typeof img === "object") {
|
|
206
|
+
if ("url" in img) return { type: "image_url", image_url: img };
|
|
207
|
+
if ("base64" in img) {
|
|
208
|
+
const media = img.media_type || "image/jpeg";
|
|
209
|
+
const dataUrl = `data:${media};base64,${img.base64}`;
|
|
210
|
+
const detail = img.detail;
|
|
211
|
+
return { type: "image_url", image_url: { url: dataUrl, detail } };
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return part;
|
|
215
|
+
}
|
|
216
|
+
return part;
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
return content;
|
|
220
|
+
};
|
|
221
|
+
const toLC = (m) => {
|
|
222
|
+
if (m._getType || m.lc_serializable) return m;
|
|
223
|
+
return { role: m.role, content: normalizeContent(m.content), name: m.name, tool_calls: m.tool_calls, tool_call_id: m.tool_call_id };
|
|
224
|
+
};
|
|
225
|
+
const lcMessages = messages.map(toLC);
|
|
226
|
+
for await (const chunk of lcModel.stream(lcMessages, options)) {
|
|
227
|
+
yield chunk;
|
|
228
|
+
}
|
|
229
|
+
},
|
|
193
230
|
bindTools: (tools) => {
|
|
194
231
|
const lcReady = toLangchainTools(tools);
|
|
195
232
|
if (typeof lcModel.bindTools === "function") {
|
|
@@ -343,7 +380,7 @@ function safe(v) {
|
|
|
343
380
|
const n = Number(v);
|
|
344
381
|
return Number.isFinite(n) && n >= 0 ? n : 0;
|
|
345
382
|
}
|
|
346
|
-
var DEFAULT_COGNIPEER_URL = "https://
|
|
383
|
+
var DEFAULT_COGNIPEER_URL = "https://console.cognipeer.com/api/client/v1/tracing/sessions";
|
|
347
384
|
function fileSink(path2) {
|
|
348
385
|
return { type: "file", path: path2 };
|
|
349
386
|
}
|
|
@@ -401,133 +438,113 @@ function coerceToString(value) {
|
|
|
401
438
|
}
|
|
402
439
|
return void 0;
|
|
403
440
|
}
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
441
|
+
var METADATA_PATHS = {
|
|
442
|
+
model: [
|
|
443
|
+
"model",
|
|
444
|
+
"options.model",
|
|
445
|
+
"params.model",
|
|
446
|
+
"config.model",
|
|
447
|
+
"configuration.model",
|
|
448
|
+
"metadata.model",
|
|
449
|
+
"lc_kwargs.model",
|
|
450
|
+
"metadata.modelName",
|
|
451
|
+
"modelName",
|
|
452
|
+
"lc_alias",
|
|
453
|
+
"model_id",
|
|
454
|
+
"id",
|
|
455
|
+
"_model",
|
|
456
|
+
"_modelName",
|
|
457
|
+
"__model",
|
|
458
|
+
"_modelId",
|
|
459
|
+
"_llmType"
|
|
460
|
+
],
|
|
461
|
+
provider: [
|
|
462
|
+
"provider",
|
|
463
|
+
"options.provider",
|
|
464
|
+
"params.provider",
|
|
465
|
+
"config.provider",
|
|
466
|
+
"configuration.provider",
|
|
467
|
+
"metadata.provider",
|
|
468
|
+
"lc_kwargs.provider"
|
|
469
|
+
]
|
|
470
|
+
};
|
|
471
|
+
function getNestedValue(obj, path2) {
|
|
472
|
+
if (!obj || typeof path2 !== "string") return void 0;
|
|
473
|
+
return path2.split(".").reduce((current, key) => current?.[key], obj);
|
|
410
474
|
}
|
|
411
|
-
function
|
|
475
|
+
function extractMetadata(model, paths) {
|
|
412
476
|
if (!model) return void 0;
|
|
477
|
+
for (const path2 of paths) {
|
|
478
|
+
const value = getNestedValue(model, path2);
|
|
479
|
+
const str = coerceToString(value);
|
|
480
|
+
if (str) return str;
|
|
481
|
+
}
|
|
413
482
|
const maybeLc = model?._lc;
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
model?.lc_kwargs?.model,
|
|
422
|
-
maybeLc?.model,
|
|
423
|
-
maybeLc?.options?.model,
|
|
424
|
-
maybeLc?.params?.model,
|
|
425
|
-
maybeLc?.config?.model,
|
|
426
|
-
maybeLc?.configuration?.model,
|
|
427
|
-
maybeLc?.metadata?.model,
|
|
428
|
-
maybeLc?.lc_kwargs?.model,
|
|
429
|
-
model?.metadata?.modelName,
|
|
430
|
-
maybeLc?.metadata?.modelName,
|
|
431
|
-
model?.modelName,
|
|
432
|
-
maybeLc?.modelName,
|
|
433
|
-
model?.lc_alias,
|
|
434
|
-
model?.model_id,
|
|
435
|
-
model?.id,
|
|
436
|
-
maybeLc?.model_id,
|
|
437
|
-
maybeLc?.id,
|
|
438
|
-
model?._model,
|
|
439
|
-
model?._modelName,
|
|
440
|
-
model?.__model
|
|
441
|
-
);
|
|
442
|
-
if (direct) return direct;
|
|
483
|
+
if (maybeLc) {
|
|
484
|
+
for (const path2 of paths) {
|
|
485
|
+
const value = getNestedValue(maybeLc, path2);
|
|
486
|
+
const str = coerceToString(value);
|
|
487
|
+
if (str) return str;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
443
490
|
const maybeClient = model?.client || model?.api || model?.service;
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
const
|
|
452
|
-
|
|
491
|
+
if (maybeClient) {
|
|
492
|
+
for (const path2 of paths) {
|
|
493
|
+
const value = getNestedValue(maybeClient, path2);
|
|
494
|
+
const str = coerceToString(value);
|
|
495
|
+
if (str) return str;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
const constructorName = model?.constructor?.name;
|
|
499
|
+
if (constructorName && !["Object", "Function"].includes(constructorName)) {
|
|
500
|
+
return constructorName;
|
|
501
|
+
}
|
|
502
|
+
return void 0;
|
|
503
|
+
}
|
|
504
|
+
function getModelName(model) {
|
|
505
|
+
return extractMetadata(model, METADATA_PATHS.model);
|
|
453
506
|
}
|
|
454
507
|
function getProviderName(model) {
|
|
455
|
-
|
|
456
|
-
const direct = firstNonEmptyString(
|
|
457
|
-
model?.provider,
|
|
458
|
-
model?.options?.provider,
|
|
459
|
-
model?.params?.provider,
|
|
460
|
-
model?.config?.provider,
|
|
461
|
-
model?.configuration?.provider,
|
|
462
|
-
model?.metadata?.provider,
|
|
463
|
-
model?.lc_kwargs?.provider,
|
|
464
|
-
model?.lc_alias,
|
|
465
|
-
model?.__provider
|
|
466
|
-
);
|
|
467
|
-
if (direct) return direct;
|
|
468
|
-
const maybeLc = model?._lc;
|
|
469
|
-
const lcValue = maybeLc ? firstNonEmptyString(
|
|
470
|
-
maybeLc?.provider,
|
|
471
|
-
maybeLc?.options?.provider,
|
|
472
|
-
maybeLc?.config?.provider,
|
|
473
|
-
maybeLc?.configuration?.provider,
|
|
474
|
-
maybeLc?.metadata?.provider,
|
|
475
|
-
maybeLc?.lc_kwargs?.provider,
|
|
476
|
-
maybeLc?.client?.config?.provider
|
|
477
|
-
) : void 0;
|
|
478
|
-
if (lcValue) return lcValue;
|
|
479
|
-
const maybeClient = model?.client || model?.api || model?.service;
|
|
480
|
-
return firstNonEmptyString(
|
|
481
|
-
maybeClient?.provider,
|
|
482
|
-
maybeClient?.config?.provider,
|
|
483
|
-
maybeClient?.options?.provider,
|
|
484
|
-
maybeClient?.metadata?.provider
|
|
485
|
-
);
|
|
508
|
+
return extractMetadata(model, METADATA_PATHS.provider);
|
|
486
509
|
}
|
|
487
510
|
function inferModelFromMessages(messageList) {
|
|
488
511
|
if (!Array.isArray(messageList)) return void 0;
|
|
489
|
-
|
|
512
|
+
const messagePaths = [
|
|
513
|
+
"metadata.model",
|
|
514
|
+
"metadata.modelName",
|
|
515
|
+
"response_metadata.model",
|
|
516
|
+
"response_metadata.modelName",
|
|
517
|
+
"usage_metadata.model",
|
|
518
|
+
"model",
|
|
519
|
+
"modelName"
|
|
520
|
+
];
|
|
521
|
+
for (let i = messageList.length - 1; i >= 0; i--) {
|
|
490
522
|
const message = messageList[i];
|
|
491
523
|
if (!message) continue;
|
|
492
|
-
const
|
|
493
|
-
message
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
message?.response_metadata?.model,
|
|
498
|
-
message?.response_metadata?.modelName,
|
|
499
|
-
message?.response_metadata?.modelNames,
|
|
500
|
-
message?.response_metadata?.model_id,
|
|
501
|
-
message?.usage_metadata?.model,
|
|
502
|
-
message?.model,
|
|
503
|
-
message?.modelName,
|
|
504
|
-
message?.model_id,
|
|
505
|
-
message?.additional_kwargs?.model,
|
|
506
|
-
message?.additional_kwargs?.modelName,
|
|
507
|
-
message?.info?.model,
|
|
508
|
-
message?.annotations?.model
|
|
509
|
-
);
|
|
510
|
-
if (candidate) return candidate;
|
|
524
|
+
for (const path2 of messagePaths) {
|
|
525
|
+
const value = getNestedValue(message, path2);
|
|
526
|
+
const str = coerceToString(value);
|
|
527
|
+
if (str) return str;
|
|
528
|
+
}
|
|
511
529
|
}
|
|
512
530
|
return void 0;
|
|
513
531
|
}
|
|
514
532
|
function inferProviderFromMessages(messageList) {
|
|
515
533
|
if (!Array.isArray(messageList)) return void 0;
|
|
516
|
-
|
|
534
|
+
const messagePaths = [
|
|
535
|
+
"metadata.provider",
|
|
536
|
+
"response_metadata.provider",
|
|
537
|
+
"usage_metadata.provider",
|
|
538
|
+
"provider"
|
|
539
|
+
];
|
|
540
|
+
for (let i = messageList.length - 1; i >= 0; i--) {
|
|
517
541
|
const message = messageList[i];
|
|
518
542
|
if (!message) continue;
|
|
519
|
-
const
|
|
520
|
-
message
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
message?.usage_metadata?.provider,
|
|
525
|
-
message?.provider,
|
|
526
|
-
message?.additional_kwargs?.provider,
|
|
527
|
-
message?.info?.provider,
|
|
528
|
-
message?.annotations?.provider
|
|
529
|
-
);
|
|
530
|
-
if (candidate) return candidate;
|
|
543
|
+
for (const path2 of messagePaths) {
|
|
544
|
+
const value = getNestedValue(message, path2);
|
|
545
|
+
const str = coerceToString(value);
|
|
546
|
+
if (str) return str;
|
|
547
|
+
}
|
|
531
548
|
}
|
|
532
549
|
return void 0;
|
|
533
550
|
}
|
|
@@ -542,6 +559,7 @@ function ensureDir(p) {
|
|
|
542
559
|
function withDefaults(config) {
|
|
543
560
|
const enabled = Boolean(config?.enabled);
|
|
544
561
|
const logData = config?.logData ?? DEFAULT_TRACE_CONFIG.logData;
|
|
562
|
+
const mode = config?.mode === "streaming" ? "streaming" : "batched";
|
|
545
563
|
let sink;
|
|
546
564
|
try {
|
|
547
565
|
sink = resolveSink(config?.sink);
|
|
@@ -554,7 +572,7 @@ function withDefaults(config) {
|
|
|
554
572
|
return {
|
|
555
573
|
enabled,
|
|
556
574
|
logData,
|
|
557
|
-
mode
|
|
575
|
+
mode,
|
|
558
576
|
sink
|
|
559
577
|
};
|
|
560
578
|
}
|
|
@@ -625,20 +643,134 @@ async function postTraceSession(url, headers, payload) {
|
|
|
625
643
|
if (!Object.keys(finalHeaders).some((key) => key.toLowerCase() === "content-type")) {
|
|
626
644
|
finalHeaders["content-type"] = "application/json";
|
|
627
645
|
}
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
646
|
+
try {
|
|
647
|
+
const response = await fetchFn(url, {
|
|
648
|
+
method: "POST",
|
|
649
|
+
headers: finalHeaders,
|
|
650
|
+
body: JSON.stringify(payload)
|
|
651
|
+
});
|
|
652
|
+
if (!response.ok) {
|
|
653
|
+
let responseText = "";
|
|
654
|
+
try {
|
|
655
|
+
responseText = await response.text();
|
|
656
|
+
} catch {
|
|
657
|
+
}
|
|
658
|
+
const statusLine = `HTTP ${response.status}${response.statusText ? ` ${response.statusText}` : ""}`;
|
|
659
|
+
const bodyPreview = responseText ? ` - ${responseText.slice(0, 200)}` : "";
|
|
660
|
+
const errorMsg = `${statusLine}${bodyPreview}`;
|
|
661
|
+
if (typeof console !== "undefined" && typeof console.error === "function") {
|
|
662
|
+
console.error("[Tracing] Error posting to", url, ":", errorMsg);
|
|
663
|
+
}
|
|
664
|
+
throw new Error(errorMsg);
|
|
665
|
+
}
|
|
666
|
+
} catch (err) {
|
|
667
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
668
|
+
if (typeof console !== "undefined" && typeof console.error === "function") {
|
|
669
|
+
console.error("[Tracing] Failed to post:", errMsg);
|
|
670
|
+
}
|
|
671
|
+
throw err;
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
async function postStreamingSessionStart(baseUrl, headers, payload) {
|
|
675
|
+
const fetchFn = typeof globalThis.fetch === "function" ? globalThis.fetch.bind(globalThis) : void 0;
|
|
676
|
+
if (!fetchFn) {
|
|
677
|
+
throw new Error("HTTP sink requires fetch to be available in this runtime.");
|
|
678
|
+
}
|
|
679
|
+
const url = `${baseUrl.replace(/\/$/, "")}/${payload.sessionId}/start`;
|
|
680
|
+
const finalHeaders = { ...headers || {} };
|
|
681
|
+
if (!Object.keys(finalHeaders).some((key) => key.toLowerCase() === "content-type")) {
|
|
682
|
+
finalHeaders["content-type"] = "application/json";
|
|
683
|
+
}
|
|
684
|
+
try {
|
|
685
|
+
const response = await fetchFn(url, {
|
|
686
|
+
method: "POST",
|
|
687
|
+
headers: finalHeaders,
|
|
688
|
+
body: JSON.stringify(payload)
|
|
689
|
+
});
|
|
690
|
+
if (!response.ok) {
|
|
691
|
+
let responseText = "";
|
|
692
|
+
try {
|
|
693
|
+
responseText = await response.text();
|
|
694
|
+
} catch {
|
|
695
|
+
}
|
|
696
|
+
const statusLine = `HTTP ${response.status}${response.statusText ? ` ${response.statusText}` : ""}`;
|
|
697
|
+
const bodyPreview = responseText ? ` - ${responseText.slice(0, 200)}` : "";
|
|
698
|
+
throw new Error(`${statusLine}${bodyPreview}`);
|
|
699
|
+
}
|
|
700
|
+
} catch (err) {
|
|
701
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
702
|
+
if (typeof console !== "undefined" && typeof console.error === "function") {
|
|
703
|
+
console.error("[Tracing] Failed to post session start:", errMsg);
|
|
704
|
+
}
|
|
705
|
+
throw err;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
async function postStreamingEvent(baseUrl, headers, sessionId, event) {
|
|
709
|
+
const fetchFn = typeof globalThis.fetch === "function" ? globalThis.fetch.bind(globalThis) : void 0;
|
|
710
|
+
if (!fetchFn) {
|
|
711
|
+
throw new Error("HTTP sink requires fetch to be available in this runtime.");
|
|
712
|
+
}
|
|
713
|
+
const url = `${baseUrl.replace(/\/$/, "")}/${sessionId}/events`;
|
|
714
|
+
const finalHeaders = { ...headers || {} };
|
|
715
|
+
if (!Object.keys(finalHeaders).some((key) => key.toLowerCase() === "content-type")) {
|
|
716
|
+
finalHeaders["content-type"] = "application/json";
|
|
717
|
+
}
|
|
718
|
+
try {
|
|
719
|
+
const response = await fetchFn(url, {
|
|
720
|
+
method: "POST",
|
|
721
|
+
headers: finalHeaders,
|
|
722
|
+
body: JSON.stringify({ event })
|
|
723
|
+
});
|
|
724
|
+
if (!response.ok) {
|
|
725
|
+
let responseText = "";
|
|
726
|
+
try {
|
|
727
|
+
responseText = await response.text();
|
|
728
|
+
} catch {
|
|
729
|
+
}
|
|
730
|
+
const statusLine = `HTTP ${response.status}${response.statusText ? ` ${response.statusText}` : ""}`;
|
|
731
|
+
const bodyPreview = responseText ? ` - ${responseText.slice(0, 200)}` : "";
|
|
732
|
+
throw new Error(`${statusLine}${bodyPreview}`);
|
|
733
|
+
}
|
|
734
|
+
} catch (err) {
|
|
735
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
736
|
+
if (typeof console !== "undefined" && typeof console.error === "function") {
|
|
737
|
+
console.error("[Tracing] Failed to post streaming event:", errMsg);
|
|
738
|
+
}
|
|
739
|
+
throw err;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
async function postStreamingSessionEnd(baseUrl, headers, payload) {
|
|
743
|
+
const fetchFn = typeof globalThis.fetch === "function" ? globalThis.fetch.bind(globalThis) : void 0;
|
|
744
|
+
if (!fetchFn) {
|
|
745
|
+
throw new Error("HTTP sink requires fetch to be available in this runtime.");
|
|
746
|
+
}
|
|
747
|
+
const url = `${baseUrl.replace(/\/$/, "")}/${payload.sessionId}/end`;
|
|
748
|
+
const finalHeaders = { ...headers || {} };
|
|
749
|
+
if (!Object.keys(finalHeaders).some((key) => key.toLowerCase() === "content-type")) {
|
|
750
|
+
finalHeaders["content-type"] = "application/json";
|
|
751
|
+
}
|
|
752
|
+
try {
|
|
753
|
+
const response = await fetchFn(url, {
|
|
754
|
+
method: "POST",
|
|
755
|
+
headers: finalHeaders,
|
|
756
|
+
body: JSON.stringify(payload)
|
|
757
|
+
});
|
|
758
|
+
if (!response.ok) {
|
|
759
|
+
let responseText = "";
|
|
760
|
+
try {
|
|
761
|
+
responseText = await response.text();
|
|
762
|
+
} catch {
|
|
763
|
+
}
|
|
764
|
+
const statusLine = `HTTP ${response.status}${response.statusText ? ` ${response.statusText}` : ""}`;
|
|
765
|
+
const bodyPreview = responseText ? ` - ${responseText.slice(0, 200)}` : "";
|
|
766
|
+
throw new Error(`${statusLine}${bodyPreview}`);
|
|
767
|
+
}
|
|
768
|
+
} catch (err) {
|
|
769
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
770
|
+
if (typeof console !== "undefined" && typeof console.error === "function") {
|
|
771
|
+
console.error("[Tracing] Failed to post session end:", errMsg);
|
|
638
772
|
}
|
|
639
|
-
|
|
640
|
-
const bodyPreview = responseText ? ` - ${responseText.slice(0, 200)}` : "";
|
|
641
|
-
throw new Error(`${statusLine}${bodyPreview}`);
|
|
773
|
+
throw err;
|
|
642
774
|
}
|
|
643
775
|
}
|
|
644
776
|
function createEmptySummary() {
|
|
@@ -817,7 +949,8 @@ function createTraceSession(opts) {
|
|
|
817
949
|
events: [],
|
|
818
950
|
summary: createEmptySummary(),
|
|
819
951
|
status: "in_progress",
|
|
820
|
-
errors: []
|
|
952
|
+
errors: [],
|
|
953
|
+
sessionStarted: false
|
|
821
954
|
};
|
|
822
955
|
if (cfg.sink.type === "file") {
|
|
823
956
|
const baseDir = cfg.sink.baseDir;
|
|
@@ -829,6 +962,49 @@ function createTraceSession(opts) {
|
|
|
829
962
|
}
|
|
830
963
|
return runtime;
|
|
831
964
|
}
|
|
965
|
+
async function startStreamingSession(session, agentRuntime) {
|
|
966
|
+
if (!session) return;
|
|
967
|
+
if (session.resolvedConfig.mode !== "streaming") return;
|
|
968
|
+
if (session.sessionStarted) return;
|
|
969
|
+
const sink = session.resolvedConfig.sink;
|
|
970
|
+
const agentInfo = agentRuntime ? {
|
|
971
|
+
name: agentRuntime.name || "unknown-agent",
|
|
972
|
+
version: agentRuntime.version,
|
|
973
|
+
model: getModelName(agentRuntime.model) || "unknown-model",
|
|
974
|
+
provider: getProviderName(agentRuntime.model)
|
|
975
|
+
} : void 0;
|
|
976
|
+
session.agentInfo = agentInfo;
|
|
977
|
+
const configSnapshot = buildConfigSnapshot(session);
|
|
978
|
+
const startedAtIso = new Date(session.startedAt).toISOString();
|
|
979
|
+
const payload = {
|
|
980
|
+
sessionId: session.sessionId,
|
|
981
|
+
startedAt: startedAtIso,
|
|
982
|
+
agent: agentInfo,
|
|
983
|
+
config: configSnapshot
|
|
984
|
+
};
|
|
985
|
+
if (sink.type === "cognipeer" || sink.type === "http") {
|
|
986
|
+
try {
|
|
987
|
+
const headers = sink.type === "cognipeer" ? { Authorization: `Bearer ${sink.apiKey}` } : sink.headers;
|
|
988
|
+
await postStreamingSessionStart(sink.url, headers, payload);
|
|
989
|
+
session.sessionStarted = true;
|
|
990
|
+
} catch (err) {
|
|
991
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
992
|
+
session.errors.push({
|
|
993
|
+
eventId: "session_start",
|
|
994
|
+
message,
|
|
995
|
+
type: "sink",
|
|
996
|
+
timestamp: startedAtIso
|
|
997
|
+
});
|
|
998
|
+
if (typeof console !== "undefined" && typeof console.warn === "function") {
|
|
999
|
+
console.warn("[Tracing] Failed to start streaming session, falling back to batched:", message);
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
} else if (sink.type === "custom" && typeof sink.onEvent === "function") {
|
|
1003
|
+
session.sessionStarted = true;
|
|
1004
|
+
} else if (sink.type === "file") {
|
|
1005
|
+
session.sessionStarted = true;
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
832
1008
|
function recordTraceEvent(session, event) {
|
|
833
1009
|
if (!session) return void 0;
|
|
834
1010
|
const sequence = session.events.length + 1;
|
|
@@ -931,6 +1107,23 @@ function recordTraceEvent(session, event) {
|
|
|
931
1107
|
}
|
|
932
1108
|
}
|
|
933
1109
|
}
|
|
1110
|
+
if (session.resolvedConfig.mode === "streaming" && session.sessionStarted) {
|
|
1111
|
+
if (sink.type === "cognipeer" || sink.type === "http") {
|
|
1112
|
+
const headers = sink.type === "cognipeer" ? { Authorization: `Bearer ${sink.apiKey}` } : sink.headers;
|
|
1113
|
+
postStreamingEvent(sink.url, headers, session.sessionId, record).catch((err) => {
|
|
1114
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
1115
|
+
if (typeof console !== "undefined" && typeof console.warn === "function") {
|
|
1116
|
+
console.warn("[Tracing] Failed to send streaming event:", errMsg);
|
|
1117
|
+
}
|
|
1118
|
+
session.errors.push({
|
|
1119
|
+
eventId: record.id,
|
|
1120
|
+
message: errMsg,
|
|
1121
|
+
type: "sink",
|
|
1122
|
+
timestamp: timestampIso
|
|
1123
|
+
});
|
|
1124
|
+
});
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
934
1127
|
return record;
|
|
935
1128
|
}
|
|
936
1129
|
async function finalizeTraceSession(session, params = {}) {
|
|
@@ -939,12 +1132,16 @@ async function finalizeTraceSession(session, params = {}) {
|
|
|
939
1132
|
const endedAtIso = new Date(endedAtMs).toISOString();
|
|
940
1133
|
const durationMs = endedAtMs - session.startedAt;
|
|
941
1134
|
const startedAtIso = new Date(session.startedAt).toISOString();
|
|
942
|
-
const
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
1135
|
+
const buildAgentInfo = (runtime) => {
|
|
1136
|
+
if (!runtime) return void 0;
|
|
1137
|
+
return {
|
|
1138
|
+
name: runtime.name || "unknown-agent",
|
|
1139
|
+
version: runtime.version,
|
|
1140
|
+
model: getModelName(runtime.model) || "unknown-model",
|
|
1141
|
+
provider: getProviderName(runtime.model)
|
|
1142
|
+
};
|
|
1143
|
+
};
|
|
1144
|
+
const agentInfo = buildAgentInfo(params.agentRuntime);
|
|
948
1145
|
if (params.error) {
|
|
949
1146
|
session.errors.push({
|
|
950
1147
|
eventId: "session",
|
|
@@ -961,7 +1158,7 @@ async function finalizeTraceSession(session, params = {}) {
|
|
|
961
1158
|
startedAt: startedAtIso,
|
|
962
1159
|
endedAt: endedAtIso,
|
|
963
1160
|
durationMs,
|
|
964
|
-
agent: agentInfo,
|
|
1161
|
+
agent: agentInfo || session.agentInfo,
|
|
965
1162
|
config: configSnapshot,
|
|
966
1163
|
summary: session.summary,
|
|
967
1164
|
events: session.events,
|
|
@@ -971,10 +1168,22 @@ async function finalizeTraceSession(session, params = {}) {
|
|
|
971
1168
|
const payloadForSink = buildPayload(initialStatus);
|
|
972
1169
|
let sinkFailed = false;
|
|
973
1170
|
const sink = session.resolvedConfig.sink;
|
|
1171
|
+
const isStreamingMode = session.resolvedConfig.mode === "streaming";
|
|
974
1172
|
if (sink.type === "cognipeer" || sink.type === "http") {
|
|
975
1173
|
try {
|
|
976
1174
|
const headers = sink.type === "cognipeer" ? { Authorization: `Bearer ${sink.apiKey}` } : sink.headers;
|
|
977
|
-
|
|
1175
|
+
if (isStreamingMode && session.sessionStarted) {
|
|
1176
|
+
await postStreamingSessionEnd(sink.url, headers, {
|
|
1177
|
+
sessionId: session.sessionId,
|
|
1178
|
+
endedAt: endedAtIso,
|
|
1179
|
+
durationMs,
|
|
1180
|
+
status: initialStatus,
|
|
1181
|
+
summary: session.summary,
|
|
1182
|
+
errors: session.errors
|
|
1183
|
+
});
|
|
1184
|
+
} else {
|
|
1185
|
+
await postTraceSession(sink.url, headers, payloadForSink);
|
|
1186
|
+
}
|
|
978
1187
|
} catch (err) {
|
|
979
1188
|
sinkFailed = true;
|
|
980
1189
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -1112,12 +1321,29 @@ function createAgentCoreNode(opts) {
|
|
|
1112
1321
|
};
|
|
1113
1322
|
const normalizeBedrockToolPairing = (input) => {
|
|
1114
1323
|
const msgs = Array.isArray(input) ? [...input] : [];
|
|
1115
|
-
|
|
1116
|
-
|
|
1324
|
+
const validToolCallIds = /* @__PURE__ */ new Set();
|
|
1325
|
+
for (const m of msgs) {
|
|
1326
|
+
if (m?.role === "assistant") {
|
|
1327
|
+
for (const tu of extractToolUses(m)) {
|
|
1328
|
+
if (tu.id) validToolCallIds.add(tu.id);
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
const filteredMsgs = msgs.filter((m) => {
|
|
1333
|
+
if (m?.role === "tool") {
|
|
1334
|
+
const toolCallId = m.tool_call_id;
|
|
1335
|
+
if (!toolCallId || !validToolCallIds.has(toolCallId)) {
|
|
1336
|
+
return false;
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
return true;
|
|
1340
|
+
});
|
|
1341
|
+
for (let i = 0; i < filteredMsgs.length; i++) {
|
|
1342
|
+
const m = filteredMsgs[i];
|
|
1117
1343
|
if (m?.role !== "assistant") continue;
|
|
1118
1344
|
const toolUses = extractToolUses(m);
|
|
1119
1345
|
if (toolUses.length === 0) continue;
|
|
1120
|
-
const next =
|
|
1346
|
+
const next = filteredMsgs[i + 1];
|
|
1121
1347
|
const firstId = toolUses[0]?.id;
|
|
1122
1348
|
const nextIsToolForFirst = !!(next && next.role === "tool" && next.tool_call_id === firstId);
|
|
1123
1349
|
if (nextIsToolForFirst) continue;
|
|
@@ -1125,12 +1351,12 @@ function createAgentCoreNode(opts) {
|
|
|
1125
1351
|
role: "tool",
|
|
1126
1352
|
name: tu.name || "unknown_tool",
|
|
1127
1353
|
tool_call_id: tu.id,
|
|
1128
|
-
content: "SUMMARIZED/DEFERRED: tool result missing in transcript; inserted placeholder for
|
|
1354
|
+
content: "SUMMARIZED/DEFERRED: tool result missing in transcript; inserted placeholder for tool_result adjacency."
|
|
1129
1355
|
}));
|
|
1130
|
-
|
|
1356
|
+
filteredMsgs.splice(i + 1, 0, ...placeholders);
|
|
1131
1357
|
i += placeholders.length;
|
|
1132
1358
|
}
|
|
1133
|
-
return
|
|
1359
|
+
return filteredMsgs;
|
|
1134
1360
|
};
|
|
1135
1361
|
const debugToolPairing = (messagesToSend) => {
|
|
1136
1362
|
try {
|
|
@@ -1152,11 +1378,54 @@ function createAgentCoreNode(opts) {
|
|
|
1152
1378
|
} catch (e) {
|
|
1153
1379
|
}
|
|
1154
1380
|
};
|
|
1381
|
+
const onEvent = state.ctx?.__onEvent;
|
|
1382
|
+
const onStream = state.ctx?.__onStream;
|
|
1383
|
+
const streamingEnabled = Boolean(state.ctx?.__streaming);
|
|
1384
|
+
const cancellationToken = state.ctx?.__cancellationToken;
|
|
1385
|
+
const abortSignal = state.ctx?.__abortSignal;
|
|
1386
|
+
const extractText = (chunk) => {
|
|
1387
|
+
if (chunk == null) return "";
|
|
1388
|
+
if (typeof chunk === "string") return chunk;
|
|
1389
|
+
if (typeof chunk?.content === "string") return chunk.content;
|
|
1390
|
+
if (Array.isArray(chunk?.content)) {
|
|
1391
|
+
return chunk.content.map((c) => typeof c === "string" ? c : c?.text ?? c?.content ?? "").join("");
|
|
1392
|
+
}
|
|
1393
|
+
if (typeof chunk?.text === "string") return chunk.text;
|
|
1394
|
+
if (typeof chunk?.delta?.content === "string") return chunk.delta.content;
|
|
1395
|
+
return "";
|
|
1396
|
+
};
|
|
1155
1397
|
let response;
|
|
1156
1398
|
try {
|
|
1157
1399
|
const normalizedMessages = normalizeBedrockToolPairing([...state.messages]);
|
|
1158
1400
|
debugToolPairing(normalizedMessages);
|
|
1159
|
-
|
|
1401
|
+
if (streamingEnabled && typeof modelWithTools.stream === "function") {
|
|
1402
|
+
let streamedText = "";
|
|
1403
|
+
let streamedMessage;
|
|
1404
|
+
for await (const chunk of modelWithTools.stream(normalizedMessages, { signal: abortSignal, cancellationToken })) {
|
|
1405
|
+
if (cancellationToken && cancellationToken.isCancellationRequested || abortSignal?.aborted) {
|
|
1406
|
+
break;
|
|
1407
|
+
}
|
|
1408
|
+
if (chunk && typeof chunk === "object" && chunk.role) {
|
|
1409
|
+
streamedMessage = chunk;
|
|
1410
|
+
}
|
|
1411
|
+
const text = extractText(chunk);
|
|
1412
|
+
if (text) {
|
|
1413
|
+
streamedText += text;
|
|
1414
|
+
onStream?.({ text });
|
|
1415
|
+
onEvent?.({ type: "stream", text });
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
if (streamedMessage) {
|
|
1419
|
+
response = { ...streamedMessage };
|
|
1420
|
+
if (response.content == null || response.content === "") {
|
|
1421
|
+
response.content = streamedText;
|
|
1422
|
+
}
|
|
1423
|
+
} else {
|
|
1424
|
+
response = { role: "assistant", content: streamedText };
|
|
1425
|
+
}
|
|
1426
|
+
} else {
|
|
1427
|
+
response = await modelWithTools.invoke(normalizedMessages, { signal: abortSignal, cancellationToken });
|
|
1428
|
+
}
|
|
1160
1429
|
} catch (err) {
|
|
1161
1430
|
const durationMs2 = Date.now() - start;
|
|
1162
1431
|
recordTraceEvent(traceSession, {
|
|
@@ -1177,7 +1446,7 @@ function createAgentCoreNode(opts) {
|
|
|
1177
1446
|
...state.messages,
|
|
1178
1447
|
response
|
|
1179
1448
|
];
|
|
1180
|
-
const rawUsage = response?.usage || response?.response_metadata?.token_usage || response?.response_metadata?.usage;
|
|
1449
|
+
const rawUsage = response?.usage || response?.response_metadata?.token_usage || response?.response_metadata?.tokenUsage || response?.response_metadata?.usage;
|
|
1181
1450
|
const normalized = normalizeUsage(rawUsage);
|
|
1182
1451
|
const modelName = getModelName(runtime.model || opts.model) || "unknown_model";
|
|
1183
1452
|
const durationMs = Date.now() - start;
|
|
@@ -1244,6 +1513,9 @@ function createToolsNode(initialTools, opts) {
|
|
|
1244
1513
|
};
|
|
1245
1514
|
const appended = [];
|
|
1246
1515
|
const onEvent = state.ctx?.__onEvent;
|
|
1516
|
+
const onProgress = state.ctx?.__onProgress;
|
|
1517
|
+
const cancellationToken = state.ctx?.__cancellationToken;
|
|
1518
|
+
const abortSignal = state.ctx?.__abortSignal;
|
|
1247
1519
|
const traceSession = state.ctx?.__traceSession;
|
|
1248
1520
|
const pendingApprovals = Array.isArray(state.pendingApprovals) ? state.pendingApprovals.map((entry) => ({ ...entry })) : [];
|
|
1249
1521
|
const pendingByCallId = new Map(pendingApprovals.map((entry) => [entry.toolCallId, entry]));
|
|
@@ -1300,7 +1572,20 @@ function createToolsNode(initialTools, opts) {
|
|
|
1300
1572
|
name: tc.name
|
|
1301
1573
|
});
|
|
1302
1574
|
}
|
|
1575
|
+
const isCancelled = () => {
|
|
1576
|
+
if (abortSignal?.aborted) return { cancelled: true, reason: "aborted" };
|
|
1577
|
+
if (cancellationToken && cancellationToken.isCancellationRequested) return { cancelled: true, reason: "cancelled" };
|
|
1578
|
+
return { cancelled: false, reason: void 0 };
|
|
1579
|
+
};
|
|
1303
1580
|
const runOne = async (tc) => {
|
|
1581
|
+
const cancelState = isCancelled();
|
|
1582
|
+
if (cancelState.cancelled) {
|
|
1583
|
+
const ctx = state.ctx = state.ctx || {};
|
|
1584
|
+
ctx.__cancelled = { stage: "tools", reason: cancelState.reason, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
|
|
1585
|
+
onEvent?.({ type: "cancelled", stage: "tools", reason: cancelState.reason });
|
|
1586
|
+
onProgress?.({ stage: "tools", message: "Cancelled", detail: { reason: cancelState.reason } });
|
|
1587
|
+
return { status: "error" };
|
|
1588
|
+
}
|
|
1304
1589
|
const t = toolByName.get(tc.name);
|
|
1305
1590
|
if (!t) {
|
|
1306
1591
|
onEvent?.({ type: "tool_call", phase: "error", name: tc.name, id: tc.id, args: tc.args, error: { message: "Tool not found" } });
|
|
@@ -1379,15 +1664,17 @@ function createToolsNode(initialTools, opts) {
|
|
|
1379
1664
|
const start = Date.now();
|
|
1380
1665
|
try {
|
|
1381
1666
|
onEvent?.({ type: "tool_call", phase: "start", name: t.name, id: tc.id, args });
|
|
1667
|
+
onProgress?.({ stage: "tools", message: `Running tool ${tc.name}` });
|
|
1382
1668
|
const sanitizedArgs = sanitizeTracePayload(args);
|
|
1383
1669
|
const inputBytes = traceSession?.resolvedConfig.logData ? estimatePayloadBytes(sanitizedArgs) : void 0;
|
|
1384
1670
|
let output;
|
|
1385
1671
|
const anyTool = t;
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
else if (typeof anyTool.
|
|
1389
|
-
else if (typeof anyTool.
|
|
1390
|
-
else if (typeof anyTool.
|
|
1672
|
+
const callOptions = { cancellationToken, signal: abortSignal };
|
|
1673
|
+
if (typeof anyTool.func === "function") output = await anyTool.func(args, callOptions);
|
|
1674
|
+
else if (typeof anyTool.invoke === "function") output = await anyTool.invoke(args, callOptions);
|
|
1675
|
+
else if (typeof anyTool.call === "function") output = await anyTool.call(args, callOptions);
|
|
1676
|
+
else if (typeof anyTool._call === "function") output = await anyTool._call(args, callOptions);
|
|
1677
|
+
else if (typeof anyTool.run === "function") output = await anyTool.run(args, callOptions);
|
|
1391
1678
|
else throw new Error("Tool is not invokable");
|
|
1392
1679
|
if (output && typeof output === "object" && output.__handoff && output.__handoff.runtime) {
|
|
1393
1680
|
const newRuntime = output.__handoff.runtime;
|
|
@@ -1417,6 +1704,7 @@ function createToolsNode(initialTools, opts) {
|
|
|
1417
1704
|
toolHistory.push({ executionId, toolName: t.name, args, output, rawOutput: output, timestamp: (/* @__PURE__ */ new Date()).toISOString(), tool_call_id: tc.id });
|
|
1418
1705
|
appended.push({ role: "tool", content, tool_call_id: tc.id || `${tc.name}_${appended.length}`, name: tc.name });
|
|
1419
1706
|
onEvent?.({ type: "tool_call", phase: "success", name: t.name, id: tc.id, args, result: output, durationMs });
|
|
1707
|
+
onProgress?.({ stage: "tools", message: `Tool ${tc.name} completed`, detail: { durationMs } });
|
|
1420
1708
|
if (needsApproval && approvalEntry) {
|
|
1421
1709
|
approvalEntry.status = "executed";
|
|
1422
1710
|
approvalEntry.resolvedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -1466,6 +1754,7 @@ function createToolsNode(initialTools, opts) {
|
|
|
1466
1754
|
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 });
|
|
1467
1755
|
appended.push({ role: "tool", content: `Error executing tool: ${e?.message || String(e)}`, tool_call_id: tc.id || `${tc.name}_${appended.length}`, name: tc.name });
|
|
1468
1756
|
onEvent?.({ type: "tool_call", phase: "error", name: t.name, id: tc.id, args, error: { message: e?.message || String(e) } });
|
|
1757
|
+
onProgress?.({ stage: "tools", message: `Tool ${tc.name} failed`, detail: { error: e?.message || String(e) } });
|
|
1469
1758
|
const sanitizedArgs = sanitizeTracePayload(args);
|
|
1470
1759
|
const inputBytes = traceSession?.resolvedConfig.logData ? estimatePayloadBytes(sanitizedArgs) : void 0;
|
|
1471
1760
|
const toolName = t.name || tc.name;
|
|
@@ -1509,6 +1798,14 @@ function createToolsNode(initialTools, opts) {
|
|
|
1509
1798
|
};
|
|
1510
1799
|
for (const tc of planned) {
|
|
1511
1800
|
if (awaitingApproval) break;
|
|
1801
|
+
const cancelState = isCancelled();
|
|
1802
|
+
if (cancelState.cancelled) {
|
|
1803
|
+
const ctx = state.ctx = state.ctx || {};
|
|
1804
|
+
ctx.__cancelled = { stage: "tools", reason: cancelState.reason, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
|
|
1805
|
+
onEvent?.({ type: "cancelled", stage: "tools", reason: cancelState.reason });
|
|
1806
|
+
onProgress?.({ stage: "tools", message: "Cancelled", detail: { reason: cancelState.reason } });
|
|
1807
|
+
break;
|
|
1808
|
+
}
|
|
1512
1809
|
const result = await runOne(tc);
|
|
1513
1810
|
if (result?.status === "awaiting_approval") {
|
|
1514
1811
|
awaitingApproval = true;
|
|
@@ -1536,7 +1833,7 @@ function createToolsNode(initialTools, opts) {
|
|
|
1536
1833
|
// src/nodes/toolLimitFinalize.ts
|
|
1537
1834
|
function createToolLimitFinalizeNode(_opts) {
|
|
1538
1835
|
return async (state) => {
|
|
1539
|
-
const notice = { role: "
|
|
1836
|
+
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." };
|
|
1540
1837
|
const ctx = { ...state.ctx || {}, __finalizedDueToToolLimit: true };
|
|
1541
1838
|
return { messages: [...state.messages, notice], ctx };
|
|
1542
1839
|
};
|
|
@@ -1813,12 +2110,37 @@ function createAgent(opts) {
|
|
|
1813
2110
|
delete nextCtx.__resumeStage;
|
|
1814
2111
|
state = { ...state, ctx: Object.keys(nextCtx).length > 0 ? nextCtx : void 0 };
|
|
1815
2112
|
}
|
|
1816
|
-
const
|
|
2113
|
+
const mergedLimits = {
|
|
2114
|
+
...opts.limits || {},
|
|
2115
|
+
...config?.limits || {}
|
|
2116
|
+
};
|
|
2117
|
+
const maxToolCalls = mergedLimits?.maxToolCalls === void 0 ? 50 : mergedLimits?.maxToolCalls;
|
|
1817
2118
|
const iterationLimit = maxToolCalls === Infinity ? 100 : Math.max(maxToolCalls * 3 + 10, 40);
|
|
1818
2119
|
let iterations = 0;
|
|
1819
2120
|
const onStateChange = config?.onStateChange;
|
|
1820
2121
|
const checkpointReason = config?.checkpointReason;
|
|
1821
2122
|
let pausedStage = null;
|
|
2123
|
+
const onProgress = state.ctx?.__onProgress;
|
|
2124
|
+
const isCancelled = () => {
|
|
2125
|
+
const ctx = state.ctx || {};
|
|
2126
|
+
const token = ctx.__cancellationToken;
|
|
2127
|
+
const signal = ctx.__abortSignal;
|
|
2128
|
+
const deadline = ctx.__deadline;
|
|
2129
|
+
if (signal?.aborted) return { cancelled: true, reason: "aborted" };
|
|
2130
|
+
if (token && token.isCancellationRequested) return { cancelled: true, reason: "cancelled" };
|
|
2131
|
+
if (deadline && Date.now() > deadline) return { cancelled: true, reason: "timeout" };
|
|
2132
|
+
return { cancelled: false, reason: void 0 };
|
|
2133
|
+
};
|
|
2134
|
+
const cancelIfRequested = (stage) => {
|
|
2135
|
+
const result = isCancelled();
|
|
2136
|
+
if (!result.cancelled) return false;
|
|
2137
|
+
const ctx = { ...state.ctx || {} };
|
|
2138
|
+
ctx.__cancelled = { stage, reason: result.reason, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
|
|
2139
|
+
state = { ...state, ctx };
|
|
2140
|
+
emit?.({ type: "cancelled", stage, reason: result.reason });
|
|
2141
|
+
onProgress?.({ stage, message: "Cancelled", detail: { reason: result.reason } });
|
|
2142
|
+
return true;
|
|
2143
|
+
};
|
|
1822
2144
|
const checkpointIfRequested = (stage) => {
|
|
1823
2145
|
if (typeof onStateChange !== "function") return false;
|
|
1824
2146
|
let result = false;
|
|
@@ -1841,8 +2163,10 @@ function createAgent(opts) {
|
|
|
1841
2163
|
};
|
|
1842
2164
|
while (iterations < iterationLimit) {
|
|
1843
2165
|
iterations++;
|
|
2166
|
+
if (cancelIfRequested("loop")) break;
|
|
1844
2167
|
const skippingAgent = resumeStage === "tools";
|
|
1845
2168
|
if (!skippingAgent) {
|
|
2169
|
+
if (cancelIfRequested("before_guardrails")) break;
|
|
1846
2170
|
if (checkpointIfRequested("before_guardrails")) break;
|
|
1847
2171
|
const preGuardrails = getGuardrailConfig(state);
|
|
1848
2172
|
if (preGuardrails.length > 0) {
|
|
@@ -1897,7 +2221,10 @@ function createAgent(opts) {
|
|
|
1897
2221
|
break;
|
|
1898
2222
|
}
|
|
1899
2223
|
}
|
|
2224
|
+
onProgress?.({ stage: "agent", message: "Invoking model" });
|
|
2225
|
+
if (cancelIfRequested("before_agent")) break;
|
|
1900
2226
|
state = { ...state, ...await agentCore(state) };
|
|
2227
|
+
onProgress?.({ stage: "agent", message: "Model response received" });
|
|
1901
2228
|
if (checkpointIfRequested("after_agent")) break;
|
|
1902
2229
|
const postGuardrails = getGuardrailConfig(state);
|
|
1903
2230
|
if (postGuardrails.length > 0) {
|
|
@@ -1965,7 +2292,10 @@ function createAgent(opts) {
|
|
|
1965
2292
|
continue;
|
|
1966
2293
|
}
|
|
1967
2294
|
if (toolCalls.length === 0) break;
|
|
2295
|
+
onProgress?.({ stage: "tools", message: "Running tools" });
|
|
2296
|
+
if (cancelIfRequested("before_tools")) break;
|
|
1968
2297
|
state = { ...state, ...await toolsNode(state) };
|
|
2298
|
+
onProgress?.({ stage: "tools", message: "Tools finished" });
|
|
1969
2299
|
if (state.ctx?.__awaitingApproval) break;
|
|
1970
2300
|
if (checkpointIfRequested("after_tools")) break;
|
|
1971
2301
|
if (state.ctx?.__finalizedDueToStructuredOutput) break;
|
|
@@ -2006,15 +2336,39 @@ function createAgent(opts) {
|
|
|
2006
2336
|
}
|
|
2007
2337
|
const invokeAgent = async (input, config) => {
|
|
2008
2338
|
const onEvent = config?.onEvent;
|
|
2339
|
+
const onProgress = config?.onProgress;
|
|
2340
|
+
const onStream = config?.onStream;
|
|
2341
|
+
const streamEnabled = config?.stream === true;
|
|
2009
2342
|
const emit = (e) => {
|
|
2010
2343
|
try {
|
|
2011
2344
|
onEvent?.(e);
|
|
2012
2345
|
} catch {
|
|
2013
2346
|
}
|
|
2014
2347
|
};
|
|
2348
|
+
const emitProgress = (progress) => {
|
|
2349
|
+
try {
|
|
2350
|
+
onProgress?.(progress);
|
|
2351
|
+
} catch {
|
|
2352
|
+
}
|
|
2353
|
+
emit({ type: "progress", ...progress });
|
|
2354
|
+
};
|
|
2015
2355
|
const traceSession = createTraceSession(opts);
|
|
2016
|
-
const ctx = {
|
|
2356
|
+
const ctx = {
|
|
2357
|
+
...input.ctx || {},
|
|
2358
|
+
__onEvent: onEvent,
|
|
2359
|
+
__onProgress: emitProgress,
|
|
2360
|
+
__onStream: onStream,
|
|
2361
|
+
__streaming: streamEnabled
|
|
2362
|
+
};
|
|
2017
2363
|
if (traceSession) ctx.__traceSession = traceSession;
|
|
2364
|
+
if (config?.cancellationToken) ctx.__cancellationToken = config.cancellationToken;
|
|
2365
|
+
if (config?.cancellationToken?.aborted !== void 0) {
|
|
2366
|
+
ctx.__abortSignal = config?.cancellationToken;
|
|
2367
|
+
}
|
|
2368
|
+
if (config?.timeoutMs && config.timeoutMs > 0) {
|
|
2369
|
+
ctx.__deadline = Date.now() + config.timeoutMs;
|
|
2370
|
+
}
|
|
2371
|
+
const runtimeWithInvokeLimits = config?.limits ? { ...runtime, limits: { ...runtime.limits || {}, ...config.limits } } : runtime;
|
|
2018
2372
|
const initial = {
|
|
2019
2373
|
messages: input.messages || [],
|
|
2020
2374
|
toolCallCount: input.toolCallCount || 0,
|
|
@@ -2023,7 +2377,7 @@ function createAgent(opts) {
|
|
|
2023
2377
|
metadata: input.metadata,
|
|
2024
2378
|
ctx,
|
|
2025
2379
|
pendingApprovals: input.pendingApprovals || [],
|
|
2026
|
-
agent: input.agent ||
|
|
2380
|
+
agent: input.agent || runtimeWithInvokeLimits,
|
|
2027
2381
|
usage: input.usage || { perRequest: [], totals: {} }
|
|
2028
2382
|
};
|
|
2029
2383
|
let res;
|
|
@@ -2066,6 +2420,10 @@ function createAgent(opts) {
|
|
|
2066
2420
|
}
|
|
2067
2421
|
}
|
|
2068
2422
|
emit({ type: "finalAnswer", content: typeof finalMsg?.content === "string" ? finalMsg.content : content });
|
|
2423
|
+
if (streamEnabled && content) {
|
|
2424
|
+
onStream?.({ text: content, isFinal: true });
|
|
2425
|
+
emit({ type: "stream", text: content, isFinal: true });
|
|
2426
|
+
}
|
|
2069
2427
|
return {
|
|
2070
2428
|
content,
|
|
2071
2429
|
output: parsed,
|
|
@@ -2236,6 +2594,31 @@ function createAgentNode(opts) {
|
|
|
2236
2594
|
// src/nodes/contextSummarize.ts
|
|
2237
2595
|
var systemMessage = (content) => ({ role: "system", content });
|
|
2238
2596
|
var humanMessage = (content) => ({ role: "user", content });
|
|
2597
|
+
function validateMessageSequence(messages) {
|
|
2598
|
+
const validated = [];
|
|
2599
|
+
const pendingToolCallIds = /* @__PURE__ */ new Set();
|
|
2600
|
+
for (const m of messages) {
|
|
2601
|
+
if (m.role === "assistant") {
|
|
2602
|
+
if (!m.tool_calls || m.tool_calls.length === 0) {
|
|
2603
|
+
pendingToolCallIds.clear();
|
|
2604
|
+
} else {
|
|
2605
|
+
m.tool_calls.forEach((tc) => {
|
|
2606
|
+
if (tc.id) pendingToolCallIds.add(tc.id);
|
|
2607
|
+
});
|
|
2608
|
+
}
|
|
2609
|
+
validated.push(m);
|
|
2610
|
+
} else if (m.role === "tool") {
|
|
2611
|
+
const toolCallId = m.tool_call_id;
|
|
2612
|
+
if (toolCallId && pendingToolCallIds.has(toolCallId)) {
|
|
2613
|
+
pendingToolCallIds.delete(toolCallId);
|
|
2614
|
+
validated.push(m);
|
|
2615
|
+
}
|
|
2616
|
+
} else {
|
|
2617
|
+
validated.push(m);
|
|
2618
|
+
}
|
|
2619
|
+
}
|
|
2620
|
+
return validated;
|
|
2621
|
+
}
|
|
2239
2622
|
function getSummarizationConfig(opts) {
|
|
2240
2623
|
if (typeof opts.summarization === "object") {
|
|
2241
2624
|
return {
|
|
@@ -2243,13 +2626,15 @@ function getSummarizationConfig(opts) {
|
|
|
2243
2626
|
maxTokens: opts.summarization.maxTokens ?? 5e4,
|
|
2244
2627
|
// Max tokens we can send to the model for summarization prompt itself
|
|
2245
2628
|
// This should be well under the model's context limit
|
|
2246
|
-
summaryPromptMaxTokens: opts.summarization.summaryPromptMaxTokens ?? 8e3
|
|
2629
|
+
summaryPromptMaxTokens: opts.summarization.summaryPromptMaxTokens ?? 8e3,
|
|
2630
|
+
promptTemplate: opts.summarization.promptTemplate
|
|
2247
2631
|
};
|
|
2248
2632
|
}
|
|
2249
2633
|
return {
|
|
2250
2634
|
enabled: opts.summarization !== false,
|
|
2251
2635
|
maxTokens: 5e4,
|
|
2252
|
-
summaryPromptMaxTokens: 8e3
|
|
2636
|
+
summaryPromptMaxTokens: 8e3,
|
|
2637
|
+
promptTemplate: void 0
|
|
2253
2638
|
};
|
|
2254
2639
|
}
|
|
2255
2640
|
function createContextSummarizeNode(opts) {
|
|
@@ -2258,10 +2643,14 @@ function createContextSummarizeNode(opts) {
|
|
|
2258
2643
|
if (!config.enabled) return {};
|
|
2259
2644
|
const model = opts.model;
|
|
2260
2645
|
const messages = state.messages || [];
|
|
2646
|
+
const onEvent = state.ctx?.__onEvent;
|
|
2647
|
+
const traceSession = state.ctx?.__traceSession;
|
|
2261
2648
|
const compressableMessages = messages.filter((m) => m.role === "tool" && m.content !== "SUMMARIZED");
|
|
2262
2649
|
if (compressableMessages.length === 0) {
|
|
2263
2650
|
return {};
|
|
2264
2651
|
}
|
|
2652
|
+
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");
|
|
2653
|
+
const tokenCountBefore = countApproxTokens(allTextBefore);
|
|
2265
2654
|
const summaryPromptMaxTokens = config.summaryPromptMaxTokens;
|
|
2266
2655
|
const availableTokensForConversation = summaryPromptMaxTokens - 500;
|
|
2267
2656
|
const messageTexts = [];
|
|
@@ -2289,32 +2678,93 @@ function createContextSummarizeNode(opts) {
|
|
|
2289
2678
|
accumulatedTokens += messageTokens;
|
|
2290
2679
|
}
|
|
2291
2680
|
const conversationText = messageTexts.join("\n\n");
|
|
2292
|
-
const
|
|
2293
|
-
|
|
2294
|
-
humanMessage(
|
|
2295
|
-
`Please summarize the following conversation history.
|
|
2681
|
+
const previousSummary = Array.isArray(state.summaries) && state.summaries.length > 0 ? state.summaries[state.summaries.length - 1] : "";
|
|
2682
|
+
const defaultPrompt = `Please summarize the following conversation history and update any previous summary.
|
|
2296
2683
|
Focus on:
|
|
2297
2684
|
- User goals and intent.
|
|
2298
2685
|
- Key decisions made and actions taken.
|
|
2299
2686
|
- Important tool outputs and data retrieved.
|
|
2300
2687
|
- Current state of the task.
|
|
2301
2688
|
|
|
2689
|
+
Previous summary (if any):
|
|
2690
|
+
${previousSummary || "(none)"}
|
|
2691
|
+
|
|
2302
2692
|
Conversation:
|
|
2303
2693
|
${conversationText}
|
|
2304
2694
|
|
|
2305
|
-
Summary
|
|
2306
|
-
|
|
2695
|
+
Summary:`;
|
|
2696
|
+
const template = config.promptTemplate || defaultPrompt;
|
|
2697
|
+
const promptBody = template.replace(/\{\{\s*conversation\s*\}\}/g, conversationText).replace(/\{\{\s*previousSummary\s*\}\}/g, previousSummary || "");
|
|
2698
|
+
const summaryPrompt = [
|
|
2699
|
+
systemMessage("You are a helpful assistant that summarizes conversation history efficiently."),
|
|
2700
|
+
humanMessage(promptBody)
|
|
2307
2701
|
];
|
|
2702
|
+
const inputTokensEstimate = countApproxTokens(summaryPrompt.map((m) => m.content).join("\n"));
|
|
2308
2703
|
let summaryText = "Summary unavailable.";
|
|
2704
|
+
let outputTokensEstimate = 0;
|
|
2705
|
+
let durationMs = 0;
|
|
2706
|
+
let inputTokensActual;
|
|
2707
|
+
let outputTokensActual;
|
|
2708
|
+
let cachedInputTokens;
|
|
2709
|
+
let totalTokensActual;
|
|
2710
|
+
const startTime = Date.now();
|
|
2309
2711
|
try {
|
|
2310
2712
|
const response = await model.invoke(summaryPrompt);
|
|
2713
|
+
durationMs = Date.now() - startTime;
|
|
2311
2714
|
summaryText = typeof response.content === "string" ? response.content : Array.isArray(response.content) ? response.content.map((c) => c.text || "").join("") : JSON.stringify(response.content);
|
|
2715
|
+
outputTokensEstimate = countApproxTokens(summaryText);
|
|
2716
|
+
const rawUsage = response?.usage || response?.response_metadata?.token_usage || response?.response_metadata?.tokenUsage || response?.response_metadata?.usage;
|
|
2717
|
+
const normalized = normalizeUsage(rawUsage);
|
|
2718
|
+
if (normalized) {
|
|
2719
|
+
inputTokensActual = normalized.prompt_tokens;
|
|
2720
|
+
outputTokensActual = normalized.completion_tokens;
|
|
2721
|
+
totalTokensActual = normalized.total_tokens;
|
|
2722
|
+
cachedInputTokens = normalized.prompt_tokens_details?.cached_tokens;
|
|
2723
|
+
}
|
|
2312
2724
|
} catch (err) {
|
|
2725
|
+
durationMs = Date.now() - startTime;
|
|
2313
2726
|
console.error("[ContextSummarize] Failed to generate summary:", err);
|
|
2727
|
+
const errorEvent = {
|
|
2728
|
+
type: "summarization",
|
|
2729
|
+
summary: "",
|
|
2730
|
+
messagesCompressed: 0,
|
|
2731
|
+
inputTokens: inputTokensEstimate,
|
|
2732
|
+
outputTokens: 0,
|
|
2733
|
+
durationMs,
|
|
2734
|
+
previousSummary: previousSummary || void 0,
|
|
2735
|
+
tokenCountBefore,
|
|
2736
|
+
tokenCountAfter: tokenCountBefore
|
|
2737
|
+
};
|
|
2738
|
+
onEvent?.(errorEvent);
|
|
2739
|
+
recordTraceEvent(traceSession, {
|
|
2740
|
+
type: "summarization",
|
|
2741
|
+
label: "Context Summarization Failed",
|
|
2742
|
+
actor: { scope: "system", name: "summarizer", role: "summarization" },
|
|
2743
|
+
status: "error",
|
|
2744
|
+
durationMs,
|
|
2745
|
+
inputTokens: inputTokensEstimate,
|
|
2746
|
+
outputTokens: 0,
|
|
2747
|
+
error: { message: err?.message || String(err), stack: err?.stack },
|
|
2748
|
+
messageList: sanitizeTracePayload(summaryPrompt)
|
|
2749
|
+
});
|
|
2314
2750
|
return {};
|
|
2315
2751
|
}
|
|
2752
|
+
const toolCallIdToAssistantIdx = /* @__PURE__ */ new Map();
|
|
2753
|
+
messages.forEach((m, idx) => {
|
|
2754
|
+
if (m.role === "assistant" && m.tool_calls && Array.isArray(m.tool_calls)) {
|
|
2755
|
+
m.tool_calls.forEach((tc) => {
|
|
2756
|
+
if (tc.id) {
|
|
2757
|
+
toolCallIdToAssistantIdx.set(tc.id, idx);
|
|
2758
|
+
}
|
|
2759
|
+
});
|
|
2760
|
+
}
|
|
2761
|
+
});
|
|
2316
2762
|
const newMessages = messages.map((m) => {
|
|
2317
2763
|
if (m.role === "tool") {
|
|
2764
|
+
const toolCallId = m.tool_call_id;
|
|
2765
|
+
if (!toolCallId || !toolCallIdToAssistantIdx.has(toolCallId)) {
|
|
2766
|
+
return null;
|
|
2767
|
+
}
|
|
2318
2768
|
if (m.content === "SUMMARIZED") return m;
|
|
2319
2769
|
return {
|
|
2320
2770
|
...m,
|
|
@@ -2322,7 +2772,8 @@ Summary:`
|
|
|
2322
2772
|
};
|
|
2323
2773
|
}
|
|
2324
2774
|
return m;
|
|
2325
|
-
});
|
|
2775
|
+
}).filter((m) => m !== null);
|
|
2776
|
+
const validatedMessages = validateMessageSequence(newMessages);
|
|
2326
2777
|
const summaryToolCallId = `call_summary_${Date.now()}`;
|
|
2327
2778
|
const assistantSummaryCall = {
|
|
2328
2779
|
role: "assistant",
|
|
@@ -2346,8 +2797,53 @@ Summary:`
|
|
|
2346
2797
|
// Matches the tool name in the call
|
|
2347
2798
|
content: summaryText
|
|
2348
2799
|
};
|
|
2800
|
+
const summaries = Array.isArray(state.summaries) ? [...state.summaries, summaryText] : [summaryText];
|
|
2801
|
+
const finalMessages = [...validatedMessages, assistantSummaryCall, toolSummaryResponse];
|
|
2802
|
+
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");
|
|
2803
|
+
const tokenCountAfter = countApproxTokens(allTextAfter);
|
|
2804
|
+
const summarizationEvent = {
|
|
2805
|
+
type: "summarization",
|
|
2806
|
+
summary: summaryText,
|
|
2807
|
+
messagesCompressed: compressableMessages.length,
|
|
2808
|
+
inputTokens: inputTokensActual ?? inputTokensEstimate,
|
|
2809
|
+
outputTokens: outputTokensActual ?? outputTokensEstimate,
|
|
2810
|
+
cachedInputTokens,
|
|
2811
|
+
totalTokens: totalTokensActual ?? (inputTokensActual ?? inputTokensEstimate) + (outputTokensActual ?? outputTokensEstimate),
|
|
2812
|
+
durationMs,
|
|
2813
|
+
previousSummary: previousSummary || void 0,
|
|
2814
|
+
tokenCountBefore,
|
|
2815
|
+
tokenCountAfter,
|
|
2816
|
+
archivedCount: compressableMessages.length
|
|
2817
|
+
// deprecated field for backward compatibility
|
|
2818
|
+
};
|
|
2819
|
+
onEvent?.(summarizationEvent);
|
|
2820
|
+
recordTraceEvent(traceSession, {
|
|
2821
|
+
type: "summarization",
|
|
2822
|
+
label: "Context Summarization",
|
|
2823
|
+
actor: { scope: "system", name: "summarizer", role: "summarization" },
|
|
2824
|
+
status: "success",
|
|
2825
|
+
durationMs,
|
|
2826
|
+
inputTokens: inputTokensActual ?? inputTokensEstimate,
|
|
2827
|
+
outputTokens: outputTokensActual ?? outputTokensEstimate,
|
|
2828
|
+
cachedInputTokens,
|
|
2829
|
+
totalTokens: totalTokensActual,
|
|
2830
|
+
messageList: sanitizeTracePayload([
|
|
2831
|
+
...summaryPrompt,
|
|
2832
|
+
{ role: "assistant", content: summaryText, name: "summarization_result" }
|
|
2833
|
+
]),
|
|
2834
|
+
debug: {
|
|
2835
|
+
messagesCompressed: compressableMessages.length,
|
|
2836
|
+
tokenCountBefore,
|
|
2837
|
+
tokenCountAfter,
|
|
2838
|
+
tokensSaved: tokenCountBefore - tokenCountAfter,
|
|
2839
|
+
previousSummaryLength: previousSummary?.length || 0,
|
|
2840
|
+
usageFromModel: inputTokensActual !== void 0,
|
|
2841
|
+
cachedInputTokens
|
|
2842
|
+
}
|
|
2843
|
+
});
|
|
2349
2844
|
return {
|
|
2350
|
-
messages:
|
|
2845
|
+
messages: finalMessages,
|
|
2846
|
+
summaries
|
|
2351
2847
|
};
|
|
2352
2848
|
};
|
|
2353
2849
|
}
|
|
@@ -2452,9 +2948,29 @@ function needsSummarization(state, opts, summarizationEnabled) {
|
|
|
2452
2948
|
maxTok = 5e4;
|
|
2453
2949
|
}
|
|
2454
2950
|
try {
|
|
2455
|
-
const allText = (state.messages || []).map((m) =>
|
|
2951
|
+
const allText = (state.messages || []).map((m) => {
|
|
2952
|
+
if (typeof m.content === "string") return m.content;
|
|
2953
|
+
if (Array.isArray(m.content)) {
|
|
2954
|
+
return m.content.map((c) => typeof c === "string" ? c : c?.text ?? c?.content ?? JSON.stringify(c)).join("");
|
|
2955
|
+
}
|
|
2956
|
+
if (m.content && typeof m.content === "object") {
|
|
2957
|
+
return JSON.stringify(m.content);
|
|
2958
|
+
}
|
|
2959
|
+
return "";
|
|
2960
|
+
}).join("\n");
|
|
2456
2961
|
const tokenCount = countApproxTokens(allText);
|
|
2457
2962
|
const needsSum = tokenCount > maxTok;
|
|
2963
|
+
if (process.env.DEBUG_SUMMARIZATION) {
|
|
2964
|
+
const msgCount = (state.messages || []).length;
|
|
2965
|
+
const toolMsgs = (state.messages || []).filter((m) => m.role === "tool");
|
|
2966
|
+
const toolDetails = toolMsgs.map((m, i) => {
|
|
2967
|
+
const contentStr = typeof m.content === "string" ? m.content : JSON.stringify(m.content);
|
|
2968
|
+
return `[${i}] id:${m.tool_call_id?.slice(-8) || "none"} len:${contentStr.length} preview:"${contentStr.slice(0, 30)}..."`;
|
|
2969
|
+
});
|
|
2970
|
+
console.log(`[Summarization] Messages: ${msgCount}, Tool messages: ${toolMsgs.length}`);
|
|
2971
|
+
toolDetails.forEach((d) => console.log(` ${d}`));
|
|
2972
|
+
console.log(`[Summarization] Token count: ${tokenCount}, Max: ${maxTok}, Needs summarization: ${needsSum}`);
|
|
2973
|
+
}
|
|
2458
2974
|
return needsSum;
|
|
2459
2975
|
} catch (e) {
|
|
2460
2976
|
return false;
|
|
@@ -2512,7 +3028,8 @@ function createSmartAgent(opts) {
|
|
|
2512
3028
|
const seedMessages = alreadyHasSystem ? [...input.messages || []] : [systemMessage2(), ...input.messages || []];
|
|
2513
3029
|
let state = { ...input, messages: seedMessages };
|
|
2514
3030
|
let lastResult = null;
|
|
2515
|
-
const
|
|
3031
|
+
const effectiveMaxToolCalls = config?.limits?.maxToolCalls ?? opts.limits?.maxToolCalls ?? 10;
|
|
3032
|
+
const iterationLimit = Math.max(effectiveMaxToolCalls * 3 + 5, 30);
|
|
2516
3033
|
for (let i = 0; i < iterationLimit; i++) {
|
|
2517
3034
|
const next = summarizationEnabled ? decideBefore(state) : "agent";
|
|
2518
3035
|
if (next === "contextSummarize" && summarizer) {
|
|
@@ -2524,7 +3041,11 @@ function createSmartAgent(opts) {
|
|
|
2524
3041
|
}
|
|
2525
3042
|
const res = await base.invoke(state, config);
|
|
2526
3043
|
lastResult = res;
|
|
3044
|
+
const currentSummaries = state.summaries;
|
|
2527
3045
|
state = res.state || { ...state, messages: res.messages };
|
|
3046
|
+
if (currentSummaries && currentSummaries.length > 0 && (!state.summaries || state.summaries.length === 0)) {
|
|
3047
|
+
state = { ...state, summaries: currentSummaries };
|
|
3048
|
+
}
|
|
2528
3049
|
if (state.ctx?.__needsSummarization && summarizer) {
|
|
2529
3050
|
const ctx = { ...state.ctx || {} };
|
|
2530
3051
|
delete ctx.__needsSummarization;
|
|
@@ -2552,6 +3073,13 @@ function createSmartAgent(opts) {
|
|
|
2552
3073
|
const res = await base.invoke(state, config);
|
|
2553
3074
|
lastResult = res;
|
|
2554
3075
|
}
|
|
3076
|
+
if (state.summaries && state.summaries.length > 0) {
|
|
3077
|
+
if (lastResult.state) {
|
|
3078
|
+
lastResult = { ...lastResult, state: { ...lastResult.state, summaries: state.summaries } };
|
|
3079
|
+
} else {
|
|
3080
|
+
lastResult = { ...lastResult, state: { ...state, summaries: state.summaries } };
|
|
3081
|
+
}
|
|
3082
|
+
}
|
|
2555
3083
|
return lastResult;
|
|
2556
3084
|
},
|
|
2557
3085
|
snapshot: base.snapshot,
|
|
@@ -2874,6 +3402,7 @@ exports.jsonSchemaRule = jsonSchemaRule;
|
|
|
2874
3402
|
exports.regexRule = regexRule;
|
|
2875
3403
|
exports.resolveToolApprovalState = resolveToolApprovalState;
|
|
2876
3404
|
exports.restoreSnapshot = restoreSnapshot;
|
|
3405
|
+
exports.startStreamingSession = startStreamingSession;
|
|
2877
3406
|
exports.withTools = withTools;
|
|
2878
3407
|
//# sourceMappingURL=index.cjs.map
|
|
2879
3408
|
//# sourceMappingURL=index.cjs.map
|