@opperai/agents 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -0
- package/dist/index.cjs +917 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +241 -9
- package/dist/index.d.ts +241 -9
- package/dist/index.js +912 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from 'crypto';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
+
import { promises } from 'fs';
|
|
3
4
|
import { Opper } from 'opperai';
|
|
4
5
|
import { zodToJsonSchema } from 'zod-to-json-schema';
|
|
5
6
|
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
@@ -391,7 +392,11 @@ var HookEvents = {
|
|
|
391
392
|
ToolError: "tool:error",
|
|
392
393
|
MemoryRead: "memory:read",
|
|
393
394
|
MemoryWrite: "memory:write",
|
|
394
|
-
MemoryError: "memory:error"
|
|
395
|
+
MemoryError: "memory:error",
|
|
396
|
+
StreamStart: "stream:start",
|
|
397
|
+
StreamChunk: "stream:chunk",
|
|
398
|
+
StreamEnd: "stream:end",
|
|
399
|
+
StreamError: "stream:error"
|
|
395
400
|
};
|
|
396
401
|
var HookManager = class {
|
|
397
402
|
registry = /* @__PURE__ */ new Map();
|
|
@@ -463,8 +468,307 @@ var HookManager = class {
|
|
|
463
468
|
};
|
|
464
469
|
var createHookManager = (logger) => new HookManager(logger);
|
|
465
470
|
|
|
471
|
+
// src/base/events.ts
|
|
472
|
+
var AgentEvents = {
|
|
473
|
+
StreamStart: HookEvents.StreamStart,
|
|
474
|
+
StreamChunk: HookEvents.StreamChunk,
|
|
475
|
+
StreamEnd: HookEvents.StreamEnd,
|
|
476
|
+
StreamError: HookEvents.StreamError
|
|
477
|
+
};
|
|
478
|
+
var AgentEventEmitter = class {
|
|
479
|
+
registry = /* @__PURE__ */ new Map();
|
|
480
|
+
logger;
|
|
481
|
+
constructor(logger) {
|
|
482
|
+
this.logger = logger ?? getDefaultLogger();
|
|
483
|
+
}
|
|
484
|
+
on(event, listener) {
|
|
485
|
+
const listeners = this.registry.get(event) ?? /* @__PURE__ */ new Set();
|
|
486
|
+
listeners.add(listener);
|
|
487
|
+
this.registry.set(event, listeners);
|
|
488
|
+
return () => this.off(event, listener);
|
|
489
|
+
}
|
|
490
|
+
once(event, listener) {
|
|
491
|
+
const wrapper = (payload) => {
|
|
492
|
+
try {
|
|
493
|
+
listener(payload);
|
|
494
|
+
} finally {
|
|
495
|
+
this.off(event, wrapper);
|
|
496
|
+
}
|
|
497
|
+
};
|
|
498
|
+
return this.on(event, wrapper);
|
|
499
|
+
}
|
|
500
|
+
off(event, listener) {
|
|
501
|
+
const listeners = this.registry.get(event);
|
|
502
|
+
if (!listeners) {
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
listeners.delete(listener);
|
|
506
|
+
if (listeners.size === 0) {
|
|
507
|
+
this.registry.delete(event);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
emit(event, payload) {
|
|
511
|
+
const listeners = Array.from(
|
|
512
|
+
this.registry.get(event) ?? /* @__PURE__ */ new Set()
|
|
513
|
+
);
|
|
514
|
+
if (listeners.length === 0) {
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
for (const listener of listeners) {
|
|
518
|
+
try {
|
|
519
|
+
listener(payload);
|
|
520
|
+
} catch (error) {
|
|
521
|
+
this.logger.warn(`Agent event listener failed for "${event}"`, {
|
|
522
|
+
event,
|
|
523
|
+
error: error instanceof Error ? error.message : String(error)
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
removeAllListeners(event) {
|
|
529
|
+
if (event) {
|
|
530
|
+
this.registry.delete(event);
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
this.registry.clear();
|
|
534
|
+
}
|
|
535
|
+
listenerCount(event) {
|
|
536
|
+
if (event) {
|
|
537
|
+
return this.registry.get(event)?.size ?? 0;
|
|
538
|
+
}
|
|
539
|
+
let total = 0;
|
|
540
|
+
for (const listeners of this.registry.values()) {
|
|
541
|
+
total += listeners.size;
|
|
542
|
+
}
|
|
543
|
+
return total;
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
|
|
466
547
|
// src/base/agent.ts
|
|
467
548
|
init_tool();
|
|
549
|
+
function sanitizeId(name) {
|
|
550
|
+
return name.replace(/\s/g, "_").replace(/-/g, "_").replace(/\./g, "_").replace(/[()]/g, "");
|
|
551
|
+
}
|
|
552
|
+
function sanitizeLabel(text) {
|
|
553
|
+
let cleaned = text.replace(/"/g, "'").replace(/\n/g, " ").replace(/`/g, "");
|
|
554
|
+
cleaned = cleaned.split(".")[0]?.trim() || cleaned.trim();
|
|
555
|
+
if (cleaned.length > 45) {
|
|
556
|
+
cleaned = cleaned.substring(0, 42) + "...";
|
|
557
|
+
}
|
|
558
|
+
return cleaned;
|
|
559
|
+
}
|
|
560
|
+
function getSchemaName(schema) {
|
|
561
|
+
if (!schema) {
|
|
562
|
+
return "N/A";
|
|
563
|
+
}
|
|
564
|
+
try {
|
|
565
|
+
const schemaAny = schema;
|
|
566
|
+
if (schemaAny.description) {
|
|
567
|
+
return schemaAny.description;
|
|
568
|
+
}
|
|
569
|
+
if (schemaAny._def?.description) {
|
|
570
|
+
return schemaAny._def.description;
|
|
571
|
+
}
|
|
572
|
+
const typeName = schemaAny._def?.typeName;
|
|
573
|
+
if (typeName === "ZodObject" && schemaAny._def) {
|
|
574
|
+
const shape = schemaAny._def.shape?.();
|
|
575
|
+
if (shape && typeof shape === "object") {
|
|
576
|
+
const keys = Object.keys(shape);
|
|
577
|
+
if (keys.length > 0) {
|
|
578
|
+
const keyStr = keys.length > 3 ? `{${keys.slice(0, 3).join(", ")}...}` : `{${keys.join(", ")}}`;
|
|
579
|
+
return keyStr;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
return "Object";
|
|
583
|
+
}
|
|
584
|
+
if (typeName) {
|
|
585
|
+
return typeName.replace("Zod", "").replace("Type", "");
|
|
586
|
+
}
|
|
587
|
+
return "Any";
|
|
588
|
+
} catch {
|
|
589
|
+
return "Any";
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
function extractParameters(schema) {
|
|
593
|
+
if (!schema) {
|
|
594
|
+
return null;
|
|
595
|
+
}
|
|
596
|
+
try {
|
|
597
|
+
const schemaAny = schema;
|
|
598
|
+
if (schemaAny._def?.typeName === "ZodObject") {
|
|
599
|
+
const shape = schemaAny._def.shape?.();
|
|
600
|
+
if (shape && typeof shape === "object") {
|
|
601
|
+
const params = [];
|
|
602
|
+
for (const [key, value] of Object.entries(shape)) {
|
|
603
|
+
const fieldType = value?._def?.typeName;
|
|
604
|
+
if (fieldType) {
|
|
605
|
+
const cleanType = fieldType.replace("Zod", "").toLowerCase();
|
|
606
|
+
params.push(`${key}: ${cleanType}`);
|
|
607
|
+
} else {
|
|
608
|
+
params.push(key);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
return params.length > 0 ? params : null;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
return null;
|
|
615
|
+
} catch {
|
|
616
|
+
return null;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
function getRegisteredHooks(hooks) {
|
|
620
|
+
const events = [];
|
|
621
|
+
const allEvents = Object.values(HookEvents);
|
|
622
|
+
for (const event of allEvents) {
|
|
623
|
+
if (hooks.listenerCount(event) > 0) {
|
|
624
|
+
events.push(event);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
return events;
|
|
628
|
+
}
|
|
629
|
+
function getWrappedAgent(tool2) {
|
|
630
|
+
const wrapped = tool2.metadata?.["wrappedAgent"];
|
|
631
|
+
if (wrapped && typeof wrapped === "object" && "name" in wrapped) {
|
|
632
|
+
return wrapped;
|
|
633
|
+
}
|
|
634
|
+
return null;
|
|
635
|
+
}
|
|
636
|
+
function isMcpTool(tool2) {
|
|
637
|
+
return tool2.metadata?.["provider"] === "mcp";
|
|
638
|
+
}
|
|
639
|
+
async function generateAgentFlowDiagram(agent, options = {}) {
|
|
640
|
+
const visited = options._visited ?? /* @__PURE__ */ new Set();
|
|
641
|
+
const isRoot = visited.size === 0;
|
|
642
|
+
const lines = [];
|
|
643
|
+
if (isRoot) {
|
|
644
|
+
lines.push("```mermaid");
|
|
645
|
+
lines.push("graph TB");
|
|
646
|
+
}
|
|
647
|
+
const agentId = sanitizeId(agent.name);
|
|
648
|
+
if (visited.has(agentId)) {
|
|
649
|
+
return "";
|
|
650
|
+
}
|
|
651
|
+
visited.add(agentId);
|
|
652
|
+
let agentLabel = `\u{1F916} ${agent.name}`;
|
|
653
|
+
if (agent.description && agent.description !== `Agent: ${agent.name}` && agent.description !== agent.name) {
|
|
654
|
+
const desc = sanitizeLabel(agent.description);
|
|
655
|
+
agentLabel += `<br/><i>${desc}</i>`;
|
|
656
|
+
}
|
|
657
|
+
const inputSchemaName = getSchemaName(agent.inputSchema);
|
|
658
|
+
const outputSchemaName = getSchemaName(agent.outputSchema);
|
|
659
|
+
agentLabel += `<br/>In: ${inputSchemaName} | Out: ${outputSchemaName}`;
|
|
660
|
+
lines.push(` ${agentId}["${agentLabel}"]:::agent`);
|
|
661
|
+
const agentWithHooks = agent;
|
|
662
|
+
const registeredHooks = getRegisteredHooks(agentWithHooks.hooks);
|
|
663
|
+
if (registeredHooks.length > 0) {
|
|
664
|
+
const hookId = `${agentId}_hooks`;
|
|
665
|
+
const hookNames = registeredHooks.slice(0, 3).join(", ");
|
|
666
|
+
const hookLabel = registeredHooks.length > 3 ? `${hookNames} +${registeredHooks.length - 3}` : hookNames;
|
|
667
|
+
lines.push(` ${hookId}["\u{1FA9D} ${hookLabel}"]:::hook`);
|
|
668
|
+
lines.push(` ${agentId} -.-> ${hookId}`);
|
|
669
|
+
}
|
|
670
|
+
const tools = agent.getTools();
|
|
671
|
+
const regularTools = [];
|
|
672
|
+
const mcpToolsByServer = /* @__PURE__ */ new Map();
|
|
673
|
+
const agentTools = [];
|
|
674
|
+
for (const tool2 of tools) {
|
|
675
|
+
const wrappedAgent = getWrappedAgent(tool2);
|
|
676
|
+
if (wrappedAgent) {
|
|
677
|
+
agentTools.push(tool2);
|
|
678
|
+
} else if (isMcpTool(tool2)) {
|
|
679
|
+
const serverName = tool2.metadata?.["server"] ?? "unknown";
|
|
680
|
+
const serverTools = mcpToolsByServer.get(serverName) ?? [];
|
|
681
|
+
serverTools.push(tool2);
|
|
682
|
+
mcpToolsByServer.set(serverName, serverTools);
|
|
683
|
+
} else {
|
|
684
|
+
regularTools.push(tool2);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
for (const tool2 of regularTools) {
|
|
688
|
+
const toolId = sanitizeId(`${agentId}_${tool2.name}`);
|
|
689
|
+
let toolLabel = `\u2699\uFE0F ${sanitizeLabel(tool2.name)}`;
|
|
690
|
+
if (tool2.description) {
|
|
691
|
+
const desc = sanitizeLabel(tool2.description);
|
|
692
|
+
toolLabel += `<br/><i>${desc}</i>`;
|
|
693
|
+
}
|
|
694
|
+
const params = extractParameters(tool2.schema);
|
|
695
|
+
if (params && params.length > 0) {
|
|
696
|
+
const paramsStr = params.length > 3 ? `${params.slice(0, 3).join(", ")}...` : params.join(", ");
|
|
697
|
+
toolLabel += `<br/>(${paramsStr})`;
|
|
698
|
+
}
|
|
699
|
+
lines.push(` ${toolId}["${toolLabel}"]:::tool`);
|
|
700
|
+
lines.push(` ${agentId} --> ${toolId}`);
|
|
701
|
+
}
|
|
702
|
+
if (mcpToolsByServer.size > 0) {
|
|
703
|
+
for (const [serverName, serverTools] of mcpToolsByServer.entries()) {
|
|
704
|
+
if (options.includeMcpTools) {
|
|
705
|
+
for (const tool2 of serverTools) {
|
|
706
|
+
const toolId = sanitizeId(`${agentId}_${tool2.name}`);
|
|
707
|
+
let toolLabel = `\u2699\uFE0F ${sanitizeLabel(tool2.name)}`;
|
|
708
|
+
if (tool2.description) {
|
|
709
|
+
const desc = sanitizeLabel(tool2.description);
|
|
710
|
+
toolLabel += `<br/><i>${desc}</i>`;
|
|
711
|
+
}
|
|
712
|
+
toolLabel += `<br/>(MCP: ${sanitizeLabel(serverName)})`;
|
|
713
|
+
lines.push(` ${toolId}["${toolLabel}"]:::tool`);
|
|
714
|
+
lines.push(` ${agentId} --> ${toolId}`);
|
|
715
|
+
}
|
|
716
|
+
} else {
|
|
717
|
+
const providerId = sanitizeId(`${agentId}_provider_${serverName}`);
|
|
718
|
+
lines.push(` ${providerId}["\u{1F50C} ${serverName}"]:::provider`);
|
|
719
|
+
lines.push(` ${agentId} --> ${providerId}`);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
for (const tool2 of agentTools) {
|
|
724
|
+
const wrappedAgent = getWrappedAgent(tool2);
|
|
725
|
+
if (wrappedAgent) {
|
|
726
|
+
const subAgentId = sanitizeId(wrappedAgent.name);
|
|
727
|
+
lines.push(` ${agentId} --> ${subAgentId}`);
|
|
728
|
+
const subDiagram = await generateAgentFlowDiagram(wrappedAgent, {
|
|
729
|
+
...options,
|
|
730
|
+
_visited: visited
|
|
731
|
+
});
|
|
732
|
+
const subLines = subDiagram.split("\n").filter(
|
|
733
|
+
(line) => line && !line.includes("```mermaid") && !line.includes("```") && !line.includes("%% Styling") && !line.includes("classDef")
|
|
734
|
+
);
|
|
735
|
+
lines.push(...subLines);
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
if (isRoot) {
|
|
739
|
+
lines.push("");
|
|
740
|
+
lines.push(" %% Styling - Opper Brand Colors");
|
|
741
|
+
lines.push(
|
|
742
|
+
" classDef agent fill:#8CF0DC,stroke:#1B2E40,stroke-width:3px,color:#1B2E40"
|
|
743
|
+
);
|
|
744
|
+
lines.push(
|
|
745
|
+
" classDef tool fill:#FFD7D7,stroke:#3C3CAF,stroke-width:2px,color:#1B2E40"
|
|
746
|
+
);
|
|
747
|
+
lines.push(
|
|
748
|
+
" classDef schema fill:#F8F8F8,stroke:#3C3CAF,stroke-width:2px,color:#1B2E40"
|
|
749
|
+
);
|
|
750
|
+
lines.push(
|
|
751
|
+
" classDef hook fill:#FFB186,stroke:#3C3CAF,stroke-width:2px,color:#1B2E40"
|
|
752
|
+
);
|
|
753
|
+
lines.push(
|
|
754
|
+
" classDef provider fill:#8CECF2,stroke:#1B2E40,stroke-width:2px,color:#1B2E40"
|
|
755
|
+
);
|
|
756
|
+
lines.push("```");
|
|
757
|
+
}
|
|
758
|
+
const diagram = lines.join("\n");
|
|
759
|
+
if (options.outputPath && isRoot) {
|
|
760
|
+
let filePath = options.outputPath;
|
|
761
|
+
if (!filePath.endsWith(".md")) {
|
|
762
|
+
filePath += ".md";
|
|
763
|
+
}
|
|
764
|
+
const fileContent = `# Agent Flow: ${agent.name}
|
|
765
|
+
|
|
766
|
+
${diagram}`;
|
|
767
|
+
await promises.writeFile(filePath, fileContent, "utf-8");
|
|
768
|
+
return filePath;
|
|
769
|
+
}
|
|
770
|
+
return diagram;
|
|
771
|
+
}
|
|
468
772
|
var MemoryEntryMetadataSchema = z.object({
|
|
469
773
|
createdAt: z.number(),
|
|
470
774
|
updatedAt: z.number(),
|
|
@@ -599,6 +903,10 @@ var BaseAgent = class {
|
|
|
599
903
|
* Whether memory is enabled
|
|
600
904
|
*/
|
|
601
905
|
enableMemory;
|
|
906
|
+
/**
|
|
907
|
+
* Whether streaming is enabled
|
|
908
|
+
*/
|
|
909
|
+
enableStreaming;
|
|
602
910
|
/**
|
|
603
911
|
* Memory instance for persistent storage (null if disabled or initialization failed)
|
|
604
912
|
*/
|
|
@@ -611,6 +919,10 @@ var BaseAgent = class {
|
|
|
611
919
|
* Hook manager for lifecycle events
|
|
612
920
|
*/
|
|
613
921
|
hooks;
|
|
922
|
+
/**
|
|
923
|
+
* Event dispatcher for runtime events (notably streaming)
|
|
924
|
+
*/
|
|
925
|
+
events;
|
|
614
926
|
/**
|
|
615
927
|
* Registry of available tools
|
|
616
928
|
*/
|
|
@@ -640,12 +952,26 @@ var BaseAgent = class {
|
|
|
640
952
|
this.inputSchema = config.inputSchema;
|
|
641
953
|
this.outputSchema = config.outputSchema;
|
|
642
954
|
this.enableMemory = config.enableMemory ?? false;
|
|
955
|
+
this.enableStreaming = config.enableStreaming ?? false;
|
|
643
956
|
this.metadata = { ...config.metadata ?? {} };
|
|
644
957
|
this.hooks = new HookManager();
|
|
958
|
+
this.events = new AgentEventEmitter();
|
|
645
959
|
this.tools = /* @__PURE__ */ new Map();
|
|
646
960
|
this.baseTools = /* @__PURE__ */ new Map();
|
|
647
961
|
this.toolProviders = /* @__PURE__ */ new Set();
|
|
648
962
|
this.providerToolRegistry = /* @__PURE__ */ new Map();
|
|
963
|
+
if (config.onStreamStart) {
|
|
964
|
+
this.on(HookEvents.StreamStart, config.onStreamStart);
|
|
965
|
+
}
|
|
966
|
+
if (config.onStreamChunk) {
|
|
967
|
+
this.on(HookEvents.StreamChunk, config.onStreamChunk);
|
|
968
|
+
}
|
|
969
|
+
if (config.onStreamEnd) {
|
|
970
|
+
this.on(HookEvents.StreamEnd, config.onStreamEnd);
|
|
971
|
+
}
|
|
972
|
+
if (config.onStreamError) {
|
|
973
|
+
this.on(HookEvents.StreamError, config.onStreamError);
|
|
974
|
+
}
|
|
649
975
|
this.opperConfig = {
|
|
650
976
|
apiKey: config.opperConfig?.apiKey ?? process.env["OPPER_API_KEY"],
|
|
651
977
|
baseUrl: config.opperConfig?.baseUrl,
|
|
@@ -798,7 +1124,9 @@ var BaseAgent = class {
|
|
|
798
1124
|
...this.inputSchema && { schema: this.inputSchema },
|
|
799
1125
|
metadata: {
|
|
800
1126
|
isAgent: true,
|
|
801
|
-
agentName: this.name
|
|
1127
|
+
agentName: this.name,
|
|
1128
|
+
wrappedAgent: this
|
|
1129
|
+
// Store reference to this agent for visualization
|
|
802
1130
|
},
|
|
803
1131
|
execute: async (input, executionContext) => {
|
|
804
1132
|
try {
|
|
@@ -825,6 +1153,35 @@ var BaseAgent = class {
|
|
|
825
1153
|
registerHook(event, handler) {
|
|
826
1154
|
return this.hooks.on(event, handler);
|
|
827
1155
|
}
|
|
1156
|
+
/**
|
|
1157
|
+
* Register an event listener.
|
|
1158
|
+
*
|
|
1159
|
+
* @param event - Event name
|
|
1160
|
+
* @param listener - Listener callback
|
|
1161
|
+
* @returns Cleanup function to unregister the listener
|
|
1162
|
+
*/
|
|
1163
|
+
on(event, listener) {
|
|
1164
|
+
return this.events.on(event, listener);
|
|
1165
|
+
}
|
|
1166
|
+
/**
|
|
1167
|
+
* Register a one-time event listener that removes itself after the first call.
|
|
1168
|
+
*
|
|
1169
|
+
* @param event - Event name
|
|
1170
|
+
* @param listener - Listener callback
|
|
1171
|
+
* @returns Cleanup function (no-op once listener fires)
|
|
1172
|
+
*/
|
|
1173
|
+
once(event, listener) {
|
|
1174
|
+
return this.events.once(event, listener);
|
|
1175
|
+
}
|
|
1176
|
+
/**
|
|
1177
|
+
* Remove a previously registered event listener.
|
|
1178
|
+
*
|
|
1179
|
+
* @param event - Event name
|
|
1180
|
+
* @param listener - Listener callback to remove
|
|
1181
|
+
*/
|
|
1182
|
+
off(event, listener) {
|
|
1183
|
+
this.events.off(event, listener);
|
|
1184
|
+
}
|
|
828
1185
|
/**
|
|
829
1186
|
* Trigger a hook event with a payload.
|
|
830
1187
|
* Swallows errors to prevent hook failures from breaking agent execution.
|
|
@@ -839,6 +1196,15 @@ var BaseAgent = class {
|
|
|
839
1196
|
console.warn(`Hook error for event ${event}:`, error);
|
|
840
1197
|
}
|
|
841
1198
|
}
|
|
1199
|
+
/**
|
|
1200
|
+
* Emit a runtime event to listeners.
|
|
1201
|
+
*
|
|
1202
|
+
* @param event - Event name
|
|
1203
|
+
* @param payload - Event payload
|
|
1204
|
+
*/
|
|
1205
|
+
emitAgentEvent(event, payload) {
|
|
1206
|
+
this.events.emit(event, payload);
|
|
1207
|
+
}
|
|
842
1208
|
/**
|
|
843
1209
|
* Execute a tool with proper context, hooks, and error handling.
|
|
844
1210
|
*
|
|
@@ -996,6 +1362,30 @@ var BaseAgent = class {
|
|
|
996
1362
|
});
|
|
997
1363
|
await Promise.allSettled(teardownPromises);
|
|
998
1364
|
}
|
|
1365
|
+
/**
|
|
1366
|
+
* Generate a Mermaid flowchart diagram visualizing the agent's structure and flow.
|
|
1367
|
+
* Shows tools, hooks, schemas, providers, and nested agents.
|
|
1368
|
+
*
|
|
1369
|
+
* @param options - Visualization options
|
|
1370
|
+
* @returns Mermaid markdown string, or file path if outputPath was provided
|
|
1371
|
+
*
|
|
1372
|
+
* @example
|
|
1373
|
+
* ```typescript
|
|
1374
|
+
* // Generate diagram string
|
|
1375
|
+
* const diagram = await agent.visualizeFlow();
|
|
1376
|
+
* console.log(diagram);
|
|
1377
|
+
*
|
|
1378
|
+
* // Save to file
|
|
1379
|
+
* const path = await agent.visualizeFlow({ outputPath: "agent_flow.md" });
|
|
1380
|
+
* console.log(`Saved to ${path}`);
|
|
1381
|
+
*
|
|
1382
|
+
* // Include MCP tools details
|
|
1383
|
+
* const diagram = await agent.visualizeFlow({ includeMcpTools: true });
|
|
1384
|
+
* ```
|
|
1385
|
+
*/
|
|
1386
|
+
async visualizeFlow(options) {
|
|
1387
|
+
return generateAgentFlowDiagram(this, options);
|
|
1388
|
+
}
|
|
999
1389
|
};
|
|
1000
1390
|
|
|
1001
1391
|
// src/index.ts
|
|
@@ -1088,7 +1478,7 @@ var ToolExecutionSummarySchema = z.object({
|
|
|
1088
1478
|
|
|
1089
1479
|
// package.json
|
|
1090
1480
|
var package_default = {
|
|
1091
|
-
version: "0.
|
|
1481
|
+
version: "0.2.0"};
|
|
1092
1482
|
|
|
1093
1483
|
// src/utils/version.ts
|
|
1094
1484
|
var SDK_NAME = "@opperai/agents";
|
|
@@ -1151,7 +1541,7 @@ var OpperClient = class {
|
|
|
1151
1541
|
return this.withRetry(async () => {
|
|
1152
1542
|
const inputSchema = this.toJsonSchema(options.inputSchema);
|
|
1153
1543
|
const outputSchema = this.toJsonSchema(options.outputSchema);
|
|
1154
|
-
const
|
|
1544
|
+
const callPayload = {
|
|
1155
1545
|
name: options.name,
|
|
1156
1546
|
instructions: options.instructions,
|
|
1157
1547
|
input: options.input,
|
|
@@ -1159,7 +1549,8 @@ var OpperClient = class {
|
|
|
1159
1549
|
...outputSchema && { outputSchema },
|
|
1160
1550
|
...options.model && { model: options.model },
|
|
1161
1551
|
...options.parentSpanId && { parentSpanId: options.parentSpanId }
|
|
1162
|
-
}
|
|
1552
|
+
};
|
|
1553
|
+
const response = options.signal ? await this.client.call(callPayload, { signal: options.signal }) : await this.client.call(callPayload);
|
|
1163
1554
|
const usagePayload = extractUsage(response);
|
|
1164
1555
|
const costPayload = extractCost(response);
|
|
1165
1556
|
const usage = {
|
|
@@ -1177,6 +1568,36 @@ var OpperClient = class {
|
|
|
1177
1568
|
return result;
|
|
1178
1569
|
}, options.name);
|
|
1179
1570
|
}
|
|
1571
|
+
/**
|
|
1572
|
+
* Stream a call to Opper with retry logic
|
|
1573
|
+
*/
|
|
1574
|
+
async stream(options) {
|
|
1575
|
+
return this.withRetry(async () => {
|
|
1576
|
+
const inputSchema = this.toJsonSchema(options.inputSchema);
|
|
1577
|
+
const outputSchema = this.toJsonSchema(options.outputSchema);
|
|
1578
|
+
const streamPayload = {
|
|
1579
|
+
name: options.name,
|
|
1580
|
+
instructions: options.instructions,
|
|
1581
|
+
input: options.input,
|
|
1582
|
+
...inputSchema && { inputSchema },
|
|
1583
|
+
...outputSchema && { outputSchema },
|
|
1584
|
+
...options.model && { model: options.model },
|
|
1585
|
+
...options.parentSpanId && { parentSpanId: options.parentSpanId }
|
|
1586
|
+
};
|
|
1587
|
+
const response = options.signal ? await this.client.stream(streamPayload, { signal: options.signal }) : await this.client.stream(streamPayload);
|
|
1588
|
+
const iterable = {
|
|
1589
|
+
async *[Symbol.asyncIterator]() {
|
|
1590
|
+
for await (const event of response.result) {
|
|
1591
|
+
yield event;
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
};
|
|
1595
|
+
return {
|
|
1596
|
+
headers: response.headers,
|
|
1597
|
+
result: iterable
|
|
1598
|
+
};
|
|
1599
|
+
}, `stream:${options.name}`);
|
|
1600
|
+
}
|
|
1180
1601
|
/**
|
|
1181
1602
|
* Create a span for tracing
|
|
1182
1603
|
*/
|
|
@@ -1345,6 +1766,189 @@ var mergeSchemaDefaults = (schema, value) => {
|
|
|
1345
1766
|
return validateSchema(schema, value);
|
|
1346
1767
|
};
|
|
1347
1768
|
|
|
1769
|
+
// src/utils/streaming.ts
|
|
1770
|
+
var STREAM_ROOT_PATH = "_root";
|
|
1771
|
+
var NUMERIC_TOKEN_PATTERN = /^\d+$/;
|
|
1772
|
+
var BRACKET_TOKEN_PATTERN = /\[(\d+)\]/g;
|
|
1773
|
+
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1774
|
+
var coercePrimitive = (value) => {
|
|
1775
|
+
const trimmed = value.trim();
|
|
1776
|
+
if (trimmed.length === 0) {
|
|
1777
|
+
return value;
|
|
1778
|
+
}
|
|
1779
|
+
const lower = trimmed.toLowerCase();
|
|
1780
|
+
if (lower === "true") {
|
|
1781
|
+
return true;
|
|
1782
|
+
}
|
|
1783
|
+
if (lower === "false") {
|
|
1784
|
+
return false;
|
|
1785
|
+
}
|
|
1786
|
+
if (lower === "null") {
|
|
1787
|
+
return null;
|
|
1788
|
+
}
|
|
1789
|
+
if (/^-?\d+$/.test(trimmed)) {
|
|
1790
|
+
const intValue = Number.parseInt(trimmed, 10);
|
|
1791
|
+
return Number.isNaN(intValue) ? value : intValue;
|
|
1792
|
+
}
|
|
1793
|
+
if (/^-?\d+\.\d+$/.test(trimmed)) {
|
|
1794
|
+
const floatValue = Number.parseFloat(trimmed);
|
|
1795
|
+
return Number.isNaN(floatValue) ? value : floatValue;
|
|
1796
|
+
}
|
|
1797
|
+
return value;
|
|
1798
|
+
};
|
|
1799
|
+
var toDisplayString = (value) => {
|
|
1800
|
+
if (value === null || value === void 0) {
|
|
1801
|
+
return "";
|
|
1802
|
+
}
|
|
1803
|
+
if (typeof value === "string") {
|
|
1804
|
+
return value;
|
|
1805
|
+
}
|
|
1806
|
+
return String(value);
|
|
1807
|
+
};
|
|
1808
|
+
var parsePathSegments = (path) => {
|
|
1809
|
+
return path.replace(BRACKET_TOKEN_PATTERN, (_, index) => `.${index}`).split(".").map((segment) => segment.trim()).filter((segment) => segment.length > 0);
|
|
1810
|
+
};
|
|
1811
|
+
var setNestedValue = (target, path, value) => {
|
|
1812
|
+
const segments = parsePathSegments(path);
|
|
1813
|
+
if (segments.length === 0) {
|
|
1814
|
+
return;
|
|
1815
|
+
}
|
|
1816
|
+
let current = target;
|
|
1817
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
1818
|
+
const segment = segments[i];
|
|
1819
|
+
if (segment === void 0) {
|
|
1820
|
+
return;
|
|
1821
|
+
}
|
|
1822
|
+
const existing = current[segment];
|
|
1823
|
+
if (!isRecord(existing)) {
|
|
1824
|
+
const nextLevel = {};
|
|
1825
|
+
current[segment] = nextLevel;
|
|
1826
|
+
current = nextLevel;
|
|
1827
|
+
continue;
|
|
1828
|
+
}
|
|
1829
|
+
current = existing;
|
|
1830
|
+
}
|
|
1831
|
+
const lastSegment = segments[segments.length - 1];
|
|
1832
|
+
if (lastSegment === void 0) {
|
|
1833
|
+
return;
|
|
1834
|
+
}
|
|
1835
|
+
current[lastSegment] = value;
|
|
1836
|
+
};
|
|
1837
|
+
var normalizeIndexed = (value) => {
|
|
1838
|
+
if (Array.isArray(value)) {
|
|
1839
|
+
return value.map((item) => normalizeIndexed(item));
|
|
1840
|
+
}
|
|
1841
|
+
if (!isRecord(value)) {
|
|
1842
|
+
return value;
|
|
1843
|
+
}
|
|
1844
|
+
const normalizedEntries = {};
|
|
1845
|
+
for (const [key, entryValue] of Object.entries(value)) {
|
|
1846
|
+
normalizedEntries[key] = normalizeIndexed(entryValue);
|
|
1847
|
+
}
|
|
1848
|
+
const keys = Object.keys(normalizedEntries);
|
|
1849
|
+
if (keys.length > 0 && keys.every((key) => NUMERIC_TOKEN_PATTERN.test(key))) {
|
|
1850
|
+
const parsedIndices = keys.map((key) => Number.parseInt(key, 10)).filter((index) => Number.isFinite(index) && index >= 0);
|
|
1851
|
+
if (parsedIndices.length === 0) {
|
|
1852
|
+
return normalizedEntries;
|
|
1853
|
+
}
|
|
1854
|
+
const maxIndex = Math.max(...parsedIndices);
|
|
1855
|
+
const result = new Array(maxIndex + 1).fill(void 0);
|
|
1856
|
+
for (const [key, entryValue] of Object.entries(normalizedEntries)) {
|
|
1857
|
+
const index = Number.parseInt(key, 10);
|
|
1858
|
+
if (!Number.isFinite(index) || index < 0) {
|
|
1859
|
+
continue;
|
|
1860
|
+
}
|
|
1861
|
+
result[index] = entryValue;
|
|
1862
|
+
}
|
|
1863
|
+
return result.map(
|
|
1864
|
+
(item) => item === void 0 ? null : item
|
|
1865
|
+
);
|
|
1866
|
+
}
|
|
1867
|
+
return normalizedEntries;
|
|
1868
|
+
};
|
|
1869
|
+
var resolveFieldValue = (path, displayBuffers, valueBuffers) => {
|
|
1870
|
+
const values = valueBuffers.get(path) ?? [];
|
|
1871
|
+
if (values.length === 0) {
|
|
1872
|
+
return displayBuffers.get(path) ?? "";
|
|
1873
|
+
}
|
|
1874
|
+
const last = values[values.length - 1];
|
|
1875
|
+
if (typeof last === "number" || typeof last === "boolean") {
|
|
1876
|
+
return last;
|
|
1877
|
+
}
|
|
1878
|
+
if (last === null) {
|
|
1879
|
+
return null;
|
|
1880
|
+
}
|
|
1881
|
+
const joined = displayBuffers.get(path) ?? values.map((value) => toDisplayString(value)).join("");
|
|
1882
|
+
return coercePrimitive(joined);
|
|
1883
|
+
};
|
|
1884
|
+
var StreamAssembler = class {
|
|
1885
|
+
displayBuffers = /* @__PURE__ */ new Map();
|
|
1886
|
+
valueBuffers = /* @__PURE__ */ new Map();
|
|
1887
|
+
schema;
|
|
1888
|
+
constructor(options = {}) {
|
|
1889
|
+
this.schema = options.schema;
|
|
1890
|
+
}
|
|
1891
|
+
feed(chunk) {
|
|
1892
|
+
const { delta } = chunk;
|
|
1893
|
+
if (delta === null || delta === void 0) {
|
|
1894
|
+
return null;
|
|
1895
|
+
}
|
|
1896
|
+
const path = chunk.jsonPath === null || chunk.jsonPath === void 0 ? STREAM_ROOT_PATH : chunk.jsonPath;
|
|
1897
|
+
const existingDisplay = this.displayBuffers.get(path) ?? "";
|
|
1898
|
+
const nextDisplay = `${existingDisplay}${toDisplayString(delta)}`;
|
|
1899
|
+
this.displayBuffers.set(path, nextDisplay);
|
|
1900
|
+
const existingValues = this.valueBuffers.get(path) ?? [];
|
|
1901
|
+
existingValues.push(delta);
|
|
1902
|
+
this.valueBuffers.set(path, existingValues);
|
|
1903
|
+
return {
|
|
1904
|
+
path,
|
|
1905
|
+
accumulated: nextDisplay,
|
|
1906
|
+
snapshot: this.snapshot()
|
|
1907
|
+
};
|
|
1908
|
+
}
|
|
1909
|
+
snapshot() {
|
|
1910
|
+
return Object.fromEntries(this.displayBuffers.entries());
|
|
1911
|
+
}
|
|
1912
|
+
hasStructuredFields() {
|
|
1913
|
+
const keys = Array.from(this.displayBuffers.keys());
|
|
1914
|
+
return keys.some((key) => key !== STREAM_ROOT_PATH);
|
|
1915
|
+
}
|
|
1916
|
+
finalize() {
|
|
1917
|
+
if (this.displayBuffers.size === 0) {
|
|
1918
|
+
return { type: "empty" };
|
|
1919
|
+
}
|
|
1920
|
+
if (!this.hasStructuredFields()) {
|
|
1921
|
+
const root = this.displayBuffers.get(STREAM_ROOT_PATH) ?? "";
|
|
1922
|
+
return { type: "root", rootText: root };
|
|
1923
|
+
}
|
|
1924
|
+
const structured = this.reconstructStructured();
|
|
1925
|
+
let coerced = structured;
|
|
1926
|
+
if (this.schema !== void 0) {
|
|
1927
|
+
try {
|
|
1928
|
+
coerced = this.schema.parse(structured);
|
|
1929
|
+
} catch {
|
|
1930
|
+
coerced = structured;
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1933
|
+
return { type: "structured", structured: coerced };
|
|
1934
|
+
}
|
|
1935
|
+
getFieldBuffers() {
|
|
1936
|
+
return new Map(this.displayBuffers);
|
|
1937
|
+
}
|
|
1938
|
+
reconstructStructured() {
|
|
1939
|
+
const result = {};
|
|
1940
|
+
for (const path of this.valueBuffers.keys()) {
|
|
1941
|
+
if (path === STREAM_ROOT_PATH) {
|
|
1942
|
+
continue;
|
|
1943
|
+
}
|
|
1944
|
+
const value = resolveFieldValue(path, this.displayBuffers, this.valueBuffers);
|
|
1945
|
+
setNestedValue(result, path, value);
|
|
1946
|
+
}
|
|
1947
|
+
return normalizeIndexed(result);
|
|
1948
|
+
}
|
|
1949
|
+
};
|
|
1950
|
+
var createStreamAssembler = (options) => new StreamAssembler(options);
|
|
1951
|
+
|
|
1348
1952
|
// src/core/agent.ts
|
|
1349
1953
|
var isToolSuccessResult = (value) => {
|
|
1350
1954
|
if (typeof value !== "object" || value === null) {
|
|
@@ -1471,6 +2075,9 @@ var Agent = class extends BaseAgent {
|
|
|
1471
2075
|
const spanName = "think";
|
|
1472
2076
|
this.log("Think step", { iteration: context.iteration });
|
|
1473
2077
|
await this.triggerHook(HookEvents.LlmCall, { context, callType: "think" });
|
|
2078
|
+
if (this.enableStreaming) {
|
|
2079
|
+
return this.thinkStreaming(input, context);
|
|
2080
|
+
}
|
|
1474
2081
|
try {
|
|
1475
2082
|
const instructions = this.buildThinkInstructions();
|
|
1476
2083
|
const thinkContext = await this.buildThinkContext(input, context);
|
|
@@ -1513,6 +2120,132 @@ var Agent = class extends BaseAgent {
|
|
|
1513
2120
|
);
|
|
1514
2121
|
}
|
|
1515
2122
|
}
|
|
2123
|
+
async thinkStreaming(input, context) {
|
|
2124
|
+
const spanName = "think";
|
|
2125
|
+
const instructions = this.buildThinkInstructions();
|
|
2126
|
+
const thinkContext = await this.buildThinkContext(input, context);
|
|
2127
|
+
const assembler = createStreamAssembler({
|
|
2128
|
+
schema: AgentDecisionSchema
|
|
2129
|
+
});
|
|
2130
|
+
let streamSpanId;
|
|
2131
|
+
await this.triggerHook(HookEvents.StreamStart, {
|
|
2132
|
+
context,
|
|
2133
|
+
callType: "think"
|
|
2134
|
+
});
|
|
2135
|
+
this.emitAgentEvent(HookEvents.StreamStart, {
|
|
2136
|
+
context,
|
|
2137
|
+
callType: "think"
|
|
2138
|
+
});
|
|
2139
|
+
try {
|
|
2140
|
+
const streamResponse = await this.opperClient.stream({
|
|
2141
|
+
name: spanName,
|
|
2142
|
+
instructions,
|
|
2143
|
+
input: thinkContext,
|
|
2144
|
+
outputSchema: AgentDecisionSchema,
|
|
2145
|
+
model: this.model,
|
|
2146
|
+
...context.parentSpanId && { parentSpanId: context.parentSpanId }
|
|
2147
|
+
});
|
|
2148
|
+
for await (const event of streamResponse.result) {
|
|
2149
|
+
const data = event?.data;
|
|
2150
|
+
if (!data) {
|
|
2151
|
+
continue;
|
|
2152
|
+
}
|
|
2153
|
+
if (!streamSpanId && typeof data.spanId === "string" && data.spanId) {
|
|
2154
|
+
streamSpanId = data.spanId;
|
|
2155
|
+
}
|
|
2156
|
+
const feedResult = assembler.feed({
|
|
2157
|
+
delta: data.delta,
|
|
2158
|
+
jsonPath: data.jsonPath
|
|
2159
|
+
});
|
|
2160
|
+
if (!feedResult) {
|
|
2161
|
+
continue;
|
|
2162
|
+
}
|
|
2163
|
+
const chunkPayload = {
|
|
2164
|
+
context,
|
|
2165
|
+
callType: "think",
|
|
2166
|
+
chunkData: {
|
|
2167
|
+
delta: data.delta,
|
|
2168
|
+
jsonPath: data.jsonPath ?? null,
|
|
2169
|
+
chunkType: data.chunkType ?? null
|
|
2170
|
+
},
|
|
2171
|
+
accumulated: feedResult.accumulated,
|
|
2172
|
+
fieldBuffers: feedResult.snapshot
|
|
2173
|
+
};
|
|
2174
|
+
await this.triggerHook(HookEvents.StreamChunk, chunkPayload);
|
|
2175
|
+
this.emitAgentEvent(HookEvents.StreamChunk, chunkPayload);
|
|
2176
|
+
}
|
|
2177
|
+
const fieldBuffers = assembler.snapshot();
|
|
2178
|
+
const endPayload = {
|
|
2179
|
+
context,
|
|
2180
|
+
callType: "think",
|
|
2181
|
+
fieldBuffers
|
|
2182
|
+
};
|
|
2183
|
+
await this.triggerHook(HookEvents.StreamEnd, endPayload);
|
|
2184
|
+
this.emitAgentEvent(HookEvents.StreamEnd, endPayload);
|
|
2185
|
+
const finalize = assembler.finalize();
|
|
2186
|
+
let decision;
|
|
2187
|
+
if (finalize.type === "structured" && finalize.structured) {
|
|
2188
|
+
decision = AgentDecisionSchema.parse(
|
|
2189
|
+
finalize.structured
|
|
2190
|
+
);
|
|
2191
|
+
} else {
|
|
2192
|
+
decision = AgentDecisionSchema.parse({
|
|
2193
|
+
reasoning: finalize.type === "root" ? finalize.rootText ?? "" : ""
|
|
2194
|
+
});
|
|
2195
|
+
}
|
|
2196
|
+
const usageTracked = await this.trackStreamingUsageBySpan(
|
|
2197
|
+
context,
|
|
2198
|
+
streamSpanId
|
|
2199
|
+
);
|
|
2200
|
+
if (!usageTracked) {
|
|
2201
|
+
context.updateUsage({
|
|
2202
|
+
requests: 1,
|
|
2203
|
+
inputTokens: 0,
|
|
2204
|
+
outputTokens: 0,
|
|
2205
|
+
totalTokens: 0,
|
|
2206
|
+
cost: { generation: 0, platform: 0, total: 0 }
|
|
2207
|
+
});
|
|
2208
|
+
}
|
|
2209
|
+
await this.triggerHook(HookEvents.LlmResponse, {
|
|
2210
|
+
context,
|
|
2211
|
+
callType: "think",
|
|
2212
|
+
response: streamResponse,
|
|
2213
|
+
parsed: decision
|
|
2214
|
+
});
|
|
2215
|
+
await this.triggerHook(HookEvents.ThinkEnd, {
|
|
2216
|
+
context,
|
|
2217
|
+
thought: { reasoning: decision.reasoning }
|
|
2218
|
+
});
|
|
2219
|
+
this.log("Think result", {
|
|
2220
|
+
reasoning: decision.reasoning,
|
|
2221
|
+
toolCalls: decision.toolCalls.length,
|
|
2222
|
+
memoryReads: decision.memoryReads?.length ?? 0,
|
|
2223
|
+
memoryWrites: Object.keys(decision.memoryUpdates ?? {}).length
|
|
2224
|
+
});
|
|
2225
|
+
const resultPayload = {
|
|
2226
|
+
decision
|
|
2227
|
+
};
|
|
2228
|
+
if (streamSpanId) {
|
|
2229
|
+
resultPayload.spanId = streamSpanId;
|
|
2230
|
+
}
|
|
2231
|
+
return resultPayload;
|
|
2232
|
+
} catch (error) {
|
|
2233
|
+
await this.triggerHook(HookEvents.StreamError, {
|
|
2234
|
+
context,
|
|
2235
|
+
callType: "think",
|
|
2236
|
+
error
|
|
2237
|
+
});
|
|
2238
|
+
this.emitAgentEvent(HookEvents.StreamError, {
|
|
2239
|
+
context,
|
|
2240
|
+
callType: "think",
|
|
2241
|
+
error
|
|
2242
|
+
});
|
|
2243
|
+
this.logger.error("Think step failed", error);
|
|
2244
|
+
throw new Error(
|
|
2245
|
+
`Think step failed: ${error instanceof Error ? error.message : String(error)}`
|
|
2246
|
+
);
|
|
2247
|
+
}
|
|
2248
|
+
}
|
|
1516
2249
|
/**
|
|
1517
2250
|
* Build static instructions for the think step
|
|
1518
2251
|
*/
|
|
@@ -1826,6 +2559,13 @@ The memory you write persists across all process() calls on this agent.`;
|
|
|
1826
2559
|
};
|
|
1827
2560
|
const instructions = `Generate the final result based on the execution history.
|
|
1828
2561
|
Follow any instructions provided for formatting and style.`;
|
|
2562
|
+
if (this.enableStreaming) {
|
|
2563
|
+
return this.generateFinalResultStreaming(
|
|
2564
|
+
context,
|
|
2565
|
+
finalContext,
|
|
2566
|
+
instructions
|
|
2567
|
+
);
|
|
2568
|
+
}
|
|
1829
2569
|
try {
|
|
1830
2570
|
const callOptions = {
|
|
1831
2571
|
name: "generate_final_result",
|
|
@@ -1861,6 +2601,170 @@ Follow any instructions provided for formatting and style.`;
|
|
|
1861
2601
|
);
|
|
1862
2602
|
}
|
|
1863
2603
|
}
|
|
2604
|
+
async generateFinalResultStreaming(context, finalContext, instructions) {
|
|
2605
|
+
const assembler = createStreamAssembler(
|
|
2606
|
+
this.outputSchema ? { schema: this.outputSchema } : void 0
|
|
2607
|
+
);
|
|
2608
|
+
let streamSpanId;
|
|
2609
|
+
await this.triggerHook(HookEvents.StreamStart, {
|
|
2610
|
+
context,
|
|
2611
|
+
callType: "final_result"
|
|
2612
|
+
});
|
|
2613
|
+
this.emitAgentEvent(HookEvents.StreamStart, {
|
|
2614
|
+
context,
|
|
2615
|
+
callType: "final_result"
|
|
2616
|
+
});
|
|
2617
|
+
try {
|
|
2618
|
+
const streamResponse = await this.opperClient.stream({
|
|
2619
|
+
name: "generate_final_result",
|
|
2620
|
+
instructions,
|
|
2621
|
+
input: finalContext,
|
|
2622
|
+
model: this.model,
|
|
2623
|
+
...context.parentSpanId && { parentSpanId: context.parentSpanId },
|
|
2624
|
+
...this.outputSchema && { outputSchema: this.outputSchema }
|
|
2625
|
+
});
|
|
2626
|
+
for await (const event of streamResponse.result) {
|
|
2627
|
+
const data = event?.data;
|
|
2628
|
+
if (!data) {
|
|
2629
|
+
continue;
|
|
2630
|
+
}
|
|
2631
|
+
if (!streamSpanId && typeof data.spanId === "string" && data.spanId) {
|
|
2632
|
+
streamSpanId = data.spanId;
|
|
2633
|
+
}
|
|
2634
|
+
const feedResult = assembler.feed({
|
|
2635
|
+
delta: data.delta,
|
|
2636
|
+
jsonPath: data.jsonPath
|
|
2637
|
+
});
|
|
2638
|
+
if (!feedResult) {
|
|
2639
|
+
continue;
|
|
2640
|
+
}
|
|
2641
|
+
const chunkPayload = {
|
|
2642
|
+
context,
|
|
2643
|
+
callType: "final_result",
|
|
2644
|
+
chunkData: {
|
|
2645
|
+
delta: data.delta,
|
|
2646
|
+
jsonPath: data.jsonPath ?? null,
|
|
2647
|
+
chunkType: data.chunkType ?? null
|
|
2648
|
+
},
|
|
2649
|
+
accumulated: feedResult.accumulated,
|
|
2650
|
+
fieldBuffers: feedResult.snapshot
|
|
2651
|
+
};
|
|
2652
|
+
await this.triggerHook(HookEvents.StreamChunk, chunkPayload);
|
|
2653
|
+
this.emitAgentEvent(HookEvents.StreamChunk, chunkPayload);
|
|
2654
|
+
}
|
|
2655
|
+
const fieldBuffers = assembler.snapshot();
|
|
2656
|
+
const endPayload = {
|
|
2657
|
+
context,
|
|
2658
|
+
callType: "final_result",
|
|
2659
|
+
fieldBuffers
|
|
2660
|
+
};
|
|
2661
|
+
await this.triggerHook(HookEvents.StreamEnd, endPayload);
|
|
2662
|
+
this.emitAgentEvent(HookEvents.StreamEnd, endPayload);
|
|
2663
|
+
const finalize = assembler.finalize();
|
|
2664
|
+
let result;
|
|
2665
|
+
if (this.outputSchema) {
|
|
2666
|
+
if (finalize.type !== "structured" || finalize.structured === void 0) {
|
|
2667
|
+
throw new Error(
|
|
2668
|
+
"Streaming response did not provide structured data for the configured output schema."
|
|
2669
|
+
);
|
|
2670
|
+
}
|
|
2671
|
+
result = this.outputSchema.parse(
|
|
2672
|
+
finalize.structured
|
|
2673
|
+
);
|
|
2674
|
+
} else if (finalize.type === "root") {
|
|
2675
|
+
result = finalize.rootText ?? "";
|
|
2676
|
+
} else if (finalize.type === "structured" && finalize.structured) {
|
|
2677
|
+
result = JSON.stringify(finalize.structured);
|
|
2678
|
+
} else {
|
|
2679
|
+
result = "";
|
|
2680
|
+
}
|
|
2681
|
+
const usageTracked = await this.trackStreamingUsageBySpan(
|
|
2682
|
+
context,
|
|
2683
|
+
streamSpanId
|
|
2684
|
+
);
|
|
2685
|
+
if (!usageTracked) {
|
|
2686
|
+
context.updateUsage({
|
|
2687
|
+
requests: 1,
|
|
2688
|
+
inputTokens: 0,
|
|
2689
|
+
outputTokens: 0,
|
|
2690
|
+
totalTokens: 0,
|
|
2691
|
+
cost: { generation: 0, platform: 0, total: 0 }
|
|
2692
|
+
});
|
|
2693
|
+
}
|
|
2694
|
+
await this.triggerHook(HookEvents.LlmResponse, {
|
|
2695
|
+
context,
|
|
2696
|
+
callType: "final_result",
|
|
2697
|
+
response: streamResponse,
|
|
2698
|
+
parsed: result
|
|
2699
|
+
});
|
|
2700
|
+
this.log(
|
|
2701
|
+
this.outputSchema ? "Final result generated (streaming, schema-validated)" : "Final result generated (streaming)"
|
|
2702
|
+
);
|
|
2703
|
+
return result;
|
|
2704
|
+
} catch (error) {
|
|
2705
|
+
await this.triggerHook(HookEvents.StreamError, {
|
|
2706
|
+
context,
|
|
2707
|
+
callType: "final_result",
|
|
2708
|
+
error
|
|
2709
|
+
});
|
|
2710
|
+
this.emitAgentEvent(HookEvents.StreamError, {
|
|
2711
|
+
context,
|
|
2712
|
+
callType: "final_result",
|
|
2713
|
+
error
|
|
2714
|
+
});
|
|
2715
|
+
this.logger.error("Failed to generate final result", error);
|
|
2716
|
+
throw new Error(
|
|
2717
|
+
`Failed to generate final result: ${error instanceof Error ? error.message : String(error)}`
|
|
2718
|
+
);
|
|
2719
|
+
}
|
|
2720
|
+
}
|
|
2721
|
+
async trackStreamingUsageBySpan(context, spanId) {
|
|
2722
|
+
if (!spanId) {
|
|
2723
|
+
return false;
|
|
2724
|
+
}
|
|
2725
|
+
try {
|
|
2726
|
+
const span = await this.opperClient.getClient().spans.get(spanId);
|
|
2727
|
+
const traceId = span?.traceId ?? span?.trace_id;
|
|
2728
|
+
if (!traceId) {
|
|
2729
|
+
return false;
|
|
2730
|
+
}
|
|
2731
|
+
const trace = await this.opperClient.getClient().traces.get(traceId);
|
|
2732
|
+
const spans = trace?.spans;
|
|
2733
|
+
if (!Array.isArray(spans)) {
|
|
2734
|
+
return false;
|
|
2735
|
+
}
|
|
2736
|
+
for (const entry of spans) {
|
|
2737
|
+
const entryId = entry?.id ?? entry["id"];
|
|
2738
|
+
if (entryId !== spanId) {
|
|
2739
|
+
continue;
|
|
2740
|
+
}
|
|
2741
|
+
const data = entry?.data;
|
|
2742
|
+
if (!data) {
|
|
2743
|
+
continue;
|
|
2744
|
+
}
|
|
2745
|
+
const record = data;
|
|
2746
|
+
const primaryTotal = record["totalTokens"];
|
|
2747
|
+
const fallbackTotal = record["total_tokens"];
|
|
2748
|
+
const totalTokensRaw = typeof primaryTotal === "number" && Number.isFinite(primaryTotal) ? primaryTotal : typeof fallbackTotal === "number" && Number.isFinite(fallbackTotal) ? fallbackTotal : void 0;
|
|
2749
|
+
if (totalTokensRaw !== void 0) {
|
|
2750
|
+
context.updateUsage({
|
|
2751
|
+
requests: 1,
|
|
2752
|
+
inputTokens: 0,
|
|
2753
|
+
outputTokens: 0,
|
|
2754
|
+
totalTokens: totalTokensRaw,
|
|
2755
|
+
cost: { generation: 0, platform: 0, total: 0 }
|
|
2756
|
+
});
|
|
2757
|
+
return true;
|
|
2758
|
+
}
|
|
2759
|
+
}
|
|
2760
|
+
} catch (error) {
|
|
2761
|
+
this.logger.warn("Could not fetch streaming usage", {
|
|
2762
|
+
spanId,
|
|
2763
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2764
|
+
});
|
|
2765
|
+
}
|
|
2766
|
+
return false;
|
|
2767
|
+
}
|
|
1864
2768
|
/**
|
|
1865
2769
|
* Log helper
|
|
1866
2770
|
*/
|
|
@@ -2077,7 +2981,7 @@ var createMCPServerConfig = MCPconfig;
|
|
|
2077
2981
|
|
|
2078
2982
|
// src/mcp/provider.ts
|
|
2079
2983
|
init_tool();
|
|
2080
|
-
var
|
|
2984
|
+
var isRecord2 = (value) => typeof value === "object" && value !== null;
|
|
2081
2985
|
var MCPToolProvider = class {
|
|
2082
2986
|
configs;
|
|
2083
2987
|
namePrefix;
|
|
@@ -2168,7 +3072,7 @@ var MCPToolProvider = class {
|
|
|
2168
3072
|
},
|
|
2169
3073
|
execute: async (input, context) => {
|
|
2170
3074
|
const startedAt = Date.now();
|
|
2171
|
-
const args =
|
|
3075
|
+
const args = isRecord2(input) ? input : input === void 0 ? {} : { value: input };
|
|
2172
3076
|
try {
|
|
2173
3077
|
const result = await client.callTool(mcpTool.name, args);
|
|
2174
3078
|
return ToolResultFactory.success(toolName, result, {
|
|
@@ -2626,6 +3530,6 @@ var ToolRunner = class {
|
|
|
2626
3530
|
}
|
|
2627
3531
|
};
|
|
2628
3532
|
|
|
2629
|
-
export { Agent, AgentContext, AgentDecisionSchema, BaseAgent, ConsoleLogger, DEFAULT_MODEL, DEFAULT_RETRY_CONFIG, ExecutionCycleSchema, HookEvents, HookManager, InMemoryStore, LogLevel, MCPClient, MCPServerConfigSchema, MCPToolProvider, MCPconfig, MemoryEntryMetadataSchema, MemoryEntrySchema, MemoryUpdateSchema, OpperClient, Result, SchemaValidationError, SilentLogger, ThoughtSchema, ToolCallRecordSchema, ToolCallSchema, ToolExecutionSummarySchema, ToolMetadataSchema, ToolResultFactory, ToolResultFailureSchema, ToolResultSchema, ToolResultSuccessSchema, ToolRunner, UsageSchema, coerceToolDefinition, createFunctionTool, createHookManager, createInMemoryStore, createMCPServerConfig, createOpperClient, createToolCallRecord, err, extractTools, getDefaultLogger, getSchemaDefault, isSchemaValid, isToolProvider, mcp, mergeSchemaDefaults, normalizeToolEntries, ok, schemaToJson, setDefaultLogger, tool, validateSchema, validateToolInput };
|
|
3533
|
+
export { Agent, AgentContext, AgentDecisionSchema, AgentEventEmitter, AgentEvents, BaseAgent, ConsoleLogger, DEFAULT_MODEL, DEFAULT_RETRY_CONFIG, ExecutionCycleSchema, HookEvents, HookManager, InMemoryStore, LogLevel, MCPClient, MCPServerConfigSchema, MCPToolProvider, MCPconfig, MemoryEntryMetadataSchema, MemoryEntrySchema, MemoryUpdateSchema, OpperClient, Result, STREAM_ROOT_PATH, SchemaValidationError, SilentLogger, StreamAssembler, ThoughtSchema, ToolCallRecordSchema, ToolCallSchema, ToolExecutionSummarySchema, ToolMetadataSchema, ToolResultFactory, ToolResultFailureSchema, ToolResultSchema, ToolResultSuccessSchema, ToolRunner, UsageSchema, coerceToolDefinition, createFunctionTool, createHookManager, createInMemoryStore, createMCPServerConfig, createOpperClient, createStreamAssembler, createToolCallRecord, err, extractTools, generateAgentFlowDiagram, getDefaultLogger, getSchemaDefault, isSchemaValid, isToolProvider, mcp, mergeSchemaDefaults, normalizeToolEntries, ok, schemaToJson, setDefaultLogger, tool, validateSchema, validateToolInput };
|
|
2630
3534
|
//# sourceMappingURL=index.js.map
|
|
2631
3535
|
//# sourceMappingURL=index.js.map
|