@opperai/agents 0.1.3 → 0.3.0-beta
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 +901 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +330 -4
- package/dist/index.d.ts +330 -4
- package/dist/index.js +896 -28
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
package/dist/index.cjs
CHANGED
|
@@ -394,7 +394,11 @@ var HookEvents = {
|
|
|
394
394
|
ToolError: "tool:error",
|
|
395
395
|
MemoryRead: "memory:read",
|
|
396
396
|
MemoryWrite: "memory:write",
|
|
397
|
-
MemoryError: "memory:error"
|
|
397
|
+
MemoryError: "memory:error",
|
|
398
|
+
StreamStart: "stream:start",
|
|
399
|
+
StreamChunk: "stream:chunk",
|
|
400
|
+
StreamEnd: "stream:end",
|
|
401
|
+
StreamError: "stream:error"
|
|
398
402
|
};
|
|
399
403
|
var HookManager = class {
|
|
400
404
|
registry = /* @__PURE__ */ new Map();
|
|
@@ -466,6 +470,82 @@ var HookManager = class {
|
|
|
466
470
|
};
|
|
467
471
|
var createHookManager = (logger) => new HookManager(logger);
|
|
468
472
|
|
|
473
|
+
// src/base/events.ts
|
|
474
|
+
var AgentEvents = {
|
|
475
|
+
StreamStart: HookEvents.StreamStart,
|
|
476
|
+
StreamChunk: HookEvents.StreamChunk,
|
|
477
|
+
StreamEnd: HookEvents.StreamEnd,
|
|
478
|
+
StreamError: HookEvents.StreamError
|
|
479
|
+
};
|
|
480
|
+
var AgentEventEmitter = class {
|
|
481
|
+
registry = /* @__PURE__ */ new Map();
|
|
482
|
+
logger;
|
|
483
|
+
constructor(logger) {
|
|
484
|
+
this.logger = logger ?? getDefaultLogger();
|
|
485
|
+
}
|
|
486
|
+
on(event, listener) {
|
|
487
|
+
const listeners = this.registry.get(event) ?? /* @__PURE__ */ new Set();
|
|
488
|
+
listeners.add(listener);
|
|
489
|
+
this.registry.set(event, listeners);
|
|
490
|
+
return () => this.off(event, listener);
|
|
491
|
+
}
|
|
492
|
+
once(event, listener) {
|
|
493
|
+
const wrapper = (payload) => {
|
|
494
|
+
try {
|
|
495
|
+
listener(payload);
|
|
496
|
+
} finally {
|
|
497
|
+
this.off(event, wrapper);
|
|
498
|
+
}
|
|
499
|
+
};
|
|
500
|
+
return this.on(event, wrapper);
|
|
501
|
+
}
|
|
502
|
+
off(event, listener) {
|
|
503
|
+
const listeners = this.registry.get(event);
|
|
504
|
+
if (!listeners) {
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
listeners.delete(listener);
|
|
508
|
+
if (listeners.size === 0) {
|
|
509
|
+
this.registry.delete(event);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
emit(event, payload) {
|
|
513
|
+
const listeners = Array.from(
|
|
514
|
+
this.registry.get(event) ?? /* @__PURE__ */ new Set()
|
|
515
|
+
);
|
|
516
|
+
if (listeners.length === 0) {
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
for (const listener of listeners) {
|
|
520
|
+
try {
|
|
521
|
+
listener(payload);
|
|
522
|
+
} catch (error) {
|
|
523
|
+
this.logger.warn(`Agent event listener failed for "${event}"`, {
|
|
524
|
+
event,
|
|
525
|
+
error: error instanceof Error ? error.message : String(error)
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
removeAllListeners(event) {
|
|
531
|
+
if (event) {
|
|
532
|
+
this.registry.delete(event);
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
this.registry.clear();
|
|
536
|
+
}
|
|
537
|
+
listenerCount(event) {
|
|
538
|
+
if (event) {
|
|
539
|
+
return this.registry.get(event)?.size ?? 0;
|
|
540
|
+
}
|
|
541
|
+
let total = 0;
|
|
542
|
+
for (const listeners of this.registry.values()) {
|
|
543
|
+
total += listeners.size;
|
|
544
|
+
}
|
|
545
|
+
return total;
|
|
546
|
+
}
|
|
547
|
+
};
|
|
548
|
+
|
|
469
549
|
// src/base/agent.ts
|
|
470
550
|
init_tool();
|
|
471
551
|
function sanitizeId(name) {
|
|
@@ -825,6 +905,10 @@ var BaseAgent = class {
|
|
|
825
905
|
* Whether memory is enabled
|
|
826
906
|
*/
|
|
827
907
|
enableMemory;
|
|
908
|
+
/**
|
|
909
|
+
* Whether streaming is enabled
|
|
910
|
+
*/
|
|
911
|
+
enableStreaming;
|
|
828
912
|
/**
|
|
829
913
|
* Memory instance for persistent storage (null if disabled or initialization failed)
|
|
830
914
|
*/
|
|
@@ -837,6 +921,10 @@ var BaseAgent = class {
|
|
|
837
921
|
* Hook manager for lifecycle events
|
|
838
922
|
*/
|
|
839
923
|
hooks;
|
|
924
|
+
/**
|
|
925
|
+
* Event dispatcher for runtime events (notably streaming)
|
|
926
|
+
*/
|
|
927
|
+
events;
|
|
840
928
|
/**
|
|
841
929
|
* Registry of available tools
|
|
842
930
|
*/
|
|
@@ -857,6 +945,28 @@ var BaseAgent = class {
|
|
|
857
945
|
* Opper client configuration
|
|
858
946
|
*/
|
|
859
947
|
opperConfig;
|
|
948
|
+
/**
|
|
949
|
+
* Creates a new BaseAgent instance
|
|
950
|
+
*
|
|
951
|
+
* @param config - Agent configuration object
|
|
952
|
+
* @param config.name - Unique name identifying this agent (required)
|
|
953
|
+
* @param config.description - Human-readable description of the agent's purpose
|
|
954
|
+
* @param config.instructions - System instructions guiding agent behavior
|
|
955
|
+
* @param config.tools - Array of tools or tool providers available to the agent
|
|
956
|
+
* @param config.maxIterations - Maximum iterations before terminating the agent loop (default: 25)
|
|
957
|
+
* @param config.model - Model identifier(s). Single model or array for fallback (default: "gcp/gemini-flash-latest")
|
|
958
|
+
* @param config.inputSchema - Zod schema for input validation
|
|
959
|
+
* @param config.outputSchema - Zod schema for output validation
|
|
960
|
+
* @param config.enableStreaming - Enable Opper streaming APIs for LLM calls (default: false)
|
|
961
|
+
* @param config.enableMemory - Enable memory subsystem (default: false)
|
|
962
|
+
* @param config.memory - Custom memory implementation (defaults to InMemoryStore if enableMemory is true)
|
|
963
|
+
* @param config.metadata - Additional metadata for the agent
|
|
964
|
+
* @param config.opperConfig - Opper API configuration containing apiKey and baseUrl
|
|
965
|
+
* @param config.onStreamStart - Handler invoked when a streaming call starts
|
|
966
|
+
* @param config.onStreamChunk - Handler invoked for each streaming chunk
|
|
967
|
+
* @param config.onStreamEnd - Handler invoked when a streaming call ends
|
|
968
|
+
* @param config.onStreamError - Handler invoked when streaming encounters an error
|
|
969
|
+
*/
|
|
860
970
|
constructor(config) {
|
|
861
971
|
this.name = config.name;
|
|
862
972
|
this.description = config.description;
|
|
@@ -866,12 +976,26 @@ var BaseAgent = class {
|
|
|
866
976
|
this.inputSchema = config.inputSchema;
|
|
867
977
|
this.outputSchema = config.outputSchema;
|
|
868
978
|
this.enableMemory = config.enableMemory ?? false;
|
|
979
|
+
this.enableStreaming = config.enableStreaming ?? false;
|
|
869
980
|
this.metadata = { ...config.metadata ?? {} };
|
|
870
981
|
this.hooks = new HookManager();
|
|
982
|
+
this.events = new AgentEventEmitter();
|
|
871
983
|
this.tools = /* @__PURE__ */ new Map();
|
|
872
984
|
this.baseTools = /* @__PURE__ */ new Map();
|
|
873
985
|
this.toolProviders = /* @__PURE__ */ new Set();
|
|
874
986
|
this.providerToolRegistry = /* @__PURE__ */ new Map();
|
|
987
|
+
if (config.onStreamStart) {
|
|
988
|
+
this.on(HookEvents.StreamStart, config.onStreamStart);
|
|
989
|
+
}
|
|
990
|
+
if (config.onStreamChunk) {
|
|
991
|
+
this.on(HookEvents.StreamChunk, config.onStreamChunk);
|
|
992
|
+
}
|
|
993
|
+
if (config.onStreamEnd) {
|
|
994
|
+
this.on(HookEvents.StreamEnd, config.onStreamEnd);
|
|
995
|
+
}
|
|
996
|
+
if (config.onStreamError) {
|
|
997
|
+
this.on(HookEvents.StreamError, config.onStreamError);
|
|
998
|
+
}
|
|
875
999
|
this.opperConfig = {
|
|
876
1000
|
apiKey: config.opperConfig?.apiKey ?? process.env["OPPER_API_KEY"],
|
|
877
1001
|
baseUrl: config.opperConfig?.baseUrl,
|
|
@@ -1053,6 +1177,35 @@ var BaseAgent = class {
|
|
|
1053
1177
|
registerHook(event, handler) {
|
|
1054
1178
|
return this.hooks.on(event, handler);
|
|
1055
1179
|
}
|
|
1180
|
+
/**
|
|
1181
|
+
* Register an event listener.
|
|
1182
|
+
*
|
|
1183
|
+
* @param event - Event name
|
|
1184
|
+
* @param listener - Listener callback
|
|
1185
|
+
* @returns Cleanup function to unregister the listener
|
|
1186
|
+
*/
|
|
1187
|
+
on(event, listener) {
|
|
1188
|
+
return this.events.on(event, listener);
|
|
1189
|
+
}
|
|
1190
|
+
/**
|
|
1191
|
+
* Register a one-time event listener that removes itself after the first call.
|
|
1192
|
+
*
|
|
1193
|
+
* @param event - Event name
|
|
1194
|
+
* @param listener - Listener callback
|
|
1195
|
+
* @returns Cleanup function (no-op once listener fires)
|
|
1196
|
+
*/
|
|
1197
|
+
once(event, listener) {
|
|
1198
|
+
return this.events.once(event, listener);
|
|
1199
|
+
}
|
|
1200
|
+
/**
|
|
1201
|
+
* Remove a previously registered event listener.
|
|
1202
|
+
*
|
|
1203
|
+
* @param event - Event name
|
|
1204
|
+
* @param listener - Listener callback to remove
|
|
1205
|
+
*/
|
|
1206
|
+
off(event, listener) {
|
|
1207
|
+
this.events.off(event, listener);
|
|
1208
|
+
}
|
|
1056
1209
|
/**
|
|
1057
1210
|
* Trigger a hook event with a payload.
|
|
1058
1211
|
* Swallows errors to prevent hook failures from breaking agent execution.
|
|
@@ -1067,6 +1220,15 @@ var BaseAgent = class {
|
|
|
1067
1220
|
console.warn(`Hook error for event ${event}:`, error);
|
|
1068
1221
|
}
|
|
1069
1222
|
}
|
|
1223
|
+
/**
|
|
1224
|
+
* Emit a runtime event to listeners.
|
|
1225
|
+
*
|
|
1226
|
+
* @param event - Event name
|
|
1227
|
+
* @param payload - Event payload
|
|
1228
|
+
*/
|
|
1229
|
+
emitAgentEvent(event, payload) {
|
|
1230
|
+
this.events.emit(event, payload);
|
|
1231
|
+
}
|
|
1070
1232
|
/**
|
|
1071
1233
|
* Execute a tool with proper context, hooks, and error handling.
|
|
1072
1234
|
*
|
|
@@ -1305,6 +1467,10 @@ var AgentDecisionSchema = zod.z.object({
|
|
|
1305
1467
|
* Agent's internal reasoning
|
|
1306
1468
|
*/
|
|
1307
1469
|
reasoning: zod.z.string(),
|
|
1470
|
+
/**
|
|
1471
|
+
* Status message for the user (e.g., "Searching for information...", "Processing results...")
|
|
1472
|
+
*/
|
|
1473
|
+
userMessage: zod.z.string().default("Working on it..."),
|
|
1308
1474
|
/**
|
|
1309
1475
|
* Tool calls to execute (if any)
|
|
1310
1476
|
* Empty array signals task completion
|
|
@@ -1317,8 +1483,34 @@ var AgentDecisionSchema = zod.z.object({
|
|
|
1317
1483
|
/**
|
|
1318
1484
|
* Memory entries to write/update (key -> payload)
|
|
1319
1485
|
*/
|
|
1320
|
-
memoryUpdates: zod.z.record(MemoryUpdateSchema).default({})
|
|
1486
|
+
memoryUpdates: zod.z.record(MemoryUpdateSchema).default({}),
|
|
1487
|
+
/**
|
|
1488
|
+
* Whether the task is complete and finalResult is available
|
|
1489
|
+
* (single LLM call pattern)
|
|
1490
|
+
*/
|
|
1491
|
+
isComplete: zod.z.boolean().default(false),
|
|
1492
|
+
/**
|
|
1493
|
+
* The final result when isComplete=true
|
|
1494
|
+
* Should match outputSchema if specified
|
|
1495
|
+
*/
|
|
1496
|
+
finalResult: zod.z.unknown().optional()
|
|
1321
1497
|
});
|
|
1498
|
+
function createAgentDecisionWithOutputSchema(outputSchema) {
|
|
1499
|
+
if (!outputSchema) {
|
|
1500
|
+
return AgentDecisionSchema;
|
|
1501
|
+
}
|
|
1502
|
+
const finalResultSchema = outputSchema.optional();
|
|
1503
|
+
const dynamicSchema = zod.z.object({
|
|
1504
|
+
reasoning: zod.z.string(),
|
|
1505
|
+
userMessage: zod.z.string().default("Working on it..."),
|
|
1506
|
+
toolCalls: zod.z.array(ToolCallSchema).default([]),
|
|
1507
|
+
memoryReads: zod.z.array(zod.z.string()).default([]),
|
|
1508
|
+
memoryUpdates: zod.z.record(MemoryUpdateSchema).default({}),
|
|
1509
|
+
isComplete: zod.z.boolean().default(false),
|
|
1510
|
+
finalResult: finalResultSchema
|
|
1511
|
+
});
|
|
1512
|
+
return dynamicSchema;
|
|
1513
|
+
}
|
|
1322
1514
|
var ToolExecutionSummarySchema = zod.z.object({
|
|
1323
1515
|
/**
|
|
1324
1516
|
* Tool name
|
|
@@ -1340,7 +1532,7 @@ var ToolExecutionSummarySchema = zod.z.object({
|
|
|
1340
1532
|
|
|
1341
1533
|
// package.json
|
|
1342
1534
|
var package_default = {
|
|
1343
|
-
version: "0.
|
|
1535
|
+
version: "0.3.0-beta"};
|
|
1344
1536
|
|
|
1345
1537
|
// src/utils/version.ts
|
|
1346
1538
|
var SDK_NAME = "@opperai/agents";
|
|
@@ -1403,7 +1595,7 @@ var OpperClient = class {
|
|
|
1403
1595
|
return this.withRetry(async () => {
|
|
1404
1596
|
const inputSchema = this.toJsonSchema(options.inputSchema);
|
|
1405
1597
|
const outputSchema = this.toJsonSchema(options.outputSchema);
|
|
1406
|
-
const
|
|
1598
|
+
const callPayload = {
|
|
1407
1599
|
name: options.name,
|
|
1408
1600
|
instructions: options.instructions,
|
|
1409
1601
|
input: options.input,
|
|
@@ -1411,7 +1603,8 @@ var OpperClient = class {
|
|
|
1411
1603
|
...outputSchema && { outputSchema },
|
|
1412
1604
|
...options.model && { model: options.model },
|
|
1413
1605
|
...options.parentSpanId && { parentSpanId: options.parentSpanId }
|
|
1414
|
-
}
|
|
1606
|
+
};
|
|
1607
|
+
const response = options.signal ? await this.client.call(callPayload, { signal: options.signal }) : await this.client.call(callPayload);
|
|
1415
1608
|
const usagePayload = extractUsage(response);
|
|
1416
1609
|
const costPayload = extractCost(response);
|
|
1417
1610
|
const usage = {
|
|
@@ -1429,6 +1622,36 @@ var OpperClient = class {
|
|
|
1429
1622
|
return result;
|
|
1430
1623
|
}, options.name);
|
|
1431
1624
|
}
|
|
1625
|
+
/**
|
|
1626
|
+
* Stream a call to Opper with retry logic
|
|
1627
|
+
*/
|
|
1628
|
+
async stream(options) {
|
|
1629
|
+
return this.withRetry(async () => {
|
|
1630
|
+
const inputSchema = this.toJsonSchema(options.inputSchema);
|
|
1631
|
+
const outputSchema = this.toJsonSchema(options.outputSchema);
|
|
1632
|
+
const streamPayload = {
|
|
1633
|
+
name: options.name,
|
|
1634
|
+
instructions: options.instructions,
|
|
1635
|
+
input: options.input,
|
|
1636
|
+
...inputSchema && { inputSchema },
|
|
1637
|
+
...outputSchema && { outputSchema },
|
|
1638
|
+
...options.model && { model: options.model },
|
|
1639
|
+
...options.parentSpanId && { parentSpanId: options.parentSpanId }
|
|
1640
|
+
};
|
|
1641
|
+
const response = options.signal ? await this.client.stream(streamPayload, { signal: options.signal }) : await this.client.stream(streamPayload);
|
|
1642
|
+
const iterable = {
|
|
1643
|
+
async *[Symbol.asyncIterator]() {
|
|
1644
|
+
for await (const event of response.result) {
|
|
1645
|
+
yield event;
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
};
|
|
1649
|
+
return {
|
|
1650
|
+
headers: response.headers,
|
|
1651
|
+
result: iterable
|
|
1652
|
+
};
|
|
1653
|
+
}, `stream:${options.name}`);
|
|
1654
|
+
}
|
|
1432
1655
|
/**
|
|
1433
1656
|
* Create a span for tracing
|
|
1434
1657
|
*/
|
|
@@ -1437,7 +1660,8 @@ var OpperClient = class {
|
|
|
1437
1660
|
const span = await this.client.spans.create({
|
|
1438
1661
|
name: options.name,
|
|
1439
1662
|
...options.input !== void 0 && { input: options.input },
|
|
1440
|
-
...options.parentSpanId && { parentId: options.parentSpanId }
|
|
1663
|
+
...options.parentSpanId && { parentId: options.parentSpanId },
|
|
1664
|
+
...options.type && { type: options.type }
|
|
1441
1665
|
});
|
|
1442
1666
|
return {
|
|
1443
1667
|
id: span.id,
|
|
@@ -1454,7 +1678,11 @@ var OpperClient = class {
|
|
|
1454
1678
|
const serializedOutput = output !== void 0 && output !== null ? typeof output === "object" ? JSON.stringify(output) : String(output) : void 0;
|
|
1455
1679
|
await this.client.spans.update(spanId, {
|
|
1456
1680
|
...serializedOutput !== void 0 && { output: serializedOutput },
|
|
1457
|
-
...options?.error && { error: options.error }
|
|
1681
|
+
...options?.error && { error: options.error },
|
|
1682
|
+
...options?.startTime && { startTime: options.startTime },
|
|
1683
|
+
...options?.endTime && { endTime: options.endTime },
|
|
1684
|
+
...options?.meta && { meta: options.meta },
|
|
1685
|
+
...options?.name && { name: options.name }
|
|
1458
1686
|
});
|
|
1459
1687
|
}, `update-span:${spanId}`);
|
|
1460
1688
|
}
|
|
@@ -1597,6 +1825,193 @@ var mergeSchemaDefaults = (schema, value) => {
|
|
|
1597
1825
|
return validateSchema(schema, value);
|
|
1598
1826
|
};
|
|
1599
1827
|
|
|
1828
|
+
// src/utils/streaming.ts
|
|
1829
|
+
var STREAM_ROOT_PATH = "_root";
|
|
1830
|
+
var NUMERIC_TOKEN_PATTERN = /^\d+$/;
|
|
1831
|
+
var BRACKET_TOKEN_PATTERN = /\[(\d+)\]/g;
|
|
1832
|
+
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1833
|
+
var coercePrimitive = (value) => {
|
|
1834
|
+
const trimmed = value.trim();
|
|
1835
|
+
if (trimmed.length === 0) {
|
|
1836
|
+
return value;
|
|
1837
|
+
}
|
|
1838
|
+
const lower = trimmed.toLowerCase();
|
|
1839
|
+
if (lower === "true") {
|
|
1840
|
+
return true;
|
|
1841
|
+
}
|
|
1842
|
+
if (lower === "false") {
|
|
1843
|
+
return false;
|
|
1844
|
+
}
|
|
1845
|
+
if (lower === "null") {
|
|
1846
|
+
return null;
|
|
1847
|
+
}
|
|
1848
|
+
if (/^-?\d+$/.test(trimmed)) {
|
|
1849
|
+
const intValue = Number.parseInt(trimmed, 10);
|
|
1850
|
+
return Number.isNaN(intValue) ? value : intValue;
|
|
1851
|
+
}
|
|
1852
|
+
if (/^-?\d+\.\d+$/.test(trimmed)) {
|
|
1853
|
+
const floatValue = Number.parseFloat(trimmed);
|
|
1854
|
+
return Number.isNaN(floatValue) ? value : floatValue;
|
|
1855
|
+
}
|
|
1856
|
+
return value;
|
|
1857
|
+
};
|
|
1858
|
+
var toDisplayString = (value) => {
|
|
1859
|
+
if (value === null || value === void 0) {
|
|
1860
|
+
return "";
|
|
1861
|
+
}
|
|
1862
|
+
if (typeof value === "string") {
|
|
1863
|
+
return value;
|
|
1864
|
+
}
|
|
1865
|
+
return String(value);
|
|
1866
|
+
};
|
|
1867
|
+
var parsePathSegments = (path) => {
|
|
1868
|
+
return path.replace(BRACKET_TOKEN_PATTERN, (_, index) => `.${index}`).split(".").map((segment) => segment.trim()).filter((segment) => segment.length > 0);
|
|
1869
|
+
};
|
|
1870
|
+
var setNestedValue = (target, path, value) => {
|
|
1871
|
+
const segments = parsePathSegments(path);
|
|
1872
|
+
if (segments.length === 0) {
|
|
1873
|
+
return;
|
|
1874
|
+
}
|
|
1875
|
+
let current = target;
|
|
1876
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
1877
|
+
const segment = segments[i];
|
|
1878
|
+
if (segment === void 0) {
|
|
1879
|
+
return;
|
|
1880
|
+
}
|
|
1881
|
+
const existing = current[segment];
|
|
1882
|
+
if (!isRecord(existing)) {
|
|
1883
|
+
const nextLevel = {};
|
|
1884
|
+
current[segment] = nextLevel;
|
|
1885
|
+
current = nextLevel;
|
|
1886
|
+
continue;
|
|
1887
|
+
}
|
|
1888
|
+
current = existing;
|
|
1889
|
+
}
|
|
1890
|
+
const lastSegment = segments[segments.length - 1];
|
|
1891
|
+
if (lastSegment === void 0) {
|
|
1892
|
+
return;
|
|
1893
|
+
}
|
|
1894
|
+
current[lastSegment] = value;
|
|
1895
|
+
};
|
|
1896
|
+
var normalizeIndexed = (value) => {
|
|
1897
|
+
if (Array.isArray(value)) {
|
|
1898
|
+
return value.map((item) => normalizeIndexed(item));
|
|
1899
|
+
}
|
|
1900
|
+
if (!isRecord(value)) {
|
|
1901
|
+
return value;
|
|
1902
|
+
}
|
|
1903
|
+
const normalizedEntries = {};
|
|
1904
|
+
for (const [key, entryValue] of Object.entries(value)) {
|
|
1905
|
+
normalizedEntries[key] = normalizeIndexed(entryValue);
|
|
1906
|
+
}
|
|
1907
|
+
const keys = Object.keys(normalizedEntries);
|
|
1908
|
+
if (keys.length > 0 && keys.every((key) => NUMERIC_TOKEN_PATTERN.test(key))) {
|
|
1909
|
+
const parsedIndices = keys.map((key) => Number.parseInt(key, 10)).filter((index) => Number.isFinite(index) && index >= 0);
|
|
1910
|
+
if (parsedIndices.length === 0) {
|
|
1911
|
+
return normalizedEntries;
|
|
1912
|
+
}
|
|
1913
|
+
const maxIndex = Math.max(...parsedIndices);
|
|
1914
|
+
const result = new Array(maxIndex + 1).fill(void 0);
|
|
1915
|
+
for (const [key, entryValue] of Object.entries(normalizedEntries)) {
|
|
1916
|
+
const index = Number.parseInt(key, 10);
|
|
1917
|
+
if (!Number.isFinite(index) || index < 0) {
|
|
1918
|
+
continue;
|
|
1919
|
+
}
|
|
1920
|
+
result[index] = entryValue;
|
|
1921
|
+
}
|
|
1922
|
+
return result.map(
|
|
1923
|
+
(item) => item === void 0 ? null : item
|
|
1924
|
+
);
|
|
1925
|
+
}
|
|
1926
|
+
return normalizedEntries;
|
|
1927
|
+
};
|
|
1928
|
+
var resolveFieldValue = (path, displayBuffers, valueBuffers) => {
|
|
1929
|
+
const values = valueBuffers.get(path) ?? [];
|
|
1930
|
+
if (values.length === 0) {
|
|
1931
|
+
return displayBuffers.get(path) ?? "";
|
|
1932
|
+
}
|
|
1933
|
+
const last = values[values.length - 1];
|
|
1934
|
+
if (typeof last === "number" || typeof last === "boolean") {
|
|
1935
|
+
return last;
|
|
1936
|
+
}
|
|
1937
|
+
if (last === null) {
|
|
1938
|
+
return null;
|
|
1939
|
+
}
|
|
1940
|
+
const joined = displayBuffers.get(path) ?? values.map((value) => toDisplayString(value)).join("");
|
|
1941
|
+
return coercePrimitive(joined);
|
|
1942
|
+
};
|
|
1943
|
+
var StreamAssembler = class {
|
|
1944
|
+
displayBuffers = /* @__PURE__ */ new Map();
|
|
1945
|
+
valueBuffers = /* @__PURE__ */ new Map();
|
|
1946
|
+
schema;
|
|
1947
|
+
constructor(options = {}) {
|
|
1948
|
+
this.schema = options.schema;
|
|
1949
|
+
}
|
|
1950
|
+
feed(chunk) {
|
|
1951
|
+
const { delta } = chunk;
|
|
1952
|
+
if (delta === null || delta === void 0) {
|
|
1953
|
+
return null;
|
|
1954
|
+
}
|
|
1955
|
+
const path = chunk.jsonPath === null || chunk.jsonPath === void 0 ? STREAM_ROOT_PATH : chunk.jsonPath;
|
|
1956
|
+
const existingDisplay = this.displayBuffers.get(path) ?? "";
|
|
1957
|
+
const nextDisplay = `${existingDisplay}${toDisplayString(delta)}`;
|
|
1958
|
+
this.displayBuffers.set(path, nextDisplay);
|
|
1959
|
+
const existingValues = this.valueBuffers.get(path) ?? [];
|
|
1960
|
+
existingValues.push(delta);
|
|
1961
|
+
this.valueBuffers.set(path, existingValues);
|
|
1962
|
+
return {
|
|
1963
|
+
path,
|
|
1964
|
+
accumulated: nextDisplay,
|
|
1965
|
+
snapshot: this.snapshot()
|
|
1966
|
+
};
|
|
1967
|
+
}
|
|
1968
|
+
snapshot() {
|
|
1969
|
+
return Object.fromEntries(this.displayBuffers.entries());
|
|
1970
|
+
}
|
|
1971
|
+
hasStructuredFields() {
|
|
1972
|
+
const keys = Array.from(this.displayBuffers.keys());
|
|
1973
|
+
return keys.some((key) => key !== STREAM_ROOT_PATH);
|
|
1974
|
+
}
|
|
1975
|
+
finalize() {
|
|
1976
|
+
if (this.displayBuffers.size === 0) {
|
|
1977
|
+
return { type: "empty" };
|
|
1978
|
+
}
|
|
1979
|
+
if (!this.hasStructuredFields()) {
|
|
1980
|
+
const root = this.displayBuffers.get(STREAM_ROOT_PATH) ?? "";
|
|
1981
|
+
return { type: "root", rootText: root };
|
|
1982
|
+
}
|
|
1983
|
+
const structured = this.reconstructStructured();
|
|
1984
|
+
let coerced = structured;
|
|
1985
|
+
if (this.schema !== void 0) {
|
|
1986
|
+
try {
|
|
1987
|
+
coerced = this.schema.parse(structured);
|
|
1988
|
+
} catch {
|
|
1989
|
+
coerced = structured;
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
return { type: "structured", structured: coerced };
|
|
1993
|
+
}
|
|
1994
|
+
getFieldBuffers() {
|
|
1995
|
+
return new Map(this.displayBuffers);
|
|
1996
|
+
}
|
|
1997
|
+
reconstructStructured() {
|
|
1998
|
+
const result = {};
|
|
1999
|
+
for (const path of this.valueBuffers.keys()) {
|
|
2000
|
+
if (path === STREAM_ROOT_PATH) {
|
|
2001
|
+
continue;
|
|
2002
|
+
}
|
|
2003
|
+
const value = resolveFieldValue(
|
|
2004
|
+
path,
|
|
2005
|
+
this.displayBuffers,
|
|
2006
|
+
this.valueBuffers
|
|
2007
|
+
);
|
|
2008
|
+
setNestedValue(result, path, value);
|
|
2009
|
+
}
|
|
2010
|
+
return normalizeIndexed(result);
|
|
2011
|
+
}
|
|
2012
|
+
};
|
|
2013
|
+
var createStreamAssembler = (options) => new StreamAssembler(options);
|
|
2014
|
+
|
|
1600
2015
|
// src/core/agent.ts
|
|
1601
2016
|
var isToolSuccessResult = (value) => {
|
|
1602
2017
|
if (typeof value !== "object" || value === null) {
|
|
@@ -1609,6 +2024,31 @@ var Agent = class extends BaseAgent {
|
|
|
1609
2024
|
opperClient;
|
|
1610
2025
|
logger;
|
|
1611
2026
|
verbose;
|
|
2027
|
+
/**
|
|
2028
|
+
* Creates a new Agent instance
|
|
2029
|
+
*
|
|
2030
|
+
* @param config - Agent configuration object
|
|
2031
|
+
* @param config.name - Unique name identifying this agent (required)
|
|
2032
|
+
* @param config.description - Human-readable description of the agent's purpose
|
|
2033
|
+
* @param config.instructions - System instructions guiding agent behavior
|
|
2034
|
+
* @param config.tools - Array of tools or tool providers available to the agent
|
|
2035
|
+
* @param config.maxIterations - Maximum iterations before terminating (default: 25)
|
|
2036
|
+
* @param config.model - Model identifier(s) as string or array for fallback (default: "gcp/gemini-flash-latest")
|
|
2037
|
+
* @param config.inputSchema - Zod schema for input validation
|
|
2038
|
+
* @param config.outputSchema - Zod schema for output validation
|
|
2039
|
+
* @param config.enableStreaming - Enable streaming for LLM calls (default: false)
|
|
2040
|
+
* @param config.enableMemory - Enable memory subsystem (default: false)
|
|
2041
|
+
* @param config.memory - Custom memory implementation (defaults to InMemoryStore if enableMemory is true)
|
|
2042
|
+
* @param config.metadata - Additional metadata for the agent
|
|
2043
|
+
* @param config.opperConfig - Opper API configuration (apiKey, baseUrl)
|
|
2044
|
+
* @param config.opperClient - Custom Opper client instance (for testing or custom configuration)
|
|
2045
|
+
* @param config.logger - Logger instance for debugging
|
|
2046
|
+
* @param config.verbose - Enable verbose logging (default: false)
|
|
2047
|
+
* @param config.onStreamStart - Handler invoked when streaming starts
|
|
2048
|
+
* @param config.onStreamChunk - Handler invoked for each streaming chunk
|
|
2049
|
+
* @param config.onStreamEnd - Handler invoked when streaming ends
|
|
2050
|
+
* @param config.onStreamError - Handler invoked on streaming errors
|
|
2051
|
+
*/
|
|
1612
2052
|
constructor(config) {
|
|
1613
2053
|
super(config);
|
|
1614
2054
|
this.logger = config.logger ?? getDefaultLogger();
|
|
@@ -1644,6 +2084,7 @@ var Agent = class extends BaseAgent {
|
|
|
1644
2084
|
maxIterations: this.maxIterations,
|
|
1645
2085
|
tools: Array.from(this.tools.keys())
|
|
1646
2086
|
});
|
|
2087
|
+
const executionStartTime = /* @__PURE__ */ new Date();
|
|
1647
2088
|
const parentSpan = await this.opperClient.createSpan({
|
|
1648
2089
|
name: `${this.name}_execution`,
|
|
1649
2090
|
input: this.serializeInput(input),
|
|
@@ -1663,6 +2104,47 @@ var Agent = class extends BaseAgent {
|
|
|
1663
2104
|
input,
|
|
1664
2105
|
context
|
|
1665
2106
|
);
|
|
2107
|
+
if (decision.isComplete && decision.finalResult !== void 0) {
|
|
2108
|
+
this.log("Task completed with final result in single call", {
|
|
2109
|
+
iteration: currentIteration
|
|
2110
|
+
});
|
|
2111
|
+
let finalResult;
|
|
2112
|
+
if (this.outputSchema) {
|
|
2113
|
+
const parseResult = this.outputSchema.safeParse(
|
|
2114
|
+
decision.finalResult
|
|
2115
|
+
);
|
|
2116
|
+
if (parseResult.success) {
|
|
2117
|
+
finalResult = parseResult.data;
|
|
2118
|
+
} else {
|
|
2119
|
+
this.logger.warn(
|
|
2120
|
+
"Final result validation against output schema failed, falling back to generate_final_result",
|
|
2121
|
+
{ error: parseResult.error.message }
|
|
2122
|
+
);
|
|
2123
|
+
break;
|
|
2124
|
+
}
|
|
2125
|
+
} else {
|
|
2126
|
+
const rawResult = decision.finalResult;
|
|
2127
|
+
if (typeof rawResult === "string") {
|
|
2128
|
+
finalResult = rawResult;
|
|
2129
|
+
} else if (rawResult === null || rawResult === void 0) {
|
|
2130
|
+
finalResult = "";
|
|
2131
|
+
} else if (typeof rawResult === "object") {
|
|
2132
|
+
finalResult = JSON.stringify(rawResult);
|
|
2133
|
+
} else {
|
|
2134
|
+
finalResult = String(rawResult);
|
|
2135
|
+
}
|
|
2136
|
+
}
|
|
2137
|
+
const executionEndTime2 = /* @__PURE__ */ new Date();
|
|
2138
|
+
await this.opperClient.updateSpan(parentSpan.id, finalResult, {
|
|
2139
|
+
startTime: executionStartTime,
|
|
2140
|
+
endTime: executionEndTime2,
|
|
2141
|
+
meta: {
|
|
2142
|
+
durationMs: executionEndTime2.getTime() - executionStartTime.getTime()
|
|
2143
|
+
}
|
|
2144
|
+
});
|
|
2145
|
+
await this.triggerHook(HookEvents.LoopEnd, { context });
|
|
2146
|
+
return finalResult;
|
|
2147
|
+
}
|
|
1666
2148
|
const memoryResults = await this.handleMemoryActions(
|
|
1667
2149
|
decision,
|
|
1668
2150
|
context,
|
|
@@ -1672,7 +2154,7 @@ var Agent = class extends BaseAgent {
|
|
|
1672
2154
|
const toolResults = await this.executeToolCalls(
|
|
1673
2155
|
decision,
|
|
1674
2156
|
context,
|
|
1675
|
-
|
|
2157
|
+
context.parentSpanId ?? void 0
|
|
1676
2158
|
);
|
|
1677
2159
|
const combinedResults = [...memoryResults, ...toolResults];
|
|
1678
2160
|
const newToolCalls = context.toolCalls.slice(toolCallStartIndex);
|
|
@@ -1707,11 +2189,24 @@ var Agent = class extends BaseAgent {
|
|
|
1707
2189
|
);
|
|
1708
2190
|
}
|
|
1709
2191
|
const result = await this.generateFinalResult(input, context);
|
|
1710
|
-
|
|
2192
|
+
const executionEndTime = /* @__PURE__ */ new Date();
|
|
2193
|
+
await this.opperClient.updateSpan(parentSpan.id, result, {
|
|
2194
|
+
startTime: executionStartTime,
|
|
2195
|
+
endTime: executionEndTime,
|
|
2196
|
+
meta: {
|
|
2197
|
+
durationMs: executionEndTime.getTime() - executionStartTime.getTime()
|
|
2198
|
+
}
|
|
2199
|
+
});
|
|
1711
2200
|
return result;
|
|
1712
2201
|
} catch (error) {
|
|
2202
|
+
const executionEndTime = /* @__PURE__ */ new Date();
|
|
1713
2203
|
await this.opperClient.updateSpan(parentSpan.id, void 0, {
|
|
1714
|
-
error: error instanceof Error ? error.message : String(error)
|
|
2204
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2205
|
+
startTime: executionStartTime,
|
|
2206
|
+
endTime: executionEndTime,
|
|
2207
|
+
meta: {
|
|
2208
|
+
durationMs: executionEndTime.getTime() - executionStartTime.getTime()
|
|
2209
|
+
}
|
|
1715
2210
|
});
|
|
1716
2211
|
throw error;
|
|
1717
2212
|
}
|
|
@@ -1720,9 +2215,16 @@ var Agent = class extends BaseAgent {
|
|
|
1720
2215
|
* Think step: Call LLM to decide next action
|
|
1721
2216
|
*/
|
|
1722
2217
|
async think(input, context) {
|
|
1723
|
-
const
|
|
2218
|
+
const sanitizedName = this.name.toLowerCase().replace(/[\s-]/g, "_");
|
|
2219
|
+
const spanName = `think_${sanitizedName}`;
|
|
1724
2220
|
this.log("Think step", { iteration: context.iteration });
|
|
1725
2221
|
await this.triggerHook(HookEvents.LlmCall, { context, callType: "think" });
|
|
2222
|
+
const decisionSchema = createAgentDecisionWithOutputSchema(
|
|
2223
|
+
this.outputSchema
|
|
2224
|
+
);
|
|
2225
|
+
if (this.enableStreaming) {
|
|
2226
|
+
return this.thinkStreaming(input, context, decisionSchema, spanName);
|
|
2227
|
+
}
|
|
1726
2228
|
try {
|
|
1727
2229
|
const instructions = this.buildThinkInstructions();
|
|
1728
2230
|
const thinkContext = await this.buildThinkContext(input, context);
|
|
@@ -1730,7 +2232,7 @@ var Agent = class extends BaseAgent {
|
|
|
1730
2232
|
name: spanName,
|
|
1731
2233
|
instructions,
|
|
1732
2234
|
input: thinkContext,
|
|
1733
|
-
outputSchema:
|
|
2235
|
+
outputSchema: decisionSchema,
|
|
1734
2236
|
model: this.model,
|
|
1735
2237
|
...context.parentSpanId && { parentSpanId: context.parentSpanId }
|
|
1736
2238
|
});
|
|
@@ -1741,15 +2243,25 @@ var Agent = class extends BaseAgent {
|
|
|
1741
2243
|
totalTokens: response.usage.totalTokens,
|
|
1742
2244
|
cost: response.usage.cost
|
|
1743
2245
|
});
|
|
1744
|
-
const decision =
|
|
2246
|
+
const decision = decisionSchema.parse(
|
|
2247
|
+
response.jsonPayload
|
|
2248
|
+
);
|
|
1745
2249
|
await this.triggerHook(HookEvents.LlmResponse, {
|
|
1746
2250
|
context,
|
|
1747
2251
|
callType: "think",
|
|
1748
2252
|
response
|
|
1749
2253
|
});
|
|
2254
|
+
if (response.spanId) {
|
|
2255
|
+
await this.opperClient.updateSpan(response.spanId, void 0, {
|
|
2256
|
+
name: "think"
|
|
2257
|
+
});
|
|
2258
|
+
}
|
|
1750
2259
|
await this.triggerHook(HookEvents.ThinkEnd, {
|
|
1751
2260
|
context,
|
|
1752
|
-
thought: {
|
|
2261
|
+
thought: {
|
|
2262
|
+
reasoning: decision.reasoning,
|
|
2263
|
+
userMessage: decision.userMessage
|
|
2264
|
+
}
|
|
1753
2265
|
});
|
|
1754
2266
|
this.log("Think result", {
|
|
1755
2267
|
reasoning: decision.reasoning,
|
|
@@ -1765,6 +2277,139 @@ var Agent = class extends BaseAgent {
|
|
|
1765
2277
|
);
|
|
1766
2278
|
}
|
|
1767
2279
|
}
|
|
2280
|
+
async thinkStreaming(input, context, decisionSchema, spanName) {
|
|
2281
|
+
const instructions = this.buildThinkInstructions();
|
|
2282
|
+
const thinkContext = await this.buildThinkContext(input, context);
|
|
2283
|
+
const assembler = createStreamAssembler({
|
|
2284
|
+
schema: decisionSchema
|
|
2285
|
+
});
|
|
2286
|
+
let streamSpanId;
|
|
2287
|
+
await this.triggerHook(HookEvents.StreamStart, {
|
|
2288
|
+
context,
|
|
2289
|
+
callType: "think"
|
|
2290
|
+
});
|
|
2291
|
+
this.emitAgentEvent(HookEvents.StreamStart, {
|
|
2292
|
+
context,
|
|
2293
|
+
callType: "think"
|
|
2294
|
+
});
|
|
2295
|
+
try {
|
|
2296
|
+
const streamResponse = await this.opperClient.stream({
|
|
2297
|
+
name: spanName,
|
|
2298
|
+
instructions,
|
|
2299
|
+
input: thinkContext,
|
|
2300
|
+
outputSchema: decisionSchema,
|
|
2301
|
+
model: this.model,
|
|
2302
|
+
...context.parentSpanId && { parentSpanId: context.parentSpanId }
|
|
2303
|
+
});
|
|
2304
|
+
for await (const event of streamResponse.result) {
|
|
2305
|
+
const data = event?.data;
|
|
2306
|
+
if (!data) {
|
|
2307
|
+
continue;
|
|
2308
|
+
}
|
|
2309
|
+
if (!streamSpanId && typeof data.spanId === "string" && data.spanId) {
|
|
2310
|
+
streamSpanId = data.spanId;
|
|
2311
|
+
}
|
|
2312
|
+
const feedResult = assembler.feed({
|
|
2313
|
+
delta: data.delta,
|
|
2314
|
+
jsonPath: data.jsonPath
|
|
2315
|
+
});
|
|
2316
|
+
if (!feedResult) {
|
|
2317
|
+
continue;
|
|
2318
|
+
}
|
|
2319
|
+
const chunkPayload = {
|
|
2320
|
+
context,
|
|
2321
|
+
callType: "think",
|
|
2322
|
+
chunkData: {
|
|
2323
|
+
delta: data.delta,
|
|
2324
|
+
jsonPath: data.jsonPath ?? null,
|
|
2325
|
+
chunkType: data.chunkType ?? null
|
|
2326
|
+
},
|
|
2327
|
+
accumulated: feedResult.accumulated,
|
|
2328
|
+
fieldBuffers: feedResult.snapshot
|
|
2329
|
+
};
|
|
2330
|
+
await this.triggerHook(HookEvents.StreamChunk, chunkPayload);
|
|
2331
|
+
this.emitAgentEvent(HookEvents.StreamChunk, chunkPayload);
|
|
2332
|
+
}
|
|
2333
|
+
const fieldBuffers = assembler.snapshot();
|
|
2334
|
+
const endPayload = {
|
|
2335
|
+
context,
|
|
2336
|
+
callType: "think",
|
|
2337
|
+
fieldBuffers
|
|
2338
|
+
};
|
|
2339
|
+
await this.triggerHook(HookEvents.StreamEnd, endPayload);
|
|
2340
|
+
this.emitAgentEvent(HookEvents.StreamEnd, endPayload);
|
|
2341
|
+
const finalize = assembler.finalize();
|
|
2342
|
+
let decision;
|
|
2343
|
+
if (finalize.type === "structured" && finalize.structured) {
|
|
2344
|
+
decision = decisionSchema.parse(
|
|
2345
|
+
finalize.structured
|
|
2346
|
+
);
|
|
2347
|
+
} else {
|
|
2348
|
+
decision = decisionSchema.parse({
|
|
2349
|
+
reasoning: finalize.type === "root" ? finalize.rootText ?? "" : ""
|
|
2350
|
+
});
|
|
2351
|
+
}
|
|
2352
|
+
const usageTracked = await this.trackStreamingUsageBySpan(
|
|
2353
|
+
context,
|
|
2354
|
+
streamSpanId
|
|
2355
|
+
);
|
|
2356
|
+
if (!usageTracked) {
|
|
2357
|
+
context.updateUsage({
|
|
2358
|
+
requests: 1,
|
|
2359
|
+
inputTokens: 0,
|
|
2360
|
+
outputTokens: 0,
|
|
2361
|
+
totalTokens: 0,
|
|
2362
|
+
cost: { generation: 0, platform: 0, total: 0 }
|
|
2363
|
+
});
|
|
2364
|
+
}
|
|
2365
|
+
await this.triggerHook(HookEvents.LlmResponse, {
|
|
2366
|
+
context,
|
|
2367
|
+
callType: "think",
|
|
2368
|
+
response: streamResponse,
|
|
2369
|
+
parsed: decision
|
|
2370
|
+
});
|
|
2371
|
+
if (streamSpanId) {
|
|
2372
|
+
await this.opperClient.updateSpan(streamSpanId, void 0, {
|
|
2373
|
+
name: "think"
|
|
2374
|
+
});
|
|
2375
|
+
}
|
|
2376
|
+
await this.triggerHook(HookEvents.ThinkEnd, {
|
|
2377
|
+
context,
|
|
2378
|
+
thought: {
|
|
2379
|
+
reasoning: decision.reasoning,
|
|
2380
|
+
userMessage: decision.userMessage
|
|
2381
|
+
}
|
|
2382
|
+
});
|
|
2383
|
+
this.log("Think result", {
|
|
2384
|
+
reasoning: decision.reasoning,
|
|
2385
|
+
toolCalls: decision.toolCalls.length,
|
|
2386
|
+
memoryReads: decision.memoryReads?.length ?? 0,
|
|
2387
|
+
memoryWrites: Object.keys(decision.memoryUpdates ?? {}).length
|
|
2388
|
+
});
|
|
2389
|
+
const resultPayload = {
|
|
2390
|
+
decision
|
|
2391
|
+
};
|
|
2392
|
+
if (streamSpanId) {
|
|
2393
|
+
resultPayload.spanId = streamSpanId;
|
|
2394
|
+
}
|
|
2395
|
+
return resultPayload;
|
|
2396
|
+
} catch (error) {
|
|
2397
|
+
await this.triggerHook(HookEvents.StreamError, {
|
|
2398
|
+
context,
|
|
2399
|
+
callType: "think",
|
|
2400
|
+
error
|
|
2401
|
+
});
|
|
2402
|
+
this.emitAgentEvent(HookEvents.StreamError, {
|
|
2403
|
+
context,
|
|
2404
|
+
callType: "think",
|
|
2405
|
+
error
|
|
2406
|
+
});
|
|
2407
|
+
this.logger.error("Think step failed", error);
|
|
2408
|
+
throw new Error(
|
|
2409
|
+
`Think step failed: ${error instanceof Error ? error.message : String(error)}`
|
|
2410
|
+
);
|
|
2411
|
+
}
|
|
2412
|
+
}
|
|
1768
2413
|
/**
|
|
1769
2414
|
* Build static instructions for the think step
|
|
1770
2415
|
*/
|
|
@@ -1775,12 +2420,22 @@ YOUR TASK:
|
|
|
1775
2420
|
1. Analyze the current situation
|
|
1776
2421
|
2. Decide if the goal is complete or more actions are needed
|
|
1777
2422
|
3. If more actions needed: specify tools to call
|
|
1778
|
-
4. If goal complete:
|
|
2423
|
+
4. If goal complete:
|
|
2424
|
+
- Set isComplete=true
|
|
2425
|
+
- Provide the complete answer/output in finalResult
|
|
2426
|
+
- Leave toolCalls empty
|
|
1779
2427
|
|
|
1780
2428
|
IMPORTANT:
|
|
1781
|
-
-
|
|
2429
|
+
- When task is COMPLETE, you MUST set isComplete=true AND provide finalResult
|
|
2430
|
+
- The finalResult should be a complete, well-structured answer based on all work done
|
|
1782
2431
|
- Only use available tools
|
|
1783
|
-
- Provide clear reasoning for each decision
|
|
2432
|
+
- Provide clear reasoning for each decision
|
|
2433
|
+
- If an outputSchema was specified, ensure finalResult matches that schema
|
|
2434
|
+
|
|
2435
|
+
USER MESSAGE:
|
|
2436
|
+
- Always provide a brief, user-friendly status in userMessage
|
|
2437
|
+
- This message is shown to users to indicate progress (e.g., "Searching for weather data...", "Calculating results...", "Done!")
|
|
2438
|
+
- Keep it concise and informative`;
|
|
1784
2439
|
if (this.enableMemory) {
|
|
1785
2440
|
instructions += `
|
|
1786
2441
|
|
|
@@ -1864,9 +2519,14 @@ The memory you write persists across all process() calls on this agent.`;
|
|
|
1864
2519
|
this.log(`Action: ${toolCall.toolName}`, {
|
|
1865
2520
|
parameters: toolCall.arguments
|
|
1866
2521
|
});
|
|
2522
|
+
const startTime = /* @__PURE__ */ new Date();
|
|
2523
|
+
const tool2 = this.tools.get(toolCall.toolName);
|
|
2524
|
+
const isAgentTool = tool2?.metadata?.["isAgent"] === true;
|
|
2525
|
+
const spanType = isAgentTool ? "\u{1F916} agent" : "\u{1F527} tool";
|
|
1867
2526
|
const toolSpan = await this.opperClient.createSpan({
|
|
1868
2527
|
name: `tool_${toolCall.toolName}`,
|
|
1869
2528
|
input: toolCall.arguments,
|
|
2529
|
+
type: spanType,
|
|
1870
2530
|
...parentSpanId ? { parentSpanId } : context.parentSpanId ? { parentSpanId: context.parentSpanId } : {}
|
|
1871
2531
|
});
|
|
1872
2532
|
try {
|
|
@@ -1876,11 +2536,20 @@ The memory you write persists across all process() calls on this agent.`;
|
|
|
1876
2536
|
context,
|
|
1877
2537
|
{ spanId: toolSpan.id }
|
|
1878
2538
|
);
|
|
2539
|
+
const endTime = /* @__PURE__ */ new Date();
|
|
2540
|
+
const durationMs = endTime.getTime() - startTime.getTime();
|
|
1879
2541
|
if (result.success) {
|
|
1880
|
-
await this.opperClient.updateSpan(toolSpan.id, result.output
|
|
2542
|
+
await this.opperClient.updateSpan(toolSpan.id, result.output, {
|
|
2543
|
+
startTime,
|
|
2544
|
+
endTime,
|
|
2545
|
+
meta: { durationMs }
|
|
2546
|
+
});
|
|
1881
2547
|
} else {
|
|
1882
2548
|
await this.opperClient.updateSpan(toolSpan.id, void 0, {
|
|
1883
|
-
error: result.error instanceof Error ? result.error.message : String(result.error)
|
|
2549
|
+
error: result.error instanceof Error ? result.error.message : String(result.error),
|
|
2550
|
+
startTime,
|
|
2551
|
+
endTime,
|
|
2552
|
+
meta: { durationMs }
|
|
1884
2553
|
});
|
|
1885
2554
|
}
|
|
1886
2555
|
const summary = {
|
|
@@ -1895,12 +2564,18 @@ The memory you write persists across all process() calls on this agent.`;
|
|
|
1895
2564
|
this.log(
|
|
1896
2565
|
`Tool ${toolCall.toolName} ${result.success ? "succeeded" : "failed"}`,
|
|
1897
2566
|
{
|
|
1898
|
-
success: result.success
|
|
2567
|
+
success: result.success,
|
|
2568
|
+
durationMs
|
|
1899
2569
|
}
|
|
1900
2570
|
);
|
|
1901
2571
|
} catch (error) {
|
|
2572
|
+
const endTime = /* @__PURE__ */ new Date();
|
|
2573
|
+
const durationMs = endTime.getTime() - startTime.getTime();
|
|
1902
2574
|
await this.opperClient.updateSpan(toolSpan.id, void 0, {
|
|
1903
|
-
error: error instanceof Error ? error.message : String(error)
|
|
2575
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2576
|
+
startTime,
|
|
2577
|
+
endTime,
|
|
2578
|
+
meta: { durationMs }
|
|
1904
2579
|
});
|
|
1905
2580
|
const summary = {
|
|
1906
2581
|
toolName: toolCall.toolName,
|
|
@@ -1909,7 +2584,8 @@ The memory you write persists across all process() calls on this agent.`;
|
|
|
1909
2584
|
};
|
|
1910
2585
|
results.push(ToolExecutionSummarySchema.parse(summary));
|
|
1911
2586
|
this.logger.warn(`Tool ${toolCall.toolName} threw error`, {
|
|
1912
|
-
error: error instanceof Error ? error.message : String(error)
|
|
2587
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2588
|
+
durationMs
|
|
1913
2589
|
});
|
|
1914
2590
|
}
|
|
1915
2591
|
}
|
|
@@ -1938,6 +2614,7 @@ The memory you write persists across all process() calls on this agent.`;
|
|
|
1938
2614
|
const spanParentId = parentSpanId ?? context.parentSpanId ?? void 0;
|
|
1939
2615
|
const summaries = [];
|
|
1940
2616
|
if (hasReads) {
|
|
2617
|
+
const startTime = /* @__PURE__ */ new Date();
|
|
1941
2618
|
try {
|
|
1942
2619
|
const keySet = new Set(
|
|
1943
2620
|
decision.memoryReads.filter(
|
|
@@ -1949,10 +2626,17 @@ The memory you write persists across all process() calls on this agent.`;
|
|
|
1949
2626
|
const memoryReadSpan = await this.opperClient.createSpan({
|
|
1950
2627
|
name: "memory_read",
|
|
1951
2628
|
input: keys,
|
|
2629
|
+
type: "\u{1F9E0} memory",
|
|
1952
2630
|
...spanParentId && { parentSpanId: spanParentId }
|
|
1953
2631
|
});
|
|
1954
2632
|
const memoryData = await this.memory.read(keys);
|
|
1955
|
-
|
|
2633
|
+
const endTime = /* @__PURE__ */ new Date();
|
|
2634
|
+
const durationMs = endTime.getTime() - startTime.getTime();
|
|
2635
|
+
await this.opperClient.updateSpan(memoryReadSpan.id, memoryData, {
|
|
2636
|
+
startTime,
|
|
2637
|
+
endTime,
|
|
2638
|
+
meta: { durationMs }
|
|
2639
|
+
});
|
|
1956
2640
|
context.setMetadata("current_memory", memoryData);
|
|
1957
2641
|
this.log(`Loaded ${Object.keys(memoryData).length} memory entries`, {
|
|
1958
2642
|
keys
|
|
@@ -1991,10 +2675,12 @@ The memory you write persists across all process() calls on this agent.`;
|
|
|
1991
2675
|
}
|
|
1992
2676
|
}
|
|
1993
2677
|
if (hasWrites) {
|
|
2678
|
+
const startTime = /* @__PURE__ */ new Date();
|
|
1994
2679
|
try {
|
|
1995
2680
|
const memoryWriteSpan = await this.opperClient.createSpan({
|
|
1996
2681
|
name: "memory_write",
|
|
1997
2682
|
input: updateEntries.map(([key]) => key),
|
|
2683
|
+
type: "\u{1F9E0} memory",
|
|
1998
2684
|
...spanParentId && { parentSpanId: spanParentId }
|
|
1999
2685
|
});
|
|
2000
2686
|
for (const [key, update] of updateEntries) {
|
|
@@ -2011,9 +2697,16 @@ The memory you write persists across all process() calls on this agent.`;
|
|
|
2011
2697
|
value: castUpdate.value
|
|
2012
2698
|
});
|
|
2013
2699
|
}
|
|
2700
|
+
const endTime = /* @__PURE__ */ new Date();
|
|
2701
|
+
const durationMs = endTime.getTime() - startTime.getTime();
|
|
2014
2702
|
await this.opperClient.updateSpan(
|
|
2015
2703
|
memoryWriteSpan.id,
|
|
2016
|
-
`Successfully wrote ${updateEntries.length} keys
|
|
2704
|
+
`Successfully wrote ${updateEntries.length} keys`,
|
|
2705
|
+
{
|
|
2706
|
+
startTime,
|
|
2707
|
+
endTime,
|
|
2708
|
+
meta: { durationMs }
|
|
2709
|
+
}
|
|
2017
2710
|
);
|
|
2018
2711
|
this.log(`Wrote ${updateEntries.length} memory entries`);
|
|
2019
2712
|
summaries.push(
|
|
@@ -2078,9 +2771,18 @@ The memory you write persists across all process() calls on this agent.`;
|
|
|
2078
2771
|
};
|
|
2079
2772
|
const instructions = `Generate the final result based on the execution history.
|
|
2080
2773
|
Follow any instructions provided for formatting and style.`;
|
|
2774
|
+
if (this.enableStreaming) {
|
|
2775
|
+
return this.generateFinalResultStreaming(
|
|
2776
|
+
context,
|
|
2777
|
+
finalContext,
|
|
2778
|
+
instructions
|
|
2779
|
+
);
|
|
2780
|
+
}
|
|
2081
2781
|
try {
|
|
2782
|
+
const sanitizedName = this.name.toLowerCase().replace(/[\s-]/g, "_");
|
|
2783
|
+
const functionName = `generate_final_result_${sanitizedName}`;
|
|
2082
2784
|
const callOptions = {
|
|
2083
|
-
name:
|
|
2785
|
+
name: functionName,
|
|
2084
2786
|
instructions,
|
|
2085
2787
|
input: finalContext,
|
|
2086
2788
|
model: this.model
|
|
@@ -2113,6 +2815,172 @@ Follow any instructions provided for formatting and style.`;
|
|
|
2113
2815
|
);
|
|
2114
2816
|
}
|
|
2115
2817
|
}
|
|
2818
|
+
async generateFinalResultStreaming(context, finalContext, instructions) {
|
|
2819
|
+
const assembler = createStreamAssembler(
|
|
2820
|
+
this.outputSchema ? { schema: this.outputSchema } : void 0
|
|
2821
|
+
);
|
|
2822
|
+
let streamSpanId;
|
|
2823
|
+
await this.triggerHook(HookEvents.StreamStart, {
|
|
2824
|
+
context,
|
|
2825
|
+
callType: "final_result"
|
|
2826
|
+
});
|
|
2827
|
+
this.emitAgentEvent(HookEvents.StreamStart, {
|
|
2828
|
+
context,
|
|
2829
|
+
callType: "final_result"
|
|
2830
|
+
});
|
|
2831
|
+
try {
|
|
2832
|
+
const sanitizedName = this.name.toLowerCase().replace(/[\s-]/g, "_");
|
|
2833
|
+
const functionName = `generate_final_result_${sanitizedName}`;
|
|
2834
|
+
const streamResponse = await this.opperClient.stream({
|
|
2835
|
+
name: functionName,
|
|
2836
|
+
instructions,
|
|
2837
|
+
input: finalContext,
|
|
2838
|
+
model: this.model,
|
|
2839
|
+
...context.parentSpanId && { parentSpanId: context.parentSpanId },
|
|
2840
|
+
...this.outputSchema && { outputSchema: this.outputSchema }
|
|
2841
|
+
});
|
|
2842
|
+
for await (const event of streamResponse.result) {
|
|
2843
|
+
const data = event?.data;
|
|
2844
|
+
if (!data) {
|
|
2845
|
+
continue;
|
|
2846
|
+
}
|
|
2847
|
+
if (!streamSpanId && typeof data.spanId === "string" && data.spanId) {
|
|
2848
|
+
streamSpanId = data.spanId;
|
|
2849
|
+
}
|
|
2850
|
+
const feedResult = assembler.feed({
|
|
2851
|
+
delta: data.delta,
|
|
2852
|
+
jsonPath: data.jsonPath
|
|
2853
|
+
});
|
|
2854
|
+
if (!feedResult) {
|
|
2855
|
+
continue;
|
|
2856
|
+
}
|
|
2857
|
+
const chunkPayload = {
|
|
2858
|
+
context,
|
|
2859
|
+
callType: "final_result",
|
|
2860
|
+
chunkData: {
|
|
2861
|
+
delta: data.delta,
|
|
2862
|
+
jsonPath: data.jsonPath ?? null,
|
|
2863
|
+
chunkType: data.chunkType ?? null
|
|
2864
|
+
},
|
|
2865
|
+
accumulated: feedResult.accumulated,
|
|
2866
|
+
fieldBuffers: feedResult.snapshot
|
|
2867
|
+
};
|
|
2868
|
+
await this.triggerHook(HookEvents.StreamChunk, chunkPayload);
|
|
2869
|
+
this.emitAgentEvent(HookEvents.StreamChunk, chunkPayload);
|
|
2870
|
+
}
|
|
2871
|
+
const fieldBuffers = assembler.snapshot();
|
|
2872
|
+
const endPayload = {
|
|
2873
|
+
context,
|
|
2874
|
+
callType: "final_result",
|
|
2875
|
+
fieldBuffers
|
|
2876
|
+
};
|
|
2877
|
+
await this.triggerHook(HookEvents.StreamEnd, endPayload);
|
|
2878
|
+
this.emitAgentEvent(HookEvents.StreamEnd, endPayload);
|
|
2879
|
+
const finalize = assembler.finalize();
|
|
2880
|
+
let result;
|
|
2881
|
+
if (this.outputSchema) {
|
|
2882
|
+
if (finalize.type !== "structured" || finalize.structured === void 0) {
|
|
2883
|
+
throw new Error(
|
|
2884
|
+
"Streaming response did not provide structured data for the configured output schema."
|
|
2885
|
+
);
|
|
2886
|
+
}
|
|
2887
|
+
result = this.outputSchema.parse(
|
|
2888
|
+
finalize.structured
|
|
2889
|
+
);
|
|
2890
|
+
} else if (finalize.type === "root") {
|
|
2891
|
+
result = finalize.rootText ?? "";
|
|
2892
|
+
} else if (finalize.type === "structured" && finalize.structured) {
|
|
2893
|
+
result = JSON.stringify(finalize.structured);
|
|
2894
|
+
} else {
|
|
2895
|
+
result = "";
|
|
2896
|
+
}
|
|
2897
|
+
const usageTracked = await this.trackStreamingUsageBySpan(
|
|
2898
|
+
context,
|
|
2899
|
+
streamSpanId
|
|
2900
|
+
);
|
|
2901
|
+
if (!usageTracked) {
|
|
2902
|
+
context.updateUsage({
|
|
2903
|
+
requests: 1,
|
|
2904
|
+
inputTokens: 0,
|
|
2905
|
+
outputTokens: 0,
|
|
2906
|
+
totalTokens: 0,
|
|
2907
|
+
cost: { generation: 0, platform: 0, total: 0 }
|
|
2908
|
+
});
|
|
2909
|
+
}
|
|
2910
|
+
await this.triggerHook(HookEvents.LlmResponse, {
|
|
2911
|
+
context,
|
|
2912
|
+
callType: "final_result",
|
|
2913
|
+
response: streamResponse,
|
|
2914
|
+
parsed: result
|
|
2915
|
+
});
|
|
2916
|
+
this.log(
|
|
2917
|
+
this.outputSchema ? "Final result generated (streaming, schema-validated)" : "Final result generated (streaming)"
|
|
2918
|
+
);
|
|
2919
|
+
return result;
|
|
2920
|
+
} catch (error) {
|
|
2921
|
+
await this.triggerHook(HookEvents.StreamError, {
|
|
2922
|
+
context,
|
|
2923
|
+
callType: "final_result",
|
|
2924
|
+
error
|
|
2925
|
+
});
|
|
2926
|
+
this.emitAgentEvent(HookEvents.StreamError, {
|
|
2927
|
+
context,
|
|
2928
|
+
callType: "final_result",
|
|
2929
|
+
error
|
|
2930
|
+
});
|
|
2931
|
+
this.logger.error("Failed to generate final result", error);
|
|
2932
|
+
throw new Error(
|
|
2933
|
+
`Failed to generate final result: ${error instanceof Error ? error.message : String(error)}`
|
|
2934
|
+
);
|
|
2935
|
+
}
|
|
2936
|
+
}
|
|
2937
|
+
async trackStreamingUsageBySpan(context, spanId) {
|
|
2938
|
+
if (!spanId) {
|
|
2939
|
+
return false;
|
|
2940
|
+
}
|
|
2941
|
+
try {
|
|
2942
|
+
const span = await this.opperClient.getClient().spans.get(spanId);
|
|
2943
|
+
const traceId = span?.traceId ?? span?.trace_id;
|
|
2944
|
+
if (!traceId) {
|
|
2945
|
+
return false;
|
|
2946
|
+
}
|
|
2947
|
+
const trace = await this.opperClient.getClient().traces.get(traceId);
|
|
2948
|
+
const spans = trace?.spans;
|
|
2949
|
+
if (!Array.isArray(spans)) {
|
|
2950
|
+
return false;
|
|
2951
|
+
}
|
|
2952
|
+
for (const entry of spans) {
|
|
2953
|
+
const entryId = entry?.id ?? entry["id"];
|
|
2954
|
+
if (entryId !== spanId) {
|
|
2955
|
+
continue;
|
|
2956
|
+
}
|
|
2957
|
+
const data = entry?.data;
|
|
2958
|
+
if (!data) {
|
|
2959
|
+
continue;
|
|
2960
|
+
}
|
|
2961
|
+
const record = data;
|
|
2962
|
+
const primaryTotal = record["totalTokens"];
|
|
2963
|
+
const fallbackTotal = record["total_tokens"];
|
|
2964
|
+
const totalTokensRaw = typeof primaryTotal === "number" && Number.isFinite(primaryTotal) ? primaryTotal : typeof fallbackTotal === "number" && Number.isFinite(fallbackTotal) ? fallbackTotal : void 0;
|
|
2965
|
+
if (totalTokensRaw !== void 0) {
|
|
2966
|
+
context.updateUsage({
|
|
2967
|
+
requests: 1,
|
|
2968
|
+
inputTokens: 0,
|
|
2969
|
+
outputTokens: 0,
|
|
2970
|
+
totalTokens: totalTokensRaw,
|
|
2971
|
+
cost: { generation: 0, platform: 0, total: 0 }
|
|
2972
|
+
});
|
|
2973
|
+
return true;
|
|
2974
|
+
}
|
|
2975
|
+
}
|
|
2976
|
+
} catch (error) {
|
|
2977
|
+
this.logger.warn("Could not fetch streaming usage", {
|
|
2978
|
+
spanId,
|
|
2979
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2980
|
+
});
|
|
2981
|
+
}
|
|
2982
|
+
return false;
|
|
2983
|
+
}
|
|
2116
2984
|
/**
|
|
2117
2985
|
* Log helper
|
|
2118
2986
|
*/
|
|
@@ -2329,7 +3197,7 @@ var createMCPServerConfig = MCPconfig;
|
|
|
2329
3197
|
|
|
2330
3198
|
// src/mcp/provider.ts
|
|
2331
3199
|
init_tool();
|
|
2332
|
-
var
|
|
3200
|
+
var isRecord2 = (value) => typeof value === "object" && value !== null;
|
|
2333
3201
|
var MCPToolProvider = class {
|
|
2334
3202
|
configs;
|
|
2335
3203
|
namePrefix;
|
|
@@ -2420,7 +3288,7 @@ var MCPToolProvider = class {
|
|
|
2420
3288
|
},
|
|
2421
3289
|
execute: async (input, context) => {
|
|
2422
3290
|
const startedAt = Date.now();
|
|
2423
|
-
const args =
|
|
3291
|
+
const args = isRecord2(input) ? input : input === void 0 ? {} : { value: input };
|
|
2424
3292
|
try {
|
|
2425
3293
|
const result = await client.callTool(mcpTool.name, args);
|
|
2426
3294
|
return exports.ToolResultFactory.success(toolName, result, {
|
|
@@ -2880,6 +3748,8 @@ var ToolRunner = class {
|
|
|
2880
3748
|
|
|
2881
3749
|
exports.Agent = Agent;
|
|
2882
3750
|
exports.AgentDecisionSchema = AgentDecisionSchema;
|
|
3751
|
+
exports.AgentEventEmitter = AgentEventEmitter;
|
|
3752
|
+
exports.AgentEvents = AgentEvents;
|
|
2883
3753
|
exports.BaseAgent = BaseAgent;
|
|
2884
3754
|
exports.ConsoleLogger = ConsoleLogger;
|
|
2885
3755
|
exports.DEFAULT_MODEL = DEFAULT_MODEL;
|
|
@@ -2896,17 +3766,21 @@ exports.MemoryEntryMetadataSchema = MemoryEntryMetadataSchema;
|
|
|
2896
3766
|
exports.MemoryEntrySchema = MemoryEntrySchema;
|
|
2897
3767
|
exports.MemoryUpdateSchema = MemoryUpdateSchema;
|
|
2898
3768
|
exports.OpperClient = OpperClient;
|
|
3769
|
+
exports.STREAM_ROOT_PATH = STREAM_ROOT_PATH;
|
|
2899
3770
|
exports.SchemaValidationError = SchemaValidationError;
|
|
2900
3771
|
exports.SilentLogger = SilentLogger;
|
|
3772
|
+
exports.StreamAssembler = StreamAssembler;
|
|
2901
3773
|
exports.ThoughtSchema = ThoughtSchema;
|
|
2902
3774
|
exports.ToolCallSchema = ToolCallSchema;
|
|
2903
3775
|
exports.ToolExecutionSummarySchema = ToolExecutionSummarySchema;
|
|
2904
3776
|
exports.ToolRunner = ToolRunner;
|
|
3777
|
+
exports.createAgentDecisionWithOutputSchema = createAgentDecisionWithOutputSchema;
|
|
2905
3778
|
exports.createFunctionTool = createFunctionTool;
|
|
2906
3779
|
exports.createHookManager = createHookManager;
|
|
2907
3780
|
exports.createInMemoryStore = createInMemoryStore;
|
|
2908
3781
|
exports.createMCPServerConfig = createMCPServerConfig;
|
|
2909
3782
|
exports.createOpperClient = createOpperClient;
|
|
3783
|
+
exports.createStreamAssembler = createStreamAssembler;
|
|
2910
3784
|
exports.extractTools = extractTools;
|
|
2911
3785
|
exports.generateAgentFlowDiagram = generateAgentFlowDiagram;
|
|
2912
3786
|
exports.getDefaultLogger = getDefaultLogger;
|