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