@microfox/ai-router 1.1.1 → 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 -61
- 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.mjs
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
import "./chunk-BJTO5JO5.mjs";
|
|
2
|
+
|
|
1
3
|
// src/router.ts
|
|
2
4
|
import {
|
|
3
5
|
createUIMessageStream,
|
|
4
6
|
createUIMessageStreamResponse,
|
|
5
7
|
generateId,
|
|
6
|
-
|
|
8
|
+
tool,
|
|
9
|
+
convertToModelMessages,
|
|
10
|
+
readUIMessageStream
|
|
7
11
|
} from "ai";
|
|
8
12
|
|
|
9
13
|
// ../../node_modules/nanoid/index.js
|
|
@@ -62,26 +66,26 @@ var StreamWriter = class {
|
|
|
62
66
|
});
|
|
63
67
|
};
|
|
64
68
|
this.writeCustomTool = (tool2) => {
|
|
65
|
-
const toolCallId = tool2.toolName?.toString() + "-" + this.generateId();
|
|
69
|
+
const toolCallId = tool2.toolCallId || tool2.toolName?.toString() + "-" + this.generateId();
|
|
66
70
|
if ("input" in tool2 && tool2.input) {
|
|
67
71
|
this.writer.write({
|
|
68
72
|
type: "tool-input-available",
|
|
69
73
|
input: tool2.input,
|
|
70
|
-
toolCallId
|
|
74
|
+
toolCallId,
|
|
71
75
|
toolName: tool2.toolName
|
|
72
76
|
});
|
|
73
77
|
}
|
|
74
78
|
if (tool2.inputTextDelta && tool2.inputTextDelta.length > 0 || "output" in tool2 && tool2.output) {
|
|
75
79
|
this.writer.write({
|
|
76
80
|
type: "tool-input-start",
|
|
77
|
-
toolCallId
|
|
81
|
+
toolCallId,
|
|
78
82
|
toolName: tool2.toolName
|
|
79
83
|
});
|
|
80
84
|
if (tool2.inputTextDelta) {
|
|
81
85
|
for (const delta of tool2.inputTextDelta) {
|
|
82
86
|
this.writer.write({
|
|
83
87
|
type: "tool-input-delta",
|
|
84
|
-
toolCallId
|
|
88
|
+
toolCallId,
|
|
85
89
|
inputTextDelta: delta
|
|
86
90
|
});
|
|
87
91
|
}
|
|
@@ -90,13 +94,13 @@ var StreamWriter = class {
|
|
|
90
94
|
if ("output" in tool2 && tool2.output) {
|
|
91
95
|
this.writer.write({
|
|
92
96
|
type: "tool-output-available",
|
|
93
|
-
toolCallId
|
|
97
|
+
toolCallId,
|
|
94
98
|
output: tool2.output
|
|
95
99
|
});
|
|
96
100
|
}
|
|
97
101
|
};
|
|
98
102
|
this.writeObjectAsTool = (tool2) => {
|
|
99
|
-
if (!tool2.result
|
|
103
|
+
if (!tool2.result?.object) {
|
|
100
104
|
throw new Error("No object found in the GenerateObjectResult");
|
|
101
105
|
}
|
|
102
106
|
const toolCallId = tool2.toolName.toString() + "-" + this.generateId();
|
|
@@ -108,17 +112,17 @@ var StreamWriter = class {
|
|
|
108
112
|
this.writer.write({
|
|
109
113
|
type: "tool-input-available",
|
|
110
114
|
toolCallId,
|
|
111
|
-
input: {
|
|
112
|
-
usage: tool2.result
|
|
113
|
-
warnings: tool2.result
|
|
114
|
-
finishReason: tool2.result
|
|
115
|
-
},
|
|
115
|
+
input: tool2.input ?? tool2.result ? {
|
|
116
|
+
usage: tool2.result?.usage,
|
|
117
|
+
warnings: tool2.result?.warnings,
|
|
118
|
+
finishReason: tool2.result?.finishReason
|
|
119
|
+
} : void 0,
|
|
116
120
|
toolName: tool2.toolName
|
|
117
121
|
});
|
|
118
122
|
this.writer.write({
|
|
119
123
|
type: "tool-output-available",
|
|
120
124
|
toolCallId,
|
|
121
|
-
output: tool2.result
|
|
125
|
+
output: tool2.result?.object
|
|
122
126
|
});
|
|
123
127
|
};
|
|
124
128
|
this.writer = writer;
|
|
@@ -146,6 +150,90 @@ var findLastMessageWith = (message, filters) => {
|
|
|
146
150
|
|
|
147
151
|
// src/router.ts
|
|
148
152
|
import path from "path";
|
|
153
|
+
|
|
154
|
+
// src/store.ts
|
|
155
|
+
var MemoryStore = class {
|
|
156
|
+
constructor() {
|
|
157
|
+
this.store = /* @__PURE__ */ new Map();
|
|
158
|
+
}
|
|
159
|
+
async get(key) {
|
|
160
|
+
return this.store.get(key);
|
|
161
|
+
}
|
|
162
|
+
async set(key, value) {
|
|
163
|
+
this.store.set(key, value);
|
|
164
|
+
}
|
|
165
|
+
async delete(key) {
|
|
166
|
+
this.store.delete(key);
|
|
167
|
+
}
|
|
168
|
+
async has(key) {
|
|
169
|
+
return this.store.has(key);
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
// src/router.ts
|
|
174
|
+
var globalLogger = void 0;
|
|
175
|
+
function setGlobalLogger(logger) {
|
|
176
|
+
globalLogger = logger;
|
|
177
|
+
}
|
|
178
|
+
function getGlobalLogger() {
|
|
179
|
+
return globalLogger;
|
|
180
|
+
}
|
|
181
|
+
function clubParts(parts) {
|
|
182
|
+
if (!parts || parts.length === 0) return parts;
|
|
183
|
+
const clubbedParts = [];
|
|
184
|
+
const toolCallIdGroups = /* @__PURE__ */ new Map();
|
|
185
|
+
const dataIdGroups = /* @__PURE__ */ new Map();
|
|
186
|
+
for (const part of parts) {
|
|
187
|
+
if (part.type?.startsWith("tool-") && part.toolCallId) {
|
|
188
|
+
const toolCallId = part.toolCallId;
|
|
189
|
+
if (!toolCallIdGroups.has(toolCallId)) {
|
|
190
|
+
toolCallIdGroups.set(toolCallId, []);
|
|
191
|
+
}
|
|
192
|
+
toolCallIdGroups.get(toolCallId).push(part);
|
|
193
|
+
} else if (part.type?.startsWith("data-") && part.id) {
|
|
194
|
+
const id = part.id;
|
|
195
|
+
if (!dataIdGroups.has(id)) {
|
|
196
|
+
dataIdGroups.set(id, []);
|
|
197
|
+
}
|
|
198
|
+
dataIdGroups.get(id).push(part);
|
|
199
|
+
} else {
|
|
200
|
+
clubbedParts.push(part);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
for (const [toolCallId, toolParts] of toolCallIdGroups) {
|
|
204
|
+
if (toolParts.length === 1) {
|
|
205
|
+
clubbedParts.push(toolParts[0]);
|
|
206
|
+
} else {
|
|
207
|
+
const mergedPart = { ...toolParts[0] };
|
|
208
|
+
for (let i = 1; i < toolParts.length; i++) {
|
|
209
|
+
const currentPart = toolParts[i];
|
|
210
|
+
Object.keys(currentPart).forEach((key) => {
|
|
211
|
+
if (key !== "type" && key !== "toolCallId") {
|
|
212
|
+
mergedPart[key] = currentPart[key];
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
clubbedParts.push(mergedPart);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
for (const [id, dataParts] of dataIdGroups) {
|
|
220
|
+
if (dataParts.length === 1) {
|
|
221
|
+
clubbedParts.push(dataParts[0]);
|
|
222
|
+
} else {
|
|
223
|
+
const mergedPart = { ...dataParts[0] };
|
|
224
|
+
for (let i = 1; i < dataParts.length; i++) {
|
|
225
|
+
const currentPart = dataParts[i];
|
|
226
|
+
Object.keys(currentPart).forEach((key) => {
|
|
227
|
+
if (key !== "type" && key !== "id") {
|
|
228
|
+
mergedPart[key] = currentPart[key];
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
clubbedParts.push(mergedPart);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return clubbedParts;
|
|
236
|
+
}
|
|
149
237
|
var AiKitError = class extends Error {
|
|
150
238
|
constructor(message) {
|
|
151
239
|
super(message);
|
|
@@ -225,14 +313,14 @@ var AiRouter = class _AiRouter {
|
|
|
225
313
|
constructor(stack, options) {
|
|
226
314
|
this.stack = [];
|
|
227
315
|
this.actAsToolDefinitions = /* @__PURE__ */ new Map();
|
|
228
|
-
this.logger =
|
|
316
|
+
this.logger = void 0;
|
|
317
|
+
this._store = new MemoryStore();
|
|
318
|
+
this.toolExecutionPromise = Promise.resolve();
|
|
229
319
|
/** Configuration options for the router instance. */
|
|
230
320
|
this.options = {
|
|
231
321
|
maxCallDepth: 10
|
|
232
322
|
};
|
|
233
323
|
this.pendingExecutions = 0;
|
|
234
|
-
this.logger = options?.logger ?? console;
|
|
235
|
-
this.logger.log("AiAgentKit v3 initialized.");
|
|
236
324
|
if (stack) {
|
|
237
325
|
this.stack = stack;
|
|
238
326
|
}
|
|
@@ -240,6 +328,25 @@ var AiRouter = class _AiRouter {
|
|
|
240
328
|
this.options.maxCallDepth = options.maxCallDepth;
|
|
241
329
|
}
|
|
242
330
|
}
|
|
331
|
+
setStore(store) {
|
|
332
|
+
this._store = store;
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Sets a logger for this router instance.
|
|
336
|
+
* If no logger is set, the router will fall back to the global logger.
|
|
337
|
+
* @param logger The logger to use for this router instance, or undefined to use global logger
|
|
338
|
+
*/
|
|
339
|
+
setLogger(logger) {
|
|
340
|
+
this.logger = logger;
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Gets the effective logger for this router instance.
|
|
344
|
+
* Returns instance logger if set, otherwise falls back to global logger.
|
|
345
|
+
* @returns The effective logger or undefined if no logging should occur
|
|
346
|
+
*/
|
|
347
|
+
_getEffectiveLogger() {
|
|
348
|
+
return this.logger ?? globalLogger;
|
|
349
|
+
}
|
|
243
350
|
/**
|
|
244
351
|
* Registers a middleware-style agent that runs for a specific path prefix, regex pattern, or wildcard.
|
|
245
352
|
* Agents can modify the context and must call `next()` to pass control to the next handler in the chain.
|
|
@@ -260,7 +367,7 @@ var AiRouter = class _AiRouter {
|
|
|
260
367
|
if (handler instanceof _AiRouter && typeof prefix === "string") {
|
|
261
368
|
const stackToMount = handler.getStackWithPrefix(prefix);
|
|
262
369
|
this.stack.push(...stackToMount);
|
|
263
|
-
this.logger
|
|
370
|
+
this.logger?.log(
|
|
264
371
|
`Router mounted: path=${prefix}, layers=${stackToMount.length}`
|
|
265
372
|
);
|
|
266
373
|
const mountPath = prefix.toString();
|
|
@@ -280,7 +387,7 @@ var AiRouter = class _AiRouter {
|
|
|
280
387
|
isAgent: true
|
|
281
388
|
// Mark as an agent
|
|
282
389
|
});
|
|
283
|
-
this.logger
|
|
390
|
+
this.logger?.log(`Agent registered: path=${prefix}`);
|
|
284
391
|
}
|
|
285
392
|
return this;
|
|
286
393
|
}
|
|
@@ -331,19 +438,62 @@ var AiRouter = class _AiRouter {
|
|
|
331
438
|
*/
|
|
332
439
|
actAsTool(path2, options) {
|
|
333
440
|
this.actAsToolDefinitions.set(path2, options);
|
|
334
|
-
this.logger
|
|
335
|
-
|
|
336
|
-
);
|
|
337
|
-
this.logger.log(
|
|
441
|
+
this.logger?.log(`[actAsTool] Added definition: at path ${path2}`);
|
|
442
|
+
this.logger?.log(
|
|
338
443
|
`[actAsTool] Router now has ${this.actAsToolDefinitions.size} definitions`
|
|
339
444
|
);
|
|
340
445
|
return this;
|
|
341
446
|
}
|
|
447
|
+
getToolSet() {
|
|
448
|
+
let allTools = Array.from(this.actAsToolDefinitions.entries()).map(
|
|
449
|
+
([key, value]) => {
|
|
450
|
+
return {
|
|
451
|
+
...value,
|
|
452
|
+
metadata: {
|
|
453
|
+
...value.metadata,
|
|
454
|
+
absolutePath: key
|
|
455
|
+
}
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
);
|
|
459
|
+
return allTools.reduce((acc, _tool) => {
|
|
460
|
+
const { inputSchema, outputSchema } = _tool;
|
|
461
|
+
acc[_tool.id] = {
|
|
462
|
+
...tool(
|
|
463
|
+
_tool
|
|
464
|
+
),
|
|
465
|
+
metadata: {
|
|
466
|
+
..._tool.metadata,
|
|
467
|
+
toolKey: _tool.id,
|
|
468
|
+
name: _tool.name,
|
|
469
|
+
description: _tool.description
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
return acc;
|
|
473
|
+
}, {});
|
|
474
|
+
}
|
|
475
|
+
getToolDefinition(path2) {
|
|
476
|
+
let definition = this.actAsToolDefinitions.get(path2);
|
|
477
|
+
if (!definition) {
|
|
478
|
+
this.logger?.error(
|
|
479
|
+
`[getToolDefinition] No definition found for path: ${path2}`
|
|
480
|
+
);
|
|
481
|
+
throw new AgentDefinitionMissingError(path2);
|
|
482
|
+
}
|
|
483
|
+
return definition;
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* @deprecated Use agent-as-tools pattern instead. Create an agent and use `.actAsTool()` to expose it as a tool.
|
|
487
|
+
* This method will be removed in a future version.
|
|
488
|
+
*/
|
|
342
489
|
// Implementation
|
|
343
490
|
tool(path2, optionsOrFactory, handler) {
|
|
344
|
-
this.logger
|
|
491
|
+
this.logger?.warn(
|
|
492
|
+
`[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}', {...})`
|
|
493
|
+
);
|
|
494
|
+
this.logger?.log(`[AiAgentKit][tool] Registering tool at path:`, path2);
|
|
345
495
|
if (this.stack.some((l) => l.isTool && l.path === path2)) {
|
|
346
|
-
this.logger
|
|
496
|
+
this.logger?.error(
|
|
347
497
|
`[AiAgentKit][tool] Tool already registered for path: ${path2}`
|
|
348
498
|
);
|
|
349
499
|
throw new AiKitError(`A tool is already registered for path: ${path2}`);
|
|
@@ -352,7 +502,7 @@ var AiRouter = class _AiRouter {
|
|
|
352
502
|
const factory = optionsOrFactory;
|
|
353
503
|
const isDynamicPath = typeof path2 === "string" && hasDynamicParams(path2);
|
|
354
504
|
const toolMiddleware = async (ctx, _next) => {
|
|
355
|
-
this.logger
|
|
505
|
+
this.logger?.log(
|
|
356
506
|
`[Tool Middleware] Executing factory for path "${path2}". Messages in context: ${ctx.request.messages?.length ?? "undefined"}`
|
|
357
507
|
);
|
|
358
508
|
const toolObject = factory(ctx);
|
|
@@ -363,14 +513,14 @@ var AiRouter = class _AiRouter {
|
|
|
363
513
|
}
|
|
364
514
|
const schema = toolObject.inputSchema;
|
|
365
515
|
if (!schema) {
|
|
366
|
-
this.logger
|
|
516
|
+
this.logger?.warn(
|
|
367
517
|
`[AiAgentKit][tool] Factory-based tool at path ${path2} has no inputSchema. Executing without params.`
|
|
368
518
|
);
|
|
369
519
|
return toolObject.execute({}, {});
|
|
370
520
|
}
|
|
371
521
|
const parsedParams = schema.safeParse(ctx.request.params);
|
|
372
522
|
if (!parsedParams.success) {
|
|
373
|
-
this.logger
|
|
523
|
+
this.logger?.error(
|
|
374
524
|
`[AiAgentKit][tool] Tool call validation failed for path: ${path2}:`,
|
|
375
525
|
parsedParams.error.message
|
|
376
526
|
);
|
|
@@ -392,7 +542,7 @@ var AiRouter = class _AiRouter {
|
|
|
392
542
|
isAgent: false,
|
|
393
543
|
hasDynamicParams: isDynamicPath
|
|
394
544
|
});
|
|
395
|
-
this.logger
|
|
545
|
+
this.logger?.log(
|
|
396
546
|
`Tool registered: path=${path2}, type=factory${isDynamicPath ? " (dynamic)" : ""}`
|
|
397
547
|
);
|
|
398
548
|
return this;
|
|
@@ -404,7 +554,7 @@ var AiRouter = class _AiRouter {
|
|
|
404
554
|
const toolMiddleware = async (ctx, _next) => {
|
|
405
555
|
if (isDynamicPath && typeof path2 === "string") {
|
|
406
556
|
const pathParams = extractPathParams(path2, ctx.request.path || "");
|
|
407
|
-
this.logger
|
|
557
|
+
this.logger?.log(
|
|
408
558
|
`[AiAgentKit][tool] Extracted dynamic path params:`,
|
|
409
559
|
pathParams
|
|
410
560
|
);
|
|
@@ -417,7 +567,7 @@ var AiRouter = class _AiRouter {
|
|
|
417
567
|
}
|
|
418
568
|
const parsedParams = options.schema.safeParse(ctx.request.params);
|
|
419
569
|
if (!parsedParams.success) {
|
|
420
|
-
this.logger
|
|
570
|
+
this.logger?.error(
|
|
421
571
|
`[AiAgentKit][tool] Tool call validation failed for path: ${path2}:`,
|
|
422
572
|
parsedParams.error.message
|
|
423
573
|
);
|
|
@@ -440,12 +590,12 @@ var AiRouter = class _AiRouter {
|
|
|
440
590
|
hasDynamicParams: isDynamicPath,
|
|
441
591
|
paramNames: dynamicParamInfo?.paramNames
|
|
442
592
|
});
|
|
443
|
-
this.logger
|
|
593
|
+
this.logger?.log(
|
|
444
594
|
`Tool registered: path=${path2}, type=static${isDynamicPath ? " (dynamic)" : ""}`
|
|
445
595
|
);
|
|
446
596
|
return this;
|
|
447
597
|
}
|
|
448
|
-
this.logger
|
|
598
|
+
this.logger?.error(
|
|
449
599
|
`[AiAgentKit][tool] Invalid arguments for tool registration at path: ${path2}`
|
|
450
600
|
);
|
|
451
601
|
throw new AiKitError(
|
|
@@ -480,6 +630,54 @@ var AiRouter = class _AiRouter {
|
|
|
480
630
|
};
|
|
481
631
|
});
|
|
482
632
|
}
|
|
633
|
+
/**
|
|
634
|
+
* Outputs all registered paths, and the tool definitions, middlewares, and agents registered on each path.
|
|
635
|
+
* @returns A map of paths to their registered handlers.
|
|
636
|
+
*/
|
|
637
|
+
registry() {
|
|
638
|
+
const registryMap = {};
|
|
639
|
+
for (const layer of this.stack) {
|
|
640
|
+
const pathKey = layer.path.toString();
|
|
641
|
+
if (!registryMap[pathKey]) {
|
|
642
|
+
registryMap[pathKey] = { middlewares: [], tools: [], agents: [] };
|
|
643
|
+
}
|
|
644
|
+
if (layer.isTool) {
|
|
645
|
+
let toolInfo = { type: layer.toolOptions?.type };
|
|
646
|
+
if (layer.toolOptions?.type === "static") {
|
|
647
|
+
toolInfo = {
|
|
648
|
+
...toolInfo,
|
|
649
|
+
schema: layer.toolOptions.schema,
|
|
650
|
+
description: layer.toolOptions.description
|
|
651
|
+
};
|
|
652
|
+
} else if (layer.toolOptions?.type === "factory") {
|
|
653
|
+
toolInfo = {
|
|
654
|
+
...toolInfo,
|
|
655
|
+
factory: layer.toolOptions.factory.toString()
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
registryMap[pathKey].tools.push(toolInfo);
|
|
659
|
+
} else if (layer.isAgent) {
|
|
660
|
+
const agentInfo = {
|
|
661
|
+
handler: layer.handler.name || "anonymous"
|
|
662
|
+
};
|
|
663
|
+
const actAsToolDef = this.actAsToolDefinitions.get(layer.path);
|
|
664
|
+
if (actAsToolDef) {
|
|
665
|
+
agentInfo.actAsTool = {
|
|
666
|
+
...actAsToolDef
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
registryMap[pathKey].agents.push(agentInfo);
|
|
670
|
+
} else {
|
|
671
|
+
registryMap[pathKey].middlewares.push({
|
|
672
|
+
handler: layer.handler.name || "anonymous"
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
return {
|
|
677
|
+
map: registryMap,
|
|
678
|
+
tools: this.getToolSet()
|
|
679
|
+
};
|
|
680
|
+
}
|
|
483
681
|
/**
|
|
484
682
|
* Resolves a path based on the parent path and the requested path.
|
|
485
683
|
* - If path starts with `@/`, it's an absolute path from the root.
|
|
@@ -506,6 +704,7 @@ var AiRouter = class _AiRouter {
|
|
|
506
704
|
// State is passed by reference to allow sub-agents to modify the parent's state.
|
|
507
705
|
// The execution context is a shallow copy to ensure call-specific data is isolated.
|
|
508
706
|
state: parentCtx.state,
|
|
707
|
+
store: parentCtx.store,
|
|
509
708
|
executionContext: {
|
|
510
709
|
...parentCtx.executionContext,
|
|
511
710
|
currentPath: options.path,
|
|
@@ -541,13 +740,24 @@ var AiRouter = class _AiRouter {
|
|
|
541
740
|
* @internal
|
|
542
741
|
*/
|
|
543
742
|
_createLogger(requestId, path2, callDepth = 0) {
|
|
743
|
+
const effectiveLogger = this._getEffectiveLogger();
|
|
744
|
+
if (!effectiveLogger) {
|
|
745
|
+
return {
|
|
746
|
+
log: () => {
|
|
747
|
+
},
|
|
748
|
+
warn: () => {
|
|
749
|
+
},
|
|
750
|
+
error: () => {
|
|
751
|
+
}
|
|
752
|
+
};
|
|
753
|
+
}
|
|
544
754
|
const indent = " ".repeat(callDepth);
|
|
545
755
|
const prefix = `${indent}[${path2.toString()}]`;
|
|
546
756
|
const fullPrefix = `[${requestId}]${prefix}`;
|
|
547
757
|
return {
|
|
548
|
-
log: (...args) =>
|
|
549
|
-
warn: (...args) =>
|
|
550
|
-
error: (...args) =>
|
|
758
|
+
log: (...args) => effectiveLogger.log(fullPrefix, ...args),
|
|
759
|
+
warn: (...args) => effectiveLogger.warn(fullPrefix, ...args),
|
|
760
|
+
error: (...args) => effectiveLogger.error(fullPrefix, ...args)
|
|
551
761
|
};
|
|
552
762
|
}
|
|
553
763
|
/**
|
|
@@ -682,83 +892,162 @@ var AiRouter = class _AiRouter {
|
|
|
682
892
|
* @returns A standard `Response` object containing the rich UI stream.
|
|
683
893
|
*/
|
|
684
894
|
handle(path2, initialContext) {
|
|
685
|
-
this.logger
|
|
895
|
+
this.logger?.log(`Handling request for path: ${path2}`);
|
|
686
896
|
const self = this;
|
|
687
897
|
let executionCompletionResolver = null;
|
|
688
898
|
const executionCompletionPromise = new Promise((resolve) => {
|
|
689
899
|
executionCompletionResolver = resolve;
|
|
690
900
|
});
|
|
691
901
|
return createUIMessageStreamResponse({
|
|
692
|
-
stream:
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
902
|
+
stream: self.handleStream(
|
|
903
|
+
path2,
|
|
904
|
+
initialContext,
|
|
905
|
+
executionCompletionPromise,
|
|
906
|
+
executionCompletionResolver
|
|
907
|
+
)
|
|
908
|
+
});
|
|
909
|
+
}
|
|
910
|
+
handleStream(path2, initialContext, executionCompletionPromise, executionCompletionResolver) {
|
|
911
|
+
const self = this;
|
|
912
|
+
return createUIMessageStream({
|
|
913
|
+
originalMessages: initialContext.request.messages,
|
|
914
|
+
execute: async ({ writer }) => {
|
|
915
|
+
const streamWriter = new StreamWriter(writer);
|
|
916
|
+
const requestId = generateId();
|
|
917
|
+
const store = self._store instanceof MemoryStore ? new MemoryStore() : self._store;
|
|
918
|
+
const ctx = {
|
|
919
|
+
...initialContext,
|
|
920
|
+
request: {
|
|
921
|
+
...initialContext.request,
|
|
922
|
+
path: path2
|
|
923
|
+
// Set the initial path for the root context
|
|
924
|
+
},
|
|
925
|
+
state: {},
|
|
926
|
+
store,
|
|
927
|
+
executionContext: { currentPath: path2, callDepth: 0 },
|
|
928
|
+
requestId,
|
|
929
|
+
logger: self._createLogger(requestId, path2, 0),
|
|
930
|
+
response: {
|
|
931
|
+
...streamWriter.writer,
|
|
932
|
+
writeMessageMetadata: streamWriter.writeMessageMetadata,
|
|
933
|
+
writeCustomTool: streamWriter.writeCustomTool,
|
|
934
|
+
writeObjectAsTool: streamWriter.writeObjectAsTool,
|
|
935
|
+
generateId
|
|
936
|
+
},
|
|
937
|
+
next: void 0,
|
|
938
|
+
// Will be replaced right after
|
|
939
|
+
_onExecutionStart: () => {
|
|
940
|
+
self.pendingExecutions++;
|
|
941
|
+
self.logger?.log(
|
|
942
|
+
`[AiAgentKit][lifecycle] Execution started. Pending: ${self.pendingExecutions}`
|
|
943
|
+
);
|
|
944
|
+
},
|
|
945
|
+
_onExecutionEnd: () => {
|
|
946
|
+
self.pendingExecutions--;
|
|
947
|
+
self.logger?.log(
|
|
948
|
+
`[AiAgentKit][lifecycle] Execution ended. Pending: ${self.pendingExecutions}`
|
|
949
|
+
);
|
|
950
|
+
if (self.pendingExecutions === 0 && executionCompletionResolver) {
|
|
951
|
+
self.logger?.log(
|
|
952
|
+
`[AiAgentKit][lifecycle] All executions finished. Resolving promise.`
|
|
727
953
|
);
|
|
728
|
-
|
|
729
|
-
self.logger.log(
|
|
730
|
-
`[AiAgentKit][lifecycle] All executions finished. Resolving promise.`
|
|
731
|
-
);
|
|
732
|
-
executionCompletionResolver();
|
|
733
|
-
}
|
|
954
|
+
executionCompletionResolver();
|
|
734
955
|
}
|
|
735
|
-
};
|
|
736
|
-
ctx.next = new NextHandler(
|
|
737
|
-
ctx,
|
|
738
|
-
self,
|
|
739
|
-
ctx._onExecutionStart,
|
|
740
|
-
ctx._onExecutionEnd
|
|
741
|
-
);
|
|
742
|
-
ctx._onExecutionStart();
|
|
743
|
-
self.logger.log(
|
|
744
|
-
`[AiAgentKit][lifecycle] Main execution chain started.`
|
|
745
|
-
);
|
|
746
|
-
try {
|
|
747
|
-
await self._execute(path2, ctx);
|
|
748
|
-
} catch (err) {
|
|
749
|
-
ctx.logger.error("Unhandled error in main execution chain", err);
|
|
750
|
-
} finally {
|
|
751
|
-
ctx._onExecutionEnd();
|
|
752
|
-
self.logger.log(
|
|
753
|
-
`[AiAgentKit][lifecycle] Main execution chain finished.`
|
|
754
|
-
);
|
|
755
956
|
}
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
957
|
+
};
|
|
958
|
+
ctx.next = new NextHandler(
|
|
959
|
+
ctx,
|
|
960
|
+
self,
|
|
961
|
+
ctx._onExecutionStart,
|
|
962
|
+
ctx._onExecutionEnd
|
|
963
|
+
);
|
|
964
|
+
ctx._onExecutionStart();
|
|
965
|
+
self.logger?.log(
|
|
966
|
+
`[AiAgentKit][lifecycle] Main execution chain started.`
|
|
967
|
+
);
|
|
968
|
+
try {
|
|
969
|
+
const response = await self._execute(path2, ctx);
|
|
970
|
+
const toolDefinition = this.actAsToolDefinitions.get(path2);
|
|
971
|
+
if (toolDefinition && !toolDefinition.metadata?.hideUI) {
|
|
972
|
+
ctx.response.writeCustomTool({
|
|
973
|
+
toolName: toolDefinition.id,
|
|
974
|
+
toolCallId: toolDefinition.id + "-" + ctx.response.generateId(),
|
|
975
|
+
output: response
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
return response;
|
|
979
|
+
} catch (err) {
|
|
980
|
+
ctx.logger.error("Unhandled error in main execution chain", err);
|
|
981
|
+
} finally {
|
|
982
|
+
ctx._onExecutionEnd();
|
|
983
|
+
self.logger?.log(
|
|
984
|
+
`[AiAgentKit][lifecycle] Main execution chain finished.`
|
|
759
985
|
);
|
|
760
986
|
}
|
|
761
|
-
|
|
987
|
+
await executionCompletionPromise;
|
|
988
|
+
self.logger?.log(
|
|
989
|
+
`[AiAgentKit][lifecycle] All executions truly finished. Stream can be safely closed.`
|
|
990
|
+
);
|
|
991
|
+
}
|
|
992
|
+
});
|
|
993
|
+
}
|
|
994
|
+
/**
|
|
995
|
+
* Handles an incoming request and returns a promise that resolves with the full,
|
|
996
|
+
* non-streamed response. This is useful for environments where streaming is not
|
|
997
|
+
* desired or for testing.
|
|
998
|
+
*
|
|
999
|
+
* @param path The path of the agent or tool to execute.
|
|
1000
|
+
* @param initialContext The initial context for the request, typically containing messages.
|
|
1001
|
+
* @returns A `Promise<Response>` that resolves with the final JSON response.
|
|
1002
|
+
*/
|
|
1003
|
+
async toAwaitResponse(path2, initialContext) {
|
|
1004
|
+
this.logger?.log(`Handling request for path: ${path2}`);
|
|
1005
|
+
const self = this;
|
|
1006
|
+
let executionCompletionResolver = null;
|
|
1007
|
+
const executionCompletionPromise = new Promise((resolve) => {
|
|
1008
|
+
executionCompletionResolver = resolve;
|
|
1009
|
+
});
|
|
1010
|
+
const stream = this.handleStream(
|
|
1011
|
+
path2,
|
|
1012
|
+
initialContext,
|
|
1013
|
+
executionCompletionPromise,
|
|
1014
|
+
executionCompletionResolver
|
|
1015
|
+
);
|
|
1016
|
+
const messageStream = readUIMessageStream({
|
|
1017
|
+
stream,
|
|
1018
|
+
onError: (error) => {
|
|
1019
|
+
this.logger?.error("Error reading UI message stream", error);
|
|
1020
|
+
}
|
|
1021
|
+
});
|
|
1022
|
+
let finalMessages = [];
|
|
1023
|
+
const thisMessageId = generateId();
|
|
1024
|
+
for await (const message of messageStream) {
|
|
1025
|
+
if (message.id?.length > 0) {
|
|
1026
|
+
finalMessages.push(message);
|
|
1027
|
+
} else if (finalMessages.find((m) => m.id === thisMessageId)) {
|
|
1028
|
+
finalMessages = finalMessages.map(
|
|
1029
|
+
(m) => m.id === thisMessageId ? {
|
|
1030
|
+
...m,
|
|
1031
|
+
metadata: {
|
|
1032
|
+
...m.metadata ?? {},
|
|
1033
|
+
...message.metadata ?? {}
|
|
1034
|
+
},
|
|
1035
|
+
parts: clubParts([
|
|
1036
|
+
...m.parts ?? [],
|
|
1037
|
+
...message.parts ?? []
|
|
1038
|
+
])
|
|
1039
|
+
} : m
|
|
1040
|
+
);
|
|
1041
|
+
} else {
|
|
1042
|
+
finalMessages.push({
|
|
1043
|
+
...message,
|
|
1044
|
+
id: thisMessageId
|
|
1045
|
+
});
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
const responseBody = JSON.stringify(finalMessages);
|
|
1049
|
+
return new Response(responseBody, {
|
|
1050
|
+
headers: { "Content-Type": "application/json" }
|
|
762
1051
|
});
|
|
763
1052
|
}
|
|
764
1053
|
};
|
|
@@ -788,14 +1077,30 @@ var NextHandler = class {
|
|
|
788
1077
|
const subContext = this.router._createSubContext(this.ctx, {
|
|
789
1078
|
type: "agent",
|
|
790
1079
|
path: resolvedPath,
|
|
791
|
-
params,
|
|
1080
|
+
params: params ?? {},
|
|
792
1081
|
messages: this.ctx.request.messages
|
|
793
1082
|
});
|
|
1083
|
+
const definition = this.router.actAsToolDefinitions.get(resolvedPath);
|
|
1084
|
+
const toolCallId = definition?.id + "-" + this.ctx.response.generateId();
|
|
1085
|
+
if (options?.streamToUI && definition) {
|
|
1086
|
+
this.ctx.response.writeCustomTool({
|
|
1087
|
+
toolName: definition?.id,
|
|
1088
|
+
toolCallId,
|
|
1089
|
+
input: subContext.request.params
|
|
1090
|
+
});
|
|
1091
|
+
}
|
|
794
1092
|
const data = await this.router._execute(
|
|
795
1093
|
resolvedPath,
|
|
796
1094
|
subContext,
|
|
797
1095
|
true
|
|
798
1096
|
);
|
|
1097
|
+
if (options?.streamToUI && definition) {
|
|
1098
|
+
this.ctx.response.writeCustomTool({
|
|
1099
|
+
toolName: definition?.id,
|
|
1100
|
+
toolCallId,
|
|
1101
|
+
output: data
|
|
1102
|
+
});
|
|
1103
|
+
}
|
|
799
1104
|
return { ok: true, data };
|
|
800
1105
|
} catch (error) {
|
|
801
1106
|
this.ctx.logger.error(`[callAgent] Error:`, error);
|
|
@@ -804,7 +1109,23 @@ var NextHandler = class {
|
|
|
804
1109
|
this.onExecutionEnd();
|
|
805
1110
|
}
|
|
806
1111
|
}
|
|
1112
|
+
/**
|
|
1113
|
+
* @deprecated Use agent-as-tools pattern instead. Use `ctx.next.callAgent()` to call agents that are exposed as tools.
|
|
1114
|
+
* This method will be removed in a future version.
|
|
1115
|
+
*
|
|
1116
|
+
* Example migration:
|
|
1117
|
+
* ```typescript
|
|
1118
|
+
* // Old way (deprecated):
|
|
1119
|
+
* const result = await ctx.next.callTool('/calculator', { a: 5, b: 3 });
|
|
1120
|
+
*
|
|
1121
|
+
* // New way (recommended):
|
|
1122
|
+
* const result = await ctx.next.callAgent('/calculator', { a: 5, b: 3 });
|
|
1123
|
+
* ```
|
|
1124
|
+
*/
|
|
807
1125
|
async callTool(toolPath, params, options) {
|
|
1126
|
+
this.ctx.logger.warn(
|
|
1127
|
+
`[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.`
|
|
1128
|
+
);
|
|
808
1129
|
this.onExecutionStart();
|
|
809
1130
|
try {
|
|
810
1131
|
const parentPath = this.ctx.executionContext.currentPath || "/";
|
|
@@ -816,7 +1137,7 @@ var NextHandler = class {
|
|
|
816
1137
|
const subContext = this.router._createSubContext(this.ctx, {
|
|
817
1138
|
type: "tool",
|
|
818
1139
|
path: resolvedPath,
|
|
819
|
-
params,
|
|
1140
|
+
params: params ?? {},
|
|
820
1141
|
messages: this.ctx.request.messages
|
|
821
1142
|
});
|
|
822
1143
|
const data = await this.router._execute(
|
|
@@ -832,7 +1153,23 @@ var NextHandler = class {
|
|
|
832
1153
|
this.onExecutionEnd();
|
|
833
1154
|
}
|
|
834
1155
|
}
|
|
1156
|
+
/**
|
|
1157
|
+
* @deprecated Use agent-as-tools pattern instead. Use `ctx.next.agentAsTool()` to attach agents as tools.
|
|
1158
|
+
* This method will be removed in a future version.
|
|
1159
|
+
*
|
|
1160
|
+
* Example migration:
|
|
1161
|
+
* ```typescript
|
|
1162
|
+
* // Old way (deprecated):
|
|
1163
|
+
* const tool = ctx.next.attachTool('/calculator');
|
|
1164
|
+
*
|
|
1165
|
+
* // New way (recommended):
|
|
1166
|
+
* const tool = ctx.next.agentAsTool('/calculator');
|
|
1167
|
+
* ```
|
|
1168
|
+
*/
|
|
835
1169
|
attachTool(toolPath, _tool) {
|
|
1170
|
+
this.ctx.logger.warn(
|
|
1171
|
+
`[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.`
|
|
1172
|
+
);
|
|
836
1173
|
const parentPath = this.ctx.executionContext.currentPath || "/";
|
|
837
1174
|
const resolvedPath = this.router._resolvePath(
|
|
838
1175
|
parentPath,
|
|
@@ -910,14 +1247,81 @@ var NextHandler = class {
|
|
|
910
1247
|
);
|
|
911
1248
|
throw new AgentDefinitionMissingError(resolvedPath);
|
|
912
1249
|
}
|
|
1250
|
+
const { id, metadata, ...restDefinition } = definition;
|
|
913
1251
|
return {
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
1252
|
+
[id]: {
|
|
1253
|
+
...restDefinition,
|
|
1254
|
+
metadata: {
|
|
1255
|
+
...metadata,
|
|
1256
|
+
toolKey: id,
|
|
1257
|
+
name: restDefinition.name,
|
|
1258
|
+
description: restDefinition.description,
|
|
1259
|
+
absolutePath: restDefinition.path
|
|
1260
|
+
},
|
|
1261
|
+
execute: (params, options) => {
|
|
1262
|
+
const executeInternal = async () => {
|
|
1263
|
+
const result = await this.callAgent(agentPath, params, options);
|
|
1264
|
+
if (!result.ok) {
|
|
1265
|
+
throw result.error;
|
|
1266
|
+
}
|
|
1267
|
+
return result.data;
|
|
1268
|
+
};
|
|
1269
|
+
const newPromise = this.router.toolExecutionPromise.then(
|
|
1270
|
+
executeInternal,
|
|
1271
|
+
executeInternal
|
|
1272
|
+
);
|
|
1273
|
+
this.router.toolExecutionPromise = newPromise;
|
|
1274
|
+
return newPromise;
|
|
919
1275
|
}
|
|
920
|
-
|
|
1276
|
+
}
|
|
1277
|
+
};
|
|
1278
|
+
}
|
|
1279
|
+
getToolDefinition(agentPath) {
|
|
1280
|
+
const parentPath = this.ctx.executionContext.currentPath || "/";
|
|
1281
|
+
const resolvedPath = this.router._resolvePath(
|
|
1282
|
+
parentPath,
|
|
1283
|
+
agentPath
|
|
1284
|
+
);
|
|
1285
|
+
let preDefined;
|
|
1286
|
+
const pathsToTry = [resolvedPath];
|
|
1287
|
+
if (typeof agentPath === "string" && agentPath.startsWith("/")) {
|
|
1288
|
+
pathsToTry.unshift(agentPath);
|
|
1289
|
+
}
|
|
1290
|
+
for (const pathToTry of pathsToTry) {
|
|
1291
|
+
for (const [key, value] of this.router.actAsToolDefinitions) {
|
|
1292
|
+
if (typeof key === "string") {
|
|
1293
|
+
if (key === pathToTry) {
|
|
1294
|
+
preDefined = value;
|
|
1295
|
+
break;
|
|
1296
|
+
}
|
|
1297
|
+
if (extractPathParams(key, pathToTry) !== null) {
|
|
1298
|
+
preDefined = value;
|
|
1299
|
+
break;
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
if (key instanceof RegExp && key.test(pathToTry)) {
|
|
1303
|
+
preDefined = value;
|
|
1304
|
+
break;
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
if (preDefined) break;
|
|
1308
|
+
}
|
|
1309
|
+
const definition = preDefined;
|
|
1310
|
+
if (!definition) {
|
|
1311
|
+
this.ctx.logger.error(
|
|
1312
|
+
`[agentAsTool] No definition found for agent at resolved path: ${resolvedPath}`
|
|
1313
|
+
);
|
|
1314
|
+
throw new AgentDefinitionMissingError(resolvedPath);
|
|
1315
|
+
}
|
|
1316
|
+
const { metadata, ...restDefinition } = definition;
|
|
1317
|
+
return {
|
|
1318
|
+
...restDefinition,
|
|
1319
|
+
metadata: {
|
|
1320
|
+
...metadata,
|
|
1321
|
+
toolKey: restDefinition.id,
|
|
1322
|
+
name: restDefinition.name,
|
|
1323
|
+
description: restDefinition.description,
|
|
1324
|
+
absolutePath: restDefinition.path
|
|
921
1325
|
}
|
|
922
1326
|
};
|
|
923
1327
|
}
|
|
@@ -928,13 +1332,16 @@ export {
|
|
|
928
1332
|
AiKitError,
|
|
929
1333
|
AiRouter,
|
|
930
1334
|
MaxCallDepthExceededError,
|
|
1335
|
+
MemoryStore,
|
|
931
1336
|
StreamWriter,
|
|
932
1337
|
ToolNotFoundError,
|
|
933
1338
|
ToolValidationError,
|
|
934
1339
|
findFirstElement,
|
|
935
1340
|
findLastElement,
|
|
936
1341
|
findLastMessageWith,
|
|
1342
|
+
getGlobalLogger,
|
|
937
1343
|
getTextParts,
|
|
938
|
-
getTextPartsContent
|
|
1344
|
+
getTextPartsContent,
|
|
1345
|
+
setGlobalLogger
|
|
939
1346
|
};
|
|
940
1347
|
//# sourceMappingURL=index.mjs.map
|