@microfox/ai-router 1.1.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -67
- package/README.md +705 -129
- package/dist/chunk-BJTO5JO5.mjs +11 -0
- package/dist/chunk-BJTO5JO5.mjs.map +1 -0
- package/dist/fs_store.d.mts +5 -0
- package/dist/fs_store.d.ts +5 -0
- package/dist/fs_store.js +122 -0
- package/dist/fs_store.js.map +1 -0
- package/dist/fs_store.mjs +101 -0
- package/dist/fs_store.mjs.map +1 -0
- package/dist/index.d.mts +196 -48
- package/dist/index.d.ts +196 -48
- package/dist/index.js +516 -110
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +517 -110
- package/dist/index.mjs.map +1 -1
- package/dist/store-BBHh-uTh.d.mts +27 -0
- package/dist/store-BBHh-uTh.d.ts +27 -0
- package/package.json +14 -7
package/dist/index.js
CHANGED
|
@@ -35,14 +35,17 @@ __export(index_exports, {
|
|
|
35
35
|
AiKitError: () => AiKitError,
|
|
36
36
|
AiRouter: () => AiRouter,
|
|
37
37
|
MaxCallDepthExceededError: () => MaxCallDepthExceededError,
|
|
38
|
+
MemoryStore: () => MemoryStore,
|
|
38
39
|
StreamWriter: () => StreamWriter,
|
|
39
40
|
ToolNotFoundError: () => ToolNotFoundError,
|
|
40
41
|
ToolValidationError: () => ToolValidationError,
|
|
41
42
|
findFirstElement: () => findFirstElement,
|
|
42
43
|
findLastElement: () => findLastElement,
|
|
43
44
|
findLastMessageWith: () => findLastMessageWith,
|
|
45
|
+
getGlobalLogger: () => getGlobalLogger,
|
|
44
46
|
getTextParts: () => getTextParts,
|
|
45
|
-
getTextPartsContent: () => getTextPartsContent
|
|
47
|
+
getTextPartsContent: () => getTextPartsContent,
|
|
48
|
+
setGlobalLogger: () => setGlobalLogger
|
|
46
49
|
});
|
|
47
50
|
module.exports = __toCommonJS(index_exports);
|
|
48
51
|
|
|
@@ -105,26 +108,26 @@ var StreamWriter = class {
|
|
|
105
108
|
});
|
|
106
109
|
};
|
|
107
110
|
this.writeCustomTool = (tool2) => {
|
|
108
|
-
const toolCallId = tool2.toolName?.toString() + "-" + this.generateId();
|
|
111
|
+
const toolCallId = tool2.toolCallId || tool2.toolName?.toString() + "-" + this.generateId();
|
|
109
112
|
if ("input" in tool2 && tool2.input) {
|
|
110
113
|
this.writer.write({
|
|
111
114
|
type: "tool-input-available",
|
|
112
115
|
input: tool2.input,
|
|
113
|
-
toolCallId
|
|
116
|
+
toolCallId,
|
|
114
117
|
toolName: tool2.toolName
|
|
115
118
|
});
|
|
116
119
|
}
|
|
117
120
|
if (tool2.inputTextDelta && tool2.inputTextDelta.length > 0 || "output" in tool2 && tool2.output) {
|
|
118
121
|
this.writer.write({
|
|
119
122
|
type: "tool-input-start",
|
|
120
|
-
toolCallId
|
|
123
|
+
toolCallId,
|
|
121
124
|
toolName: tool2.toolName
|
|
122
125
|
});
|
|
123
126
|
if (tool2.inputTextDelta) {
|
|
124
127
|
for (const delta of tool2.inputTextDelta) {
|
|
125
128
|
this.writer.write({
|
|
126
129
|
type: "tool-input-delta",
|
|
127
|
-
toolCallId
|
|
130
|
+
toolCallId,
|
|
128
131
|
inputTextDelta: delta
|
|
129
132
|
});
|
|
130
133
|
}
|
|
@@ -133,13 +136,13 @@ var StreamWriter = class {
|
|
|
133
136
|
if ("output" in tool2 && tool2.output) {
|
|
134
137
|
this.writer.write({
|
|
135
138
|
type: "tool-output-available",
|
|
136
|
-
toolCallId
|
|
139
|
+
toolCallId,
|
|
137
140
|
output: tool2.output
|
|
138
141
|
});
|
|
139
142
|
}
|
|
140
143
|
};
|
|
141
144
|
this.writeObjectAsTool = (tool2) => {
|
|
142
|
-
if (!tool2.result
|
|
145
|
+
if (!tool2.result?.object) {
|
|
143
146
|
throw new Error("No object found in the GenerateObjectResult");
|
|
144
147
|
}
|
|
145
148
|
const toolCallId = tool2.toolName.toString() + "-" + this.generateId();
|
|
@@ -151,17 +154,17 @@ var StreamWriter = class {
|
|
|
151
154
|
this.writer.write({
|
|
152
155
|
type: "tool-input-available",
|
|
153
156
|
toolCallId,
|
|
154
|
-
input: {
|
|
155
|
-
usage: tool2.result
|
|
156
|
-
warnings: tool2.result
|
|
157
|
-
finishReason: tool2.result
|
|
158
|
-
},
|
|
157
|
+
input: tool2.input ?? tool2.result ? {
|
|
158
|
+
usage: tool2.result?.usage,
|
|
159
|
+
warnings: tool2.result?.warnings,
|
|
160
|
+
finishReason: tool2.result?.finishReason
|
|
161
|
+
} : void 0,
|
|
159
162
|
toolName: tool2.toolName
|
|
160
163
|
});
|
|
161
164
|
this.writer.write({
|
|
162
165
|
type: "tool-output-available",
|
|
163
166
|
toolCallId,
|
|
164
|
-
output: tool2.result
|
|
167
|
+
output: tool2.result?.object
|
|
165
168
|
});
|
|
166
169
|
};
|
|
167
170
|
this.writer = writer;
|
|
@@ -189,6 +192,90 @@ var findLastMessageWith = (message, filters) => {
|
|
|
189
192
|
|
|
190
193
|
// src/router.ts
|
|
191
194
|
var import_path = __toESM(require("path"));
|
|
195
|
+
|
|
196
|
+
// src/store.ts
|
|
197
|
+
var MemoryStore = class {
|
|
198
|
+
constructor() {
|
|
199
|
+
this.store = /* @__PURE__ */ new Map();
|
|
200
|
+
}
|
|
201
|
+
async get(key) {
|
|
202
|
+
return this.store.get(key);
|
|
203
|
+
}
|
|
204
|
+
async set(key, value) {
|
|
205
|
+
this.store.set(key, value);
|
|
206
|
+
}
|
|
207
|
+
async delete(key) {
|
|
208
|
+
this.store.delete(key);
|
|
209
|
+
}
|
|
210
|
+
async has(key) {
|
|
211
|
+
return this.store.has(key);
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
// src/router.ts
|
|
216
|
+
var globalLogger = void 0;
|
|
217
|
+
function setGlobalLogger(logger) {
|
|
218
|
+
globalLogger = logger;
|
|
219
|
+
}
|
|
220
|
+
function getGlobalLogger() {
|
|
221
|
+
return globalLogger;
|
|
222
|
+
}
|
|
223
|
+
function clubParts(parts) {
|
|
224
|
+
if (!parts || parts.length === 0) return parts;
|
|
225
|
+
const clubbedParts = [];
|
|
226
|
+
const toolCallIdGroups = /* @__PURE__ */ new Map();
|
|
227
|
+
const dataIdGroups = /* @__PURE__ */ new Map();
|
|
228
|
+
for (const part of parts) {
|
|
229
|
+
if (part.type?.startsWith("tool-") && part.toolCallId) {
|
|
230
|
+
const toolCallId = part.toolCallId;
|
|
231
|
+
if (!toolCallIdGroups.has(toolCallId)) {
|
|
232
|
+
toolCallIdGroups.set(toolCallId, []);
|
|
233
|
+
}
|
|
234
|
+
toolCallIdGroups.get(toolCallId).push(part);
|
|
235
|
+
} else if (part.type?.startsWith("data-") && part.id) {
|
|
236
|
+
const id = part.id;
|
|
237
|
+
if (!dataIdGroups.has(id)) {
|
|
238
|
+
dataIdGroups.set(id, []);
|
|
239
|
+
}
|
|
240
|
+
dataIdGroups.get(id).push(part);
|
|
241
|
+
} else {
|
|
242
|
+
clubbedParts.push(part);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
for (const [toolCallId, toolParts] of toolCallIdGroups) {
|
|
246
|
+
if (toolParts.length === 1) {
|
|
247
|
+
clubbedParts.push(toolParts[0]);
|
|
248
|
+
} else {
|
|
249
|
+
const mergedPart = { ...toolParts[0] };
|
|
250
|
+
for (let i = 1; i < toolParts.length; i++) {
|
|
251
|
+
const currentPart = toolParts[i];
|
|
252
|
+
Object.keys(currentPart).forEach((key) => {
|
|
253
|
+
if (key !== "type" && key !== "toolCallId") {
|
|
254
|
+
mergedPart[key] = currentPart[key];
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
clubbedParts.push(mergedPart);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
for (const [id, dataParts] of dataIdGroups) {
|
|
262
|
+
if (dataParts.length === 1) {
|
|
263
|
+
clubbedParts.push(dataParts[0]);
|
|
264
|
+
} else {
|
|
265
|
+
const mergedPart = { ...dataParts[0] };
|
|
266
|
+
for (let i = 1; i < dataParts.length; i++) {
|
|
267
|
+
const currentPart = dataParts[i];
|
|
268
|
+
Object.keys(currentPart).forEach((key) => {
|
|
269
|
+
if (key !== "type" && key !== "id") {
|
|
270
|
+
mergedPart[key] = currentPart[key];
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
clubbedParts.push(mergedPart);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return clubbedParts;
|
|
278
|
+
}
|
|
192
279
|
var AiKitError = class extends Error {
|
|
193
280
|
constructor(message) {
|
|
194
281
|
super(message);
|
|
@@ -268,14 +355,14 @@ var AiRouter = class _AiRouter {
|
|
|
268
355
|
constructor(stack, options) {
|
|
269
356
|
this.stack = [];
|
|
270
357
|
this.actAsToolDefinitions = /* @__PURE__ */ new Map();
|
|
271
|
-
this.logger =
|
|
358
|
+
this.logger = void 0;
|
|
359
|
+
this._store = new MemoryStore();
|
|
360
|
+
this.toolExecutionPromise = Promise.resolve();
|
|
272
361
|
/** Configuration options for the router instance. */
|
|
273
362
|
this.options = {
|
|
274
363
|
maxCallDepth: 10
|
|
275
364
|
};
|
|
276
365
|
this.pendingExecutions = 0;
|
|
277
|
-
this.logger = options?.logger ?? console;
|
|
278
|
-
this.logger.log("AiAgentKit v3 initialized.");
|
|
279
366
|
if (stack) {
|
|
280
367
|
this.stack = stack;
|
|
281
368
|
}
|
|
@@ -283,6 +370,25 @@ var AiRouter = class _AiRouter {
|
|
|
283
370
|
this.options.maxCallDepth = options.maxCallDepth;
|
|
284
371
|
}
|
|
285
372
|
}
|
|
373
|
+
setStore(store) {
|
|
374
|
+
this._store = store;
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Sets a logger for this router instance.
|
|
378
|
+
* If no logger is set, the router will fall back to the global logger.
|
|
379
|
+
* @param logger The logger to use for this router instance, or undefined to use global logger
|
|
380
|
+
*/
|
|
381
|
+
setLogger(logger) {
|
|
382
|
+
this.logger = logger;
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Gets the effective logger for this router instance.
|
|
386
|
+
* Returns instance logger if set, otherwise falls back to global logger.
|
|
387
|
+
* @returns The effective logger or undefined if no logging should occur
|
|
388
|
+
*/
|
|
389
|
+
_getEffectiveLogger() {
|
|
390
|
+
return this.logger ?? globalLogger;
|
|
391
|
+
}
|
|
286
392
|
/**
|
|
287
393
|
* Registers a middleware-style agent that runs for a specific path prefix, regex pattern, or wildcard.
|
|
288
394
|
* Agents can modify the context and must call `next()` to pass control to the next handler in the chain.
|
|
@@ -303,7 +409,7 @@ var AiRouter = class _AiRouter {
|
|
|
303
409
|
if (handler instanceof _AiRouter && typeof prefix === "string") {
|
|
304
410
|
const stackToMount = handler.getStackWithPrefix(prefix);
|
|
305
411
|
this.stack.push(...stackToMount);
|
|
306
|
-
this.logger
|
|
412
|
+
this.logger?.log(
|
|
307
413
|
`Router mounted: path=${prefix}, layers=${stackToMount.length}`
|
|
308
414
|
);
|
|
309
415
|
const mountPath = prefix.toString();
|
|
@@ -323,7 +429,7 @@ var AiRouter = class _AiRouter {
|
|
|
323
429
|
isAgent: true
|
|
324
430
|
// Mark as an agent
|
|
325
431
|
});
|
|
326
|
-
this.logger
|
|
432
|
+
this.logger?.log(`Agent registered: path=${prefix}`);
|
|
327
433
|
}
|
|
328
434
|
return this;
|
|
329
435
|
}
|
|
@@ -374,19 +480,62 @@ var AiRouter = class _AiRouter {
|
|
|
374
480
|
*/
|
|
375
481
|
actAsTool(path2, options) {
|
|
376
482
|
this.actAsToolDefinitions.set(path2, options);
|
|
377
|
-
this.logger
|
|
378
|
-
|
|
379
|
-
);
|
|
380
|
-
this.logger.log(
|
|
483
|
+
this.logger?.log(`[actAsTool] Added definition: at path ${path2}`);
|
|
484
|
+
this.logger?.log(
|
|
381
485
|
`[actAsTool] Router now has ${this.actAsToolDefinitions.size} definitions`
|
|
382
486
|
);
|
|
383
487
|
return this;
|
|
384
488
|
}
|
|
489
|
+
getToolSet() {
|
|
490
|
+
let allTools = Array.from(this.actAsToolDefinitions.entries()).map(
|
|
491
|
+
([key, value]) => {
|
|
492
|
+
return {
|
|
493
|
+
...value,
|
|
494
|
+
metadata: {
|
|
495
|
+
...value.metadata,
|
|
496
|
+
absolutePath: key
|
|
497
|
+
}
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
);
|
|
501
|
+
return allTools.reduce((acc, _tool) => {
|
|
502
|
+
const { inputSchema, outputSchema } = _tool;
|
|
503
|
+
acc[_tool.id] = {
|
|
504
|
+
...(0, import_ai.tool)(
|
|
505
|
+
_tool
|
|
506
|
+
),
|
|
507
|
+
metadata: {
|
|
508
|
+
..._tool.metadata,
|
|
509
|
+
toolKey: _tool.id,
|
|
510
|
+
name: _tool.name,
|
|
511
|
+
description: _tool.description
|
|
512
|
+
}
|
|
513
|
+
};
|
|
514
|
+
return acc;
|
|
515
|
+
}, {});
|
|
516
|
+
}
|
|
517
|
+
getToolDefinition(path2) {
|
|
518
|
+
let definition = this.actAsToolDefinitions.get(path2);
|
|
519
|
+
if (!definition) {
|
|
520
|
+
this.logger?.error(
|
|
521
|
+
`[getToolDefinition] No definition found for path: ${path2}`
|
|
522
|
+
);
|
|
523
|
+
throw new AgentDefinitionMissingError(path2);
|
|
524
|
+
}
|
|
525
|
+
return definition;
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* @deprecated Use agent-as-tools pattern instead. Create an agent and use `.actAsTool()` to expose it as a tool.
|
|
529
|
+
* This method will be removed in a future version.
|
|
530
|
+
*/
|
|
385
531
|
// Implementation
|
|
386
532
|
tool(path2, optionsOrFactory, handler) {
|
|
387
|
-
this.logger
|
|
533
|
+
this.logger?.warn(
|
|
534
|
+
`[DEPRECATION WARNING] router.tool() is deprecated and will be removed in a future version. Please migrate to the agent-as-tools pattern: router.agent('${path2}', async (ctx) => {...}).actAsTool('${path2}', {...})`
|
|
535
|
+
);
|
|
536
|
+
this.logger?.log(`[AiAgentKit][tool] Registering tool at path:`, path2);
|
|
388
537
|
if (this.stack.some((l) => l.isTool && l.path === path2)) {
|
|
389
|
-
this.logger
|
|
538
|
+
this.logger?.error(
|
|
390
539
|
`[AiAgentKit][tool] Tool already registered for path: ${path2}`
|
|
391
540
|
);
|
|
392
541
|
throw new AiKitError(`A tool is already registered for path: ${path2}`);
|
|
@@ -395,7 +544,7 @@ var AiRouter = class _AiRouter {
|
|
|
395
544
|
const factory = optionsOrFactory;
|
|
396
545
|
const isDynamicPath = typeof path2 === "string" && hasDynamicParams(path2);
|
|
397
546
|
const toolMiddleware = async (ctx, _next) => {
|
|
398
|
-
this.logger
|
|
547
|
+
this.logger?.log(
|
|
399
548
|
`[Tool Middleware] Executing factory for path "${path2}". Messages in context: ${ctx.request.messages?.length ?? "undefined"}`
|
|
400
549
|
);
|
|
401
550
|
const toolObject = factory(ctx);
|
|
@@ -406,14 +555,14 @@ var AiRouter = class _AiRouter {
|
|
|
406
555
|
}
|
|
407
556
|
const schema = toolObject.inputSchema;
|
|
408
557
|
if (!schema) {
|
|
409
|
-
this.logger
|
|
558
|
+
this.logger?.warn(
|
|
410
559
|
`[AiAgentKit][tool] Factory-based tool at path ${path2} has no inputSchema. Executing without params.`
|
|
411
560
|
);
|
|
412
561
|
return toolObject.execute({}, {});
|
|
413
562
|
}
|
|
414
563
|
const parsedParams = schema.safeParse(ctx.request.params);
|
|
415
564
|
if (!parsedParams.success) {
|
|
416
|
-
this.logger
|
|
565
|
+
this.logger?.error(
|
|
417
566
|
`[AiAgentKit][tool] Tool call validation failed for path: ${path2}:`,
|
|
418
567
|
parsedParams.error.message
|
|
419
568
|
);
|
|
@@ -435,7 +584,7 @@ var AiRouter = class _AiRouter {
|
|
|
435
584
|
isAgent: false,
|
|
436
585
|
hasDynamicParams: isDynamicPath
|
|
437
586
|
});
|
|
438
|
-
this.logger
|
|
587
|
+
this.logger?.log(
|
|
439
588
|
`Tool registered: path=${path2}, type=factory${isDynamicPath ? " (dynamic)" : ""}`
|
|
440
589
|
);
|
|
441
590
|
return this;
|
|
@@ -447,7 +596,7 @@ var AiRouter = class _AiRouter {
|
|
|
447
596
|
const toolMiddleware = async (ctx, _next) => {
|
|
448
597
|
if (isDynamicPath && typeof path2 === "string") {
|
|
449
598
|
const pathParams = extractPathParams(path2, ctx.request.path || "");
|
|
450
|
-
this.logger
|
|
599
|
+
this.logger?.log(
|
|
451
600
|
`[AiAgentKit][tool] Extracted dynamic path params:`,
|
|
452
601
|
pathParams
|
|
453
602
|
);
|
|
@@ -460,7 +609,7 @@ var AiRouter = class _AiRouter {
|
|
|
460
609
|
}
|
|
461
610
|
const parsedParams = options.schema.safeParse(ctx.request.params);
|
|
462
611
|
if (!parsedParams.success) {
|
|
463
|
-
this.logger
|
|
612
|
+
this.logger?.error(
|
|
464
613
|
`[AiAgentKit][tool] Tool call validation failed for path: ${path2}:`,
|
|
465
614
|
parsedParams.error.message
|
|
466
615
|
);
|
|
@@ -483,12 +632,12 @@ var AiRouter = class _AiRouter {
|
|
|
483
632
|
hasDynamicParams: isDynamicPath,
|
|
484
633
|
paramNames: dynamicParamInfo?.paramNames
|
|
485
634
|
});
|
|
486
|
-
this.logger
|
|
635
|
+
this.logger?.log(
|
|
487
636
|
`Tool registered: path=${path2}, type=static${isDynamicPath ? " (dynamic)" : ""}`
|
|
488
637
|
);
|
|
489
638
|
return this;
|
|
490
639
|
}
|
|
491
|
-
this.logger
|
|
640
|
+
this.logger?.error(
|
|
492
641
|
`[AiAgentKit][tool] Invalid arguments for tool registration at path: ${path2}`
|
|
493
642
|
);
|
|
494
643
|
throw new AiKitError(
|
|
@@ -523,6 +672,54 @@ var AiRouter = class _AiRouter {
|
|
|
523
672
|
};
|
|
524
673
|
});
|
|
525
674
|
}
|
|
675
|
+
/**
|
|
676
|
+
* Outputs all registered paths, and the tool definitions, middlewares, and agents registered on each path.
|
|
677
|
+
* @returns A map of paths to their registered handlers.
|
|
678
|
+
*/
|
|
679
|
+
registry() {
|
|
680
|
+
const registryMap = {};
|
|
681
|
+
for (const layer of this.stack) {
|
|
682
|
+
const pathKey = layer.path.toString();
|
|
683
|
+
if (!registryMap[pathKey]) {
|
|
684
|
+
registryMap[pathKey] = { middlewares: [], tools: [], agents: [] };
|
|
685
|
+
}
|
|
686
|
+
if (layer.isTool) {
|
|
687
|
+
let toolInfo = { type: layer.toolOptions?.type };
|
|
688
|
+
if (layer.toolOptions?.type === "static") {
|
|
689
|
+
toolInfo = {
|
|
690
|
+
...toolInfo,
|
|
691
|
+
schema: layer.toolOptions.schema,
|
|
692
|
+
description: layer.toolOptions.description
|
|
693
|
+
};
|
|
694
|
+
} else if (layer.toolOptions?.type === "factory") {
|
|
695
|
+
toolInfo = {
|
|
696
|
+
...toolInfo,
|
|
697
|
+
factory: layer.toolOptions.factory.toString()
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
registryMap[pathKey].tools.push(toolInfo);
|
|
701
|
+
} else if (layer.isAgent) {
|
|
702
|
+
const agentInfo = {
|
|
703
|
+
handler: layer.handler.name || "anonymous"
|
|
704
|
+
};
|
|
705
|
+
const actAsToolDef = this.actAsToolDefinitions.get(layer.path);
|
|
706
|
+
if (actAsToolDef) {
|
|
707
|
+
agentInfo.actAsTool = {
|
|
708
|
+
...actAsToolDef
|
|
709
|
+
};
|
|
710
|
+
}
|
|
711
|
+
registryMap[pathKey].agents.push(agentInfo);
|
|
712
|
+
} else {
|
|
713
|
+
registryMap[pathKey].middlewares.push({
|
|
714
|
+
handler: layer.handler.name || "anonymous"
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
return {
|
|
719
|
+
map: registryMap,
|
|
720
|
+
tools: this.getToolSet()
|
|
721
|
+
};
|
|
722
|
+
}
|
|
526
723
|
/**
|
|
527
724
|
* Resolves a path based on the parent path and the requested path.
|
|
528
725
|
* - If path starts with `@/`, it's an absolute path from the root.
|
|
@@ -549,6 +746,7 @@ var AiRouter = class _AiRouter {
|
|
|
549
746
|
// State is passed by reference to allow sub-agents to modify the parent's state.
|
|
550
747
|
// The execution context is a shallow copy to ensure call-specific data is isolated.
|
|
551
748
|
state: parentCtx.state,
|
|
749
|
+
store: parentCtx.store,
|
|
552
750
|
executionContext: {
|
|
553
751
|
...parentCtx.executionContext,
|
|
554
752
|
currentPath: options.path,
|
|
@@ -584,13 +782,24 @@ var AiRouter = class _AiRouter {
|
|
|
584
782
|
* @internal
|
|
585
783
|
*/
|
|
586
784
|
_createLogger(requestId, path2, callDepth = 0) {
|
|
785
|
+
const effectiveLogger = this._getEffectiveLogger();
|
|
786
|
+
if (!effectiveLogger) {
|
|
787
|
+
return {
|
|
788
|
+
log: () => {
|
|
789
|
+
},
|
|
790
|
+
warn: () => {
|
|
791
|
+
},
|
|
792
|
+
error: () => {
|
|
793
|
+
}
|
|
794
|
+
};
|
|
795
|
+
}
|
|
587
796
|
const indent = " ".repeat(callDepth);
|
|
588
797
|
const prefix = `${indent}[${path2.toString()}]`;
|
|
589
798
|
const fullPrefix = `[${requestId}]${prefix}`;
|
|
590
799
|
return {
|
|
591
|
-
log: (...args) =>
|
|
592
|
-
warn: (...args) =>
|
|
593
|
-
error: (...args) =>
|
|
800
|
+
log: (...args) => effectiveLogger.log(fullPrefix, ...args),
|
|
801
|
+
warn: (...args) => effectiveLogger.warn(fullPrefix, ...args),
|
|
802
|
+
error: (...args) => effectiveLogger.error(fullPrefix, ...args)
|
|
594
803
|
};
|
|
595
804
|
}
|
|
596
805
|
/**
|
|
@@ -725,83 +934,162 @@ var AiRouter = class _AiRouter {
|
|
|
725
934
|
* @returns A standard `Response` object containing the rich UI stream.
|
|
726
935
|
*/
|
|
727
936
|
handle(path2, initialContext) {
|
|
728
|
-
this.logger
|
|
937
|
+
this.logger?.log(`Handling request for path: ${path2}`);
|
|
729
938
|
const self = this;
|
|
730
939
|
let executionCompletionResolver = null;
|
|
731
940
|
const executionCompletionPromise = new Promise((resolve) => {
|
|
732
941
|
executionCompletionResolver = resolve;
|
|
733
942
|
});
|
|
734
943
|
return (0, import_ai.createUIMessageStreamResponse)({
|
|
735
|
-
stream:
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
944
|
+
stream: self.handleStream(
|
|
945
|
+
path2,
|
|
946
|
+
initialContext,
|
|
947
|
+
executionCompletionPromise,
|
|
948
|
+
executionCompletionResolver
|
|
949
|
+
)
|
|
950
|
+
});
|
|
951
|
+
}
|
|
952
|
+
handleStream(path2, initialContext, executionCompletionPromise, executionCompletionResolver) {
|
|
953
|
+
const self = this;
|
|
954
|
+
return (0, import_ai.createUIMessageStream)({
|
|
955
|
+
originalMessages: initialContext.request.messages,
|
|
956
|
+
execute: async ({ writer }) => {
|
|
957
|
+
const streamWriter = new StreamWriter(writer);
|
|
958
|
+
const requestId = (0, import_ai.generateId)();
|
|
959
|
+
const store = self._store instanceof MemoryStore ? new MemoryStore() : self._store;
|
|
960
|
+
const ctx = {
|
|
961
|
+
...initialContext,
|
|
962
|
+
request: {
|
|
963
|
+
...initialContext.request,
|
|
964
|
+
path: path2
|
|
965
|
+
// Set the initial path for the root context
|
|
966
|
+
},
|
|
967
|
+
state: {},
|
|
968
|
+
store,
|
|
969
|
+
executionContext: { currentPath: path2, callDepth: 0 },
|
|
970
|
+
requestId,
|
|
971
|
+
logger: self._createLogger(requestId, path2, 0),
|
|
972
|
+
response: {
|
|
973
|
+
...streamWriter.writer,
|
|
974
|
+
writeMessageMetadata: streamWriter.writeMessageMetadata,
|
|
975
|
+
writeCustomTool: streamWriter.writeCustomTool,
|
|
976
|
+
writeObjectAsTool: streamWriter.writeObjectAsTool,
|
|
977
|
+
generateId: import_ai.generateId
|
|
978
|
+
},
|
|
979
|
+
next: void 0,
|
|
980
|
+
// Will be replaced right after
|
|
981
|
+
_onExecutionStart: () => {
|
|
982
|
+
self.pendingExecutions++;
|
|
983
|
+
self.logger?.log(
|
|
984
|
+
`[AiAgentKit][lifecycle] Execution started. Pending: ${self.pendingExecutions}`
|
|
985
|
+
);
|
|
986
|
+
},
|
|
987
|
+
_onExecutionEnd: () => {
|
|
988
|
+
self.pendingExecutions--;
|
|
989
|
+
self.logger?.log(
|
|
990
|
+
`[AiAgentKit][lifecycle] Execution ended. Pending: ${self.pendingExecutions}`
|
|
991
|
+
);
|
|
992
|
+
if (self.pendingExecutions === 0 && executionCompletionResolver) {
|
|
993
|
+
self.logger?.log(
|
|
994
|
+
`[AiAgentKit][lifecycle] All executions finished. Resolving promise.`
|
|
770
995
|
);
|
|
771
|
-
|
|
772
|
-
self.logger.log(
|
|
773
|
-
`[AiAgentKit][lifecycle] All executions finished. Resolving promise.`
|
|
774
|
-
);
|
|
775
|
-
executionCompletionResolver();
|
|
776
|
-
}
|
|
996
|
+
executionCompletionResolver();
|
|
777
997
|
}
|
|
778
|
-
};
|
|
779
|
-
ctx.next = new NextHandler(
|
|
780
|
-
ctx,
|
|
781
|
-
self,
|
|
782
|
-
ctx._onExecutionStart,
|
|
783
|
-
ctx._onExecutionEnd
|
|
784
|
-
);
|
|
785
|
-
ctx._onExecutionStart();
|
|
786
|
-
self.logger.log(
|
|
787
|
-
`[AiAgentKit][lifecycle] Main execution chain started.`
|
|
788
|
-
);
|
|
789
|
-
try {
|
|
790
|
-
await self._execute(path2, ctx);
|
|
791
|
-
} catch (err) {
|
|
792
|
-
ctx.logger.error("Unhandled error in main execution chain", err);
|
|
793
|
-
} finally {
|
|
794
|
-
ctx._onExecutionEnd();
|
|
795
|
-
self.logger.log(
|
|
796
|
-
`[AiAgentKit][lifecycle] Main execution chain finished.`
|
|
797
|
-
);
|
|
798
998
|
}
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
999
|
+
};
|
|
1000
|
+
ctx.next = new NextHandler(
|
|
1001
|
+
ctx,
|
|
1002
|
+
self,
|
|
1003
|
+
ctx._onExecutionStart,
|
|
1004
|
+
ctx._onExecutionEnd
|
|
1005
|
+
);
|
|
1006
|
+
ctx._onExecutionStart();
|
|
1007
|
+
self.logger?.log(
|
|
1008
|
+
`[AiAgentKit][lifecycle] Main execution chain started.`
|
|
1009
|
+
);
|
|
1010
|
+
try {
|
|
1011
|
+
const response = await self._execute(path2, ctx);
|
|
1012
|
+
const toolDefinition = this.actAsToolDefinitions.get(path2);
|
|
1013
|
+
if (toolDefinition && !toolDefinition.metadata?.hideUI) {
|
|
1014
|
+
ctx.response.writeCustomTool({
|
|
1015
|
+
toolName: toolDefinition.id,
|
|
1016
|
+
toolCallId: toolDefinition.id + "-" + ctx.response.generateId(),
|
|
1017
|
+
output: response
|
|
1018
|
+
});
|
|
1019
|
+
}
|
|
1020
|
+
return response;
|
|
1021
|
+
} catch (err) {
|
|
1022
|
+
ctx.logger.error("Unhandled error in main execution chain", err);
|
|
1023
|
+
} finally {
|
|
1024
|
+
ctx._onExecutionEnd();
|
|
1025
|
+
self.logger?.log(
|
|
1026
|
+
`[AiAgentKit][lifecycle] Main execution chain finished.`
|
|
802
1027
|
);
|
|
803
1028
|
}
|
|
804
|
-
|
|
1029
|
+
await executionCompletionPromise;
|
|
1030
|
+
self.logger?.log(
|
|
1031
|
+
`[AiAgentKit][lifecycle] All executions truly finished. Stream can be safely closed.`
|
|
1032
|
+
);
|
|
1033
|
+
}
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
1036
|
+
/**
|
|
1037
|
+
* Handles an incoming request and returns a promise that resolves with the full,
|
|
1038
|
+
* non-streamed response. This is useful for environments where streaming is not
|
|
1039
|
+
* desired or for testing.
|
|
1040
|
+
*
|
|
1041
|
+
* @param path The path of the agent or tool to execute.
|
|
1042
|
+
* @param initialContext The initial context for the request, typically containing messages.
|
|
1043
|
+
* @returns A `Promise<Response>` that resolves with the final JSON response.
|
|
1044
|
+
*/
|
|
1045
|
+
async toAwaitResponse(path2, initialContext) {
|
|
1046
|
+
this.logger?.log(`Handling request for path: ${path2}`);
|
|
1047
|
+
const self = this;
|
|
1048
|
+
let executionCompletionResolver = null;
|
|
1049
|
+
const executionCompletionPromise = new Promise((resolve) => {
|
|
1050
|
+
executionCompletionResolver = resolve;
|
|
1051
|
+
});
|
|
1052
|
+
const stream = this.handleStream(
|
|
1053
|
+
path2,
|
|
1054
|
+
initialContext,
|
|
1055
|
+
executionCompletionPromise,
|
|
1056
|
+
executionCompletionResolver
|
|
1057
|
+
);
|
|
1058
|
+
const messageStream = (0, import_ai.readUIMessageStream)({
|
|
1059
|
+
stream,
|
|
1060
|
+
onError: (error) => {
|
|
1061
|
+
this.logger?.error("Error reading UI message stream", error);
|
|
1062
|
+
}
|
|
1063
|
+
});
|
|
1064
|
+
let finalMessages = [];
|
|
1065
|
+
const thisMessageId = (0, import_ai.generateId)();
|
|
1066
|
+
for await (const message of messageStream) {
|
|
1067
|
+
if (message.id?.length > 0) {
|
|
1068
|
+
finalMessages.push(message);
|
|
1069
|
+
} else if (finalMessages.find((m) => m.id === thisMessageId)) {
|
|
1070
|
+
finalMessages = finalMessages.map(
|
|
1071
|
+
(m) => m.id === thisMessageId ? {
|
|
1072
|
+
...m,
|
|
1073
|
+
metadata: {
|
|
1074
|
+
...m.metadata ?? {},
|
|
1075
|
+
...message.metadata ?? {}
|
|
1076
|
+
},
|
|
1077
|
+
parts: clubParts([
|
|
1078
|
+
...m.parts ?? [],
|
|
1079
|
+
...message.parts ?? []
|
|
1080
|
+
])
|
|
1081
|
+
} : m
|
|
1082
|
+
);
|
|
1083
|
+
} else {
|
|
1084
|
+
finalMessages.push({
|
|
1085
|
+
...message,
|
|
1086
|
+
id: thisMessageId
|
|
1087
|
+
});
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
const responseBody = JSON.stringify(finalMessages);
|
|
1091
|
+
return new Response(responseBody, {
|
|
1092
|
+
headers: { "Content-Type": "application/json" }
|
|
805
1093
|
});
|
|
806
1094
|
}
|
|
807
1095
|
};
|
|
@@ -831,14 +1119,30 @@ var NextHandler = class {
|
|
|
831
1119
|
const subContext = this.router._createSubContext(this.ctx, {
|
|
832
1120
|
type: "agent",
|
|
833
1121
|
path: resolvedPath,
|
|
834
|
-
params,
|
|
1122
|
+
params: params ?? {},
|
|
835
1123
|
messages: this.ctx.request.messages
|
|
836
1124
|
});
|
|
1125
|
+
const definition = this.router.actAsToolDefinitions.get(resolvedPath);
|
|
1126
|
+
const toolCallId = definition?.id + "-" + this.ctx.response.generateId();
|
|
1127
|
+
if (options?.streamToUI && definition) {
|
|
1128
|
+
this.ctx.response.writeCustomTool({
|
|
1129
|
+
toolName: definition?.id,
|
|
1130
|
+
toolCallId,
|
|
1131
|
+
input: subContext.request.params
|
|
1132
|
+
});
|
|
1133
|
+
}
|
|
837
1134
|
const data = await this.router._execute(
|
|
838
1135
|
resolvedPath,
|
|
839
1136
|
subContext,
|
|
840
1137
|
true
|
|
841
1138
|
);
|
|
1139
|
+
if (options?.streamToUI && definition) {
|
|
1140
|
+
this.ctx.response.writeCustomTool({
|
|
1141
|
+
toolName: definition?.id,
|
|
1142
|
+
toolCallId,
|
|
1143
|
+
output: data
|
|
1144
|
+
});
|
|
1145
|
+
}
|
|
842
1146
|
return { ok: true, data };
|
|
843
1147
|
} catch (error) {
|
|
844
1148
|
this.ctx.logger.error(`[callAgent] Error:`, error);
|
|
@@ -847,7 +1151,23 @@ var NextHandler = class {
|
|
|
847
1151
|
this.onExecutionEnd();
|
|
848
1152
|
}
|
|
849
1153
|
}
|
|
1154
|
+
/**
|
|
1155
|
+
* @deprecated Use agent-as-tools pattern instead. Use `ctx.next.callAgent()` to call agents that are exposed as tools.
|
|
1156
|
+
* This method will be removed in a future version.
|
|
1157
|
+
*
|
|
1158
|
+
* Example migration:
|
|
1159
|
+
* ```typescript
|
|
1160
|
+
* // Old way (deprecated):
|
|
1161
|
+
* const result = await ctx.next.callTool('/calculator', { a: 5, b: 3 });
|
|
1162
|
+
*
|
|
1163
|
+
* // New way (recommended):
|
|
1164
|
+
* const result = await ctx.next.callAgent('/calculator', { a: 5, b: 3 });
|
|
1165
|
+
* ```
|
|
1166
|
+
*/
|
|
850
1167
|
async callTool(toolPath, params, options) {
|
|
1168
|
+
this.ctx.logger.warn(
|
|
1169
|
+
`[DEPRECATION WARNING] ctx.next.callTool() is deprecated and will be removed in a future version. Please use ctx.next.callAgent() instead to call agents that are exposed as tools.`
|
|
1170
|
+
);
|
|
851
1171
|
this.onExecutionStart();
|
|
852
1172
|
try {
|
|
853
1173
|
const parentPath = this.ctx.executionContext.currentPath || "/";
|
|
@@ -859,7 +1179,7 @@ var NextHandler = class {
|
|
|
859
1179
|
const subContext = this.router._createSubContext(this.ctx, {
|
|
860
1180
|
type: "tool",
|
|
861
1181
|
path: resolvedPath,
|
|
862
|
-
params,
|
|
1182
|
+
params: params ?? {},
|
|
863
1183
|
messages: this.ctx.request.messages
|
|
864
1184
|
});
|
|
865
1185
|
const data = await this.router._execute(
|
|
@@ -875,7 +1195,23 @@ var NextHandler = class {
|
|
|
875
1195
|
this.onExecutionEnd();
|
|
876
1196
|
}
|
|
877
1197
|
}
|
|
1198
|
+
/**
|
|
1199
|
+
* @deprecated Use agent-as-tools pattern instead. Use `ctx.next.agentAsTool()` to attach agents as tools.
|
|
1200
|
+
* This method will be removed in a future version.
|
|
1201
|
+
*
|
|
1202
|
+
* Example migration:
|
|
1203
|
+
* ```typescript
|
|
1204
|
+
* // Old way (deprecated):
|
|
1205
|
+
* const tool = ctx.next.attachTool('/calculator');
|
|
1206
|
+
*
|
|
1207
|
+
* // New way (recommended):
|
|
1208
|
+
* const tool = ctx.next.agentAsTool('/calculator');
|
|
1209
|
+
* ```
|
|
1210
|
+
*/
|
|
878
1211
|
attachTool(toolPath, _tool) {
|
|
1212
|
+
this.ctx.logger.warn(
|
|
1213
|
+
`[DEPRECATION WARNING] ctx.next.attachTool() is deprecated and will be removed in a future version. Please use ctx.next.agentAsTool() instead to attach agents as tools.`
|
|
1214
|
+
);
|
|
879
1215
|
const parentPath = this.ctx.executionContext.currentPath || "/";
|
|
880
1216
|
const resolvedPath = this.router._resolvePath(
|
|
881
1217
|
parentPath,
|
|
@@ -953,14 +1289,81 @@ var NextHandler = class {
|
|
|
953
1289
|
);
|
|
954
1290
|
throw new AgentDefinitionMissingError(resolvedPath);
|
|
955
1291
|
}
|
|
1292
|
+
const { id, metadata, ...restDefinition } = definition;
|
|
956
1293
|
return {
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
1294
|
+
[id]: {
|
|
1295
|
+
...restDefinition,
|
|
1296
|
+
metadata: {
|
|
1297
|
+
...metadata,
|
|
1298
|
+
toolKey: id,
|
|
1299
|
+
name: restDefinition.name,
|
|
1300
|
+
description: restDefinition.description,
|
|
1301
|
+
absolutePath: restDefinition.path
|
|
1302
|
+
},
|
|
1303
|
+
execute: (params, options) => {
|
|
1304
|
+
const executeInternal = async () => {
|
|
1305
|
+
const result = await this.callAgent(agentPath, params, options);
|
|
1306
|
+
if (!result.ok) {
|
|
1307
|
+
throw result.error;
|
|
1308
|
+
}
|
|
1309
|
+
return result.data;
|
|
1310
|
+
};
|
|
1311
|
+
const newPromise = this.router.toolExecutionPromise.then(
|
|
1312
|
+
executeInternal,
|
|
1313
|
+
executeInternal
|
|
1314
|
+
);
|
|
1315
|
+
this.router.toolExecutionPromise = newPromise;
|
|
1316
|
+
return newPromise;
|
|
962
1317
|
}
|
|
963
|
-
|
|
1318
|
+
}
|
|
1319
|
+
};
|
|
1320
|
+
}
|
|
1321
|
+
getToolDefinition(agentPath) {
|
|
1322
|
+
const parentPath = this.ctx.executionContext.currentPath || "/";
|
|
1323
|
+
const resolvedPath = this.router._resolvePath(
|
|
1324
|
+
parentPath,
|
|
1325
|
+
agentPath
|
|
1326
|
+
);
|
|
1327
|
+
let preDefined;
|
|
1328
|
+
const pathsToTry = [resolvedPath];
|
|
1329
|
+
if (typeof agentPath === "string" && agentPath.startsWith("/")) {
|
|
1330
|
+
pathsToTry.unshift(agentPath);
|
|
1331
|
+
}
|
|
1332
|
+
for (const pathToTry of pathsToTry) {
|
|
1333
|
+
for (const [key, value] of this.router.actAsToolDefinitions) {
|
|
1334
|
+
if (typeof key === "string") {
|
|
1335
|
+
if (key === pathToTry) {
|
|
1336
|
+
preDefined = value;
|
|
1337
|
+
break;
|
|
1338
|
+
}
|
|
1339
|
+
if (extractPathParams(key, pathToTry) !== null) {
|
|
1340
|
+
preDefined = value;
|
|
1341
|
+
break;
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
if (key instanceof RegExp && key.test(pathToTry)) {
|
|
1345
|
+
preDefined = value;
|
|
1346
|
+
break;
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
if (preDefined) break;
|
|
1350
|
+
}
|
|
1351
|
+
const definition = preDefined;
|
|
1352
|
+
if (!definition) {
|
|
1353
|
+
this.ctx.logger.error(
|
|
1354
|
+
`[agentAsTool] No definition found for agent at resolved path: ${resolvedPath}`
|
|
1355
|
+
);
|
|
1356
|
+
throw new AgentDefinitionMissingError(resolvedPath);
|
|
1357
|
+
}
|
|
1358
|
+
const { metadata, ...restDefinition } = definition;
|
|
1359
|
+
return {
|
|
1360
|
+
...restDefinition,
|
|
1361
|
+
metadata: {
|
|
1362
|
+
...metadata,
|
|
1363
|
+
toolKey: restDefinition.id,
|
|
1364
|
+
name: restDefinition.name,
|
|
1365
|
+
description: restDefinition.description,
|
|
1366
|
+
absolutePath: restDefinition.path
|
|
964
1367
|
}
|
|
965
1368
|
};
|
|
966
1369
|
}
|
|
@@ -972,13 +1375,16 @@ var NextHandler = class {
|
|
|
972
1375
|
AiKitError,
|
|
973
1376
|
AiRouter,
|
|
974
1377
|
MaxCallDepthExceededError,
|
|
1378
|
+
MemoryStore,
|
|
975
1379
|
StreamWriter,
|
|
976
1380
|
ToolNotFoundError,
|
|
977
1381
|
ToolValidationError,
|
|
978
1382
|
findFirstElement,
|
|
979
1383
|
findLastElement,
|
|
980
1384
|
findLastMessageWith,
|
|
1385
|
+
getGlobalLogger,
|
|
981
1386
|
getTextParts,
|
|
982
|
-
getTextPartsContent
|
|
1387
|
+
getTextPartsContent,
|
|
1388
|
+
setGlobalLogger
|
|
983
1389
|
});
|
|
984
1390
|
//# sourceMappingURL=index.js.map
|