@aigne/core 1.62.0 → 1.63.0-beta.1

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 CHANGED
@@ -1,5 +1,32 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.63.0-beta.1](https://github.com/AIGNE-io/aigne-framework/compare/core-v1.63.0-beta...core-v1.63.0-beta.1) (2025-10-09)
4
+
5
+
6
+ ### Features
7
+
8
+ * **core:** add `toolCallsConcurrency` support for AI agent ([#598](https://github.com/AIGNE-io/aigne-framework/issues/598)) ([84df406](https://github.com/AIGNE-io/aigne-framework/commit/84df406bfa9e3bdf159509f4b9cf2301ec80b155))
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **core:** add input_file_key support for agent yaml ([#597](https://github.com/AIGNE-io/aigne-framework/issues/597)) ([63414a3](https://github.com/AIGNE-io/aigne-framework/commit/63414a3d46c74c686e7f033c224ca6175bea8c3f))
14
+
15
+ ## [1.63.0-beta](https://github.com/AIGNE-io/aigne-framework/compare/core-v1.62.0...core-v1.63.0-beta) (2025-10-07)
16
+
17
+
18
+ ### Features
19
+
20
+ * **afs:** add basic AFS(AIGNE File System) support ([#505](https://github.com/AIGNE-io/aigne-framework/issues/505)) ([ac2a18a](https://github.com/AIGNE-io/aigne-framework/commit/ac2a18a82470a2f31c466f329386525eb1cdab6d))
21
+
22
+
23
+ ### Dependencies
24
+
25
+ * The following workspace dependencies were updated
26
+ * dependencies
27
+ * @aigne/afs bumped to 1.0.0
28
+ * @aigne/observability-api bumped to 0.11.2-beta
29
+
3
30
  ## [1.62.0](https://github.com/AIGNE-io/aigne-framework/compare/core-v1.62.0-beta.6...core-v1.62.0) (2025-10-04)
4
31
 
5
32
 
@@ -1,3 +1,4 @@
1
+ import { AFS, type AFSOptions } from "@aigne/afs";
1
2
  import { nodejs } from "@aigne/platform-helpers/nodejs/index.js";
2
3
  import type * as prompts from "@inquirer/prompts";
3
4
  import { type ZodObject, type ZodType } from "zod";
@@ -120,6 +121,7 @@ export interface AgentOptions<I extends Message = Message, O extends Message = M
120
121
  * One or more memory agents this agent can use
121
122
  */
122
123
  memory?: MemoryAgent | MemoryAgent[];
124
+ afs?: true | AFSOptions | AFS | ((afs: AFS) => AFS);
123
125
  asyncMemoryRecord?: boolean;
124
126
  /**
125
127
  * Maximum number of memory items to retrieve
@@ -205,8 +207,11 @@ export declare abstract class Agent<I extends Message = any, O extends Message =
205
207
  constructor(options?: AgentOptions<I, O>);
206
208
  /**
207
209
  * List of memories this agent can use
210
+ *
211
+ * @deprecated use afs instead
208
212
  */
209
213
  readonly memories: MemoryAgent[];
214
+ afs?: AFS;
210
215
  asyncMemoryRecord?: boolean;
211
216
  tag?: string;
212
217
  /**
@@ -46,6 +46,7 @@ exports.isAgentResponseProgress = isAgentResponseProgress;
46
46
  exports.textDelta = textDelta;
47
47
  exports.jsonDelta = jsonDelta;
48
48
  exports.agentProcessResultToObject = agentProcessResultToObject;
49
+ const afs_1 = require("@aigne/afs");
49
50
  const index_js_1 = require("@aigne/platform-helpers/nodejs/index.js");
50
51
  const fast_deep_equal_1 = __importDefault(require("fast-deep-equal"));
51
52
  const nunjucks_1 = __importDefault(require("nunjucks"));
@@ -151,6 +152,15 @@ class Agent {
151
152
  else if (options.memory) {
152
153
  this.memories.push(options.memory);
153
154
  }
155
+ this.afs = !options.afs
156
+ ? undefined
157
+ : options.afs === true
158
+ ? new afs_1.AFS()
159
+ : typeof options.afs === "function"
160
+ ? options.afs(new afs_1.AFS())
161
+ : options.afs instanceof afs_1.AFS
162
+ ? options.afs
163
+ : new afs_1.AFS(options.afs);
154
164
  this.asyncMemoryRecord = options.asyncMemoryRecord;
155
165
  this.maxRetrieveMemoryCount = options.maxRetrieveMemoryCount;
156
166
  this.hooks = (0, type_utils_js_1.flat)(options.hooks);
@@ -164,8 +174,11 @@ class Agent {
164
174
  }
165
175
  /**
166
176
  * List of memories this agent can use
177
+ *
178
+ * @deprecated use afs instead
167
179
  */
168
180
  memories = [];
181
+ afs;
169
182
  asyncMemoryRecord;
170
183
  tag;
171
184
  /**
@@ -537,6 +550,7 @@ class Agent {
537
550
  const o = await this.callHooks(["onSuccess", "onEnd"], { input, output: finalOutput }, options);
538
551
  if (o?.output)
539
552
  finalOutput = o.output;
553
+ this.afs?.emit("agentSucceed", { input, output: finalOutput });
540
554
  if (!this.disableEvents)
541
555
  context.emit("agentSucceed", { agent: this, output: finalOutput });
542
556
  return finalOutput;
@@ -1,6 +1,6 @@
1
1
  import { type ZodObject, type ZodType, z } from "zod";
2
2
  import { PromptBuilder } from "../prompt/prompt-builder.js";
3
- import { Agent, type AgentInvokeOptions, type AgentOptions, type AgentProcessAsyncGenerator, type Message } from "./agent.js";
3
+ import { Agent, type AgentInvokeOptions, type AgentOptions, type AgentProcessAsyncGenerator, type AgentProcessResult, type Message } from "./agent.js";
4
4
  import type { ChatModel, ChatModelInput } from "./chat-model.js";
5
5
  import type { GuideRailAgentOutput } from "./guide-rail-agent.js";
6
6
  import type { FileType } from "./model.js";
@@ -42,6 +42,12 @@ export interface AIAgentOptions<I extends Message = Message, O extends Message =
42
42
  * @default AIAgentToolChoice.auto
43
43
  */
44
44
  toolChoice?: AIAgentToolChoice | Agent;
45
+ /**
46
+ * Maximum number of tool calls to execute concurrently
47
+ *
48
+ * @default 1
49
+ */
50
+ toolCallsConcurrency?: number;
45
51
  /**
46
52
  * Whether to preserve text generated during tool usage in the final output
47
53
  */
@@ -237,6 +243,12 @@ export declare class AIAgent<I extends Message = any, O extends Message = any> e
237
243
  * {@includeCode ../../test/agents/ai-agent.test.ts#example-ai-agent-router}
238
244
  */
239
245
  toolChoice?: AIAgentToolChoice | Agent;
246
+ /**
247
+ * Maximum number of tool calls to execute concurrently
248
+ *
249
+ * @default 1
250
+ */
251
+ toolCallsConcurrency?: number;
240
252
  /**
241
253
  * Whether to preserve text generated during tool usage in the final output
242
254
  */
@@ -304,7 +316,8 @@ export declare class AIAgent<I extends Message = any, O extends Message = any> e
304
316
  *
305
317
  * @protected
306
318
  */
307
- process(input: I, options: AgentInvokeOptions): AgentProcessAsyncGenerator<O>;
319
+ process(input: I, options: AgentInvokeOptions): AgentProcessResult<O>;
320
+ private _process;
308
321
  protected onGuideRailError(error: GuideRailAgentOutput): Promise<O | GuideRailAgentOutput>;
309
322
  /**
310
323
  * Process router mode requests
@@ -1,6 +1,10 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.AIAgent = exports.aiAgentOptionsSchema = exports.aiAgentToolChoiceSchema = exports.AIAgentToolChoice = exports.DEFAULT_OUTPUT_FILE_KEY = exports.DEFAULT_OUTPUT_KEY = void 0;
7
+ const fastq_1 = __importDefault(require("fastq"));
4
8
  const zod_1 = require("zod");
5
9
  const prompt_builder_js_1 = require("../prompt/prompt-builder.js");
6
10
  const structured_stream_instructions_js_1 = require("../prompt/prompts/structured-stream-instructions.js");
@@ -58,6 +62,7 @@ exports.aiAgentOptionsSchema = agent_js_1.agentOptionsSchema.extend({
58
62
  inputKey: zod_1.z.string().optional(),
59
63
  outputKey: zod_1.z.string().optional(),
60
64
  toolChoice: exports.aiAgentToolChoiceSchema.optional(),
65
+ toolCallsConcurrency: zod_1.z.number().int().min(0).optional(),
61
66
  keepTextInToolUses: zod_1.z.boolean().optional(),
62
67
  memoryAgentsAsTools: zod_1.z.boolean().optional(),
63
68
  memoryPromptTemplate: zod_1.z.string().optional(),
@@ -117,6 +122,7 @@ class AIAgent extends agent_js_1.Agent {
117
122
  this.outputFileKey = options.outputFileKey || exports.DEFAULT_OUTPUT_FILE_KEY;
118
123
  this.outputFileType = options.outputFileType;
119
124
  this.toolChoice = options.toolChoice;
125
+ this.toolCallsConcurrency = options.toolCallsConcurrency || 1;
120
126
  this.keepTextInToolUses = options.keepTextInToolUses;
121
127
  this.memoryAgentsAsTools = options.memoryAgentsAsTools;
122
128
  this.memoryPromptTemplate = options.memoryPromptTemplate;
@@ -173,6 +179,12 @@ class AIAgent extends agent_js_1.Agent {
173
179
  * {@includeCode ../../test/agents/ai-agent.test.ts#example-ai-agent-router}
174
180
  */
175
181
  toolChoice;
182
+ /**
183
+ * Maximum number of tool calls to execute concurrently
184
+ *
185
+ * @default 1
186
+ */
187
+ toolCallsConcurrency;
176
188
  /**
177
189
  * Whether to preserve text generated during tool usage in the final output
178
190
  */
@@ -235,7 +247,10 @@ class AIAgent extends agent_js_1.Agent {
235
247
  *
236
248
  * @protected
237
249
  */
238
- async *process(input, options) {
250
+ process(input, options) {
251
+ return this._process(input, options);
252
+ }
253
+ async *_process(input, options) {
239
254
  const model = this.model || options.model || options.context.model;
240
255
  if (!model)
241
256
  throw new Error("model is required to run AIAgent");
@@ -284,31 +299,43 @@ class AIAgent extends agent_js_1.Agent {
284
299
  yield { delta: { text: { [outputKey]: "\n" } } };
285
300
  }
286
301
  const executedToolCalls = [];
302
+ let error;
303
+ const queue = fastq_1.default.promise(async ({ tool, call }) => {
304
+ try {
305
+ // NOTE: should pass both arguments (model generated) and input (user provided) to the tool
306
+ const output = await this.invokeSkill(tool, { ...input, ...call.function.arguments }, options).catch((error) => {
307
+ if (!this.catchToolsError) {
308
+ return Promise.reject(error);
309
+ }
310
+ return {
311
+ isError: true,
312
+ error: {
313
+ message: error.message,
314
+ },
315
+ };
316
+ });
317
+ executedToolCalls.push({ call, output });
318
+ }
319
+ catch (e) {
320
+ error = e;
321
+ queue.killAndDrain();
322
+ }
323
+ }, this.toolCallsConcurrency || 1);
287
324
  // Execute tools
288
325
  for (const call of toolCalls) {
289
326
  const tool = toolsMap.get(call.function.name);
290
327
  if (!tool)
291
328
  throw new Error(`Tool not found: ${call.function.name}`);
292
- // NOTE: should pass both arguments (model generated) and input (user provided) to the tool
293
- const output = await this.invokeSkill(tool, { ...input, ...call.function.arguments }, options).catch((error) => {
294
- if (!this.catchToolsError) {
295
- return Promise.reject(error);
296
- }
297
- return {
298
- isError: true,
299
- error: {
300
- message: error.message,
301
- },
302
- };
303
- });
304
- // NOTE: Return transfer output immediately
305
- if ((0, types_js_1.isTransferAgentOutput)(output)) {
306
- return output;
307
- }
308
- executedToolCalls.push({ call, output });
329
+ queue.push({ tool, call });
309
330
  }
331
+ await queue.drained();
332
+ if (error)
333
+ throw error;
310
334
  // Continue LLM function calling loop if any tools were executed
311
335
  if (executedToolCalls.length) {
336
+ const transferOutput = executedToolCalls.find((i) => (0, types_js_1.isTransferAgentOutput)(i.output))?.output;
337
+ if (transferOutput)
338
+ return transferOutput;
312
339
  toolCallMessages.push(await template_js_1.AgentMessageTemplate.from(undefined, executedToolCalls.map(({ call }) => call)).format(), ...(await Promise.all(executedToolCalls.map(({ call, output }) => template_js_1.ToolMessageTemplate.from(output, call.id).format()))));
313
340
  continue;
314
341
  }
@@ -46,8 +46,10 @@ export interface AIAgentSchema extends BaseAgentSchema {
46
46
  type: "ai";
47
47
  instructions?: Instructions;
48
48
  inputKey?: string;
49
+ inputFileKey?: string;
49
50
  outputKey?: string;
50
51
  toolChoice?: AIAgentToolChoice;
52
+ toolCallsConcurrency?: number;
51
53
  keepTextInToolUses?: boolean;
52
54
  }
53
55
  export interface ImageAgentSchema extends BaseAgentSchema {
@@ -95,7 +95,10 @@ async function parseAgentFile(path, data) {
95
95
  instructions: (0, schema_js_1.optionalize)(instructionsSchema),
96
96
  inputKey: (0, schema_js_1.optionalize)(zod_1.z.string()),
97
97
  outputKey: (0, schema_js_1.optionalize)(zod_1.z.string()),
98
+ inputFileKey: (0, schema_js_1.optionalize)(zod_1.z.string()),
99
+ outputFileKey: (0, schema_js_1.optionalize)(zod_1.z.string()),
98
100
  toolChoice: (0, schema_js_1.optionalize)(zod_1.z.nativeEnum(ai_agent_js_1.AIAgentToolChoice)),
101
+ toolCallsConcurrency: (0, schema_js_1.optionalize)(zod_1.z.number().int().min(0)),
99
102
  keepTextInToolUses: (0, schema_js_1.optionalize)(zod_1.z.boolean()),
100
103
  structuredStreamMode: (0, schema_js_1.optionalize)(zod_1.z.boolean()),
101
104
  })
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PromptBuilder = void 0;
4
+ const afs_1 = require("@aigne/afs");
4
5
  const index_js_1 = require("@aigne/platform-helpers/nodejs/index.js");
5
6
  const yaml_1 = require("yaml");
6
7
  const zod_1 = require("zod");
@@ -98,6 +99,18 @@ class PromptBuilder {
98
99
  if (options.agent?.useMemoriesFromContext && options.context?.memories?.length) {
99
100
  memories.push(...options.context.memories);
100
101
  }
102
+ if (options.agent?.afs) {
103
+ const history = await options.agent.afs.list(afs_1.AFSHistory.Path, {
104
+ limit: options.agent.maxRetrieveMemoryCount || 1,
105
+ orderBy: [["createdAt", "desc"]],
106
+ });
107
+ if (message) {
108
+ const result = await options.agent.afs.search("/", message);
109
+ const ms = result.list.map((entry) => ({ content: (0, yaml_1.stringify)(entry.content) }));
110
+ memories.push(...ms);
111
+ }
112
+ memories.push(...history.list.filter((i) => (0, type_utils_js_1.isNonNullable)(i.content)));
113
+ }
101
114
  if (memories.length)
102
115
  messages.push(...(await this.convertMemoriesToMessages(memories, options)));
103
116
  // if the agent is using structured stream mode, add the instructions
@@ -1,4 +1,4 @@
1
- import type { AgentHooks } from "../agents/agent.ts";
1
+ import type { AgentHooks } from "../agents/agent.js";
2
2
  import type { AIGNECLIAgents } from "../aigne/type.js";
3
3
  export declare function sortHooks(hooks: AgentHooks[]): AgentHooks[];
4
4
  export interface CLIAgent<T> {
@@ -9,4 +9,4 @@ export interface CLIAgent<T> {
9
9
  agents?: CLIAgent<T>[];
10
10
  }
11
11
  export declare function mapCliAgent<A, O>({ agent, agents, ...input }: CLIAgent<A>, transform: (input: A) => O): CLIAgent<O>;
12
- export declare function findCliAgent(cli: AIGNECLIAgents, parent: string[] | "*", name: string): import("../agents/agent.ts").Agent<any, any> | undefined;
12
+ export declare function findCliAgent(cli: AIGNECLIAgents, parent: string[] | "*", name: string): import("../agents/agent.js").Agent<any, any> | undefined;
@@ -1,3 +1,4 @@
1
+ import { AFS, type AFSOptions } from "@aigne/afs";
1
2
  import { nodejs } from "@aigne/platform-helpers/nodejs/index.js";
2
3
  import type * as prompts from "@inquirer/prompts";
3
4
  import { type ZodObject, type ZodType } from "zod";
@@ -120,6 +121,7 @@ export interface AgentOptions<I extends Message = Message, O extends Message = M
120
121
  * One or more memory agents this agent can use
121
122
  */
122
123
  memory?: MemoryAgent | MemoryAgent[];
124
+ afs?: true | AFSOptions | AFS | ((afs: AFS) => AFS);
123
125
  asyncMemoryRecord?: boolean;
124
126
  /**
125
127
  * Maximum number of memory items to retrieve
@@ -205,8 +207,11 @@ export declare abstract class Agent<I extends Message = any, O extends Message =
205
207
  constructor(options?: AgentOptions<I, O>);
206
208
  /**
207
209
  * List of memories this agent can use
210
+ *
211
+ * @deprecated use afs instead
208
212
  */
209
213
  readonly memories: MemoryAgent[];
214
+ afs?: AFS;
210
215
  asyncMemoryRecord?: boolean;
211
216
  tag?: string;
212
217
  /**
@@ -1,6 +1,6 @@
1
1
  import { type ZodObject, type ZodType, z } from "zod";
2
2
  import { PromptBuilder } from "../prompt/prompt-builder.js";
3
- import { Agent, type AgentInvokeOptions, type AgentOptions, type AgentProcessAsyncGenerator, type Message } from "./agent.js";
3
+ import { Agent, type AgentInvokeOptions, type AgentOptions, type AgentProcessAsyncGenerator, type AgentProcessResult, type Message } from "./agent.js";
4
4
  import type { ChatModel, ChatModelInput } from "./chat-model.js";
5
5
  import type { GuideRailAgentOutput } from "./guide-rail-agent.js";
6
6
  import type { FileType } from "./model.js";
@@ -42,6 +42,12 @@ export interface AIAgentOptions<I extends Message = Message, O extends Message =
42
42
  * @default AIAgentToolChoice.auto
43
43
  */
44
44
  toolChoice?: AIAgentToolChoice | Agent;
45
+ /**
46
+ * Maximum number of tool calls to execute concurrently
47
+ *
48
+ * @default 1
49
+ */
50
+ toolCallsConcurrency?: number;
45
51
  /**
46
52
  * Whether to preserve text generated during tool usage in the final output
47
53
  */
@@ -237,6 +243,12 @@ export declare class AIAgent<I extends Message = any, O extends Message = any> e
237
243
  * {@includeCode ../../test/agents/ai-agent.test.ts#example-ai-agent-router}
238
244
  */
239
245
  toolChoice?: AIAgentToolChoice | Agent;
246
+ /**
247
+ * Maximum number of tool calls to execute concurrently
248
+ *
249
+ * @default 1
250
+ */
251
+ toolCallsConcurrency?: number;
240
252
  /**
241
253
  * Whether to preserve text generated during tool usage in the final output
242
254
  */
@@ -304,7 +316,8 @@ export declare class AIAgent<I extends Message = any, O extends Message = any> e
304
316
  *
305
317
  * @protected
306
318
  */
307
- process(input: I, options: AgentInvokeOptions): AgentProcessAsyncGenerator<O>;
319
+ process(input: I, options: AgentInvokeOptions): AgentProcessResult<O>;
320
+ private _process;
308
321
  protected onGuideRailError(error: GuideRailAgentOutput): Promise<O | GuideRailAgentOutput>;
309
322
  /**
310
323
  * Process router mode requests
@@ -46,8 +46,10 @@ export interface AIAgentSchema extends BaseAgentSchema {
46
46
  type: "ai";
47
47
  instructions?: Instructions;
48
48
  inputKey?: string;
49
+ inputFileKey?: string;
49
50
  outputKey?: string;
50
51
  toolChoice?: AIAgentToolChoice;
52
+ toolCallsConcurrency?: number;
51
53
  keepTextInToolUses?: boolean;
52
54
  }
53
55
  export interface ImageAgentSchema extends BaseAgentSchema {
@@ -1,4 +1,4 @@
1
- import type { AgentHooks } from "../agents/agent.ts";
1
+ import type { AgentHooks } from "../agents/agent.js";
2
2
  import type { AIGNECLIAgents } from "../aigne/type.js";
3
3
  export declare function sortHooks(hooks: AgentHooks[]): AgentHooks[];
4
4
  export interface CLIAgent<T> {
@@ -9,4 +9,4 @@ export interface CLIAgent<T> {
9
9
  agents?: CLIAgent<T>[];
10
10
  }
11
11
  export declare function mapCliAgent<A, O>({ agent, agents, ...input }: CLIAgent<A>, transform: (input: A) => O): CLIAgent<O>;
12
- export declare function findCliAgent(cli: AIGNECLIAgents, parent: string[] | "*", name: string): import("../agents/agent.ts").Agent<any, any> | undefined;
12
+ export declare function findCliAgent(cli: AIGNECLIAgents, parent: string[] | "*", name: string): import("../agents/agent.js").Agent<any, any> | undefined;
@@ -1,3 +1,4 @@
1
+ import { AFS, type AFSOptions } from "@aigne/afs";
1
2
  import { nodejs } from "@aigne/platform-helpers/nodejs/index.js";
2
3
  import type * as prompts from "@inquirer/prompts";
3
4
  import { type ZodObject, type ZodType } from "zod";
@@ -120,6 +121,7 @@ export interface AgentOptions<I extends Message = Message, O extends Message = M
120
121
  * One or more memory agents this agent can use
121
122
  */
122
123
  memory?: MemoryAgent | MemoryAgent[];
124
+ afs?: true | AFSOptions | AFS | ((afs: AFS) => AFS);
123
125
  asyncMemoryRecord?: boolean;
124
126
  /**
125
127
  * Maximum number of memory items to retrieve
@@ -205,8 +207,11 @@ export declare abstract class Agent<I extends Message = any, O extends Message =
205
207
  constructor(options?: AgentOptions<I, O>);
206
208
  /**
207
209
  * List of memories this agent can use
210
+ *
211
+ * @deprecated use afs instead
208
212
  */
209
213
  readonly memories: MemoryAgent[];
214
+ afs?: AFS;
210
215
  asyncMemoryRecord?: boolean;
211
216
  tag?: string;
212
217
  /**
@@ -1,3 +1,4 @@
1
+ import { AFS } from "@aigne/afs";
1
2
  import { nodejs } from "@aigne/platform-helpers/nodejs/index.js";
2
3
  import equal from "fast-deep-equal";
3
4
  import nunjucks from "nunjucks";
@@ -103,6 +104,15 @@ export class Agent {
103
104
  else if (options.memory) {
104
105
  this.memories.push(options.memory);
105
106
  }
107
+ this.afs = !options.afs
108
+ ? undefined
109
+ : options.afs === true
110
+ ? new AFS()
111
+ : typeof options.afs === "function"
112
+ ? options.afs(new AFS())
113
+ : options.afs instanceof AFS
114
+ ? options.afs
115
+ : new AFS(options.afs);
106
116
  this.asyncMemoryRecord = options.asyncMemoryRecord;
107
117
  this.maxRetrieveMemoryCount = options.maxRetrieveMemoryCount;
108
118
  this.hooks = flat(options.hooks);
@@ -116,8 +126,11 @@ export class Agent {
116
126
  }
117
127
  /**
118
128
  * List of memories this agent can use
129
+ *
130
+ * @deprecated use afs instead
119
131
  */
120
132
  memories = [];
133
+ afs;
121
134
  asyncMemoryRecord;
122
135
  tag;
123
136
  /**
@@ -489,6 +502,7 @@ export class Agent {
489
502
  const o = await this.callHooks(["onSuccess", "onEnd"], { input, output: finalOutput }, options);
490
503
  if (o?.output)
491
504
  finalOutput = o.output;
505
+ this.afs?.emit("agentSucceed", { input, output: finalOutput });
492
506
  if (!this.disableEvents)
493
507
  context.emit("agentSucceed", { agent: this, output: finalOutput });
494
508
  return finalOutput;
@@ -1,6 +1,6 @@
1
1
  import { type ZodObject, type ZodType, z } from "zod";
2
2
  import { PromptBuilder } from "../prompt/prompt-builder.js";
3
- import { Agent, type AgentInvokeOptions, type AgentOptions, type AgentProcessAsyncGenerator, type Message } from "./agent.js";
3
+ import { Agent, type AgentInvokeOptions, type AgentOptions, type AgentProcessAsyncGenerator, type AgentProcessResult, type Message } from "./agent.js";
4
4
  import type { ChatModel, ChatModelInput } from "./chat-model.js";
5
5
  import type { GuideRailAgentOutput } from "./guide-rail-agent.js";
6
6
  import type { FileType } from "./model.js";
@@ -42,6 +42,12 @@ export interface AIAgentOptions<I extends Message = Message, O extends Message =
42
42
  * @default AIAgentToolChoice.auto
43
43
  */
44
44
  toolChoice?: AIAgentToolChoice | Agent;
45
+ /**
46
+ * Maximum number of tool calls to execute concurrently
47
+ *
48
+ * @default 1
49
+ */
50
+ toolCallsConcurrency?: number;
45
51
  /**
46
52
  * Whether to preserve text generated during tool usage in the final output
47
53
  */
@@ -237,6 +243,12 @@ export declare class AIAgent<I extends Message = any, O extends Message = any> e
237
243
  * {@includeCode ../../test/agents/ai-agent.test.ts#example-ai-agent-router}
238
244
  */
239
245
  toolChoice?: AIAgentToolChoice | Agent;
246
+ /**
247
+ * Maximum number of tool calls to execute concurrently
248
+ *
249
+ * @default 1
250
+ */
251
+ toolCallsConcurrency?: number;
240
252
  /**
241
253
  * Whether to preserve text generated during tool usage in the final output
242
254
  */
@@ -304,7 +316,8 @@ export declare class AIAgent<I extends Message = any, O extends Message = any> e
304
316
  *
305
317
  * @protected
306
318
  */
307
- process(input: I, options: AgentInvokeOptions): AgentProcessAsyncGenerator<O>;
319
+ process(input: I, options: AgentInvokeOptions): AgentProcessResult<O>;
320
+ private _process;
308
321
  protected onGuideRailError(error: GuideRailAgentOutput): Promise<O | GuideRailAgentOutput>;
309
322
  /**
310
323
  * Process router mode requests
@@ -1,3 +1,4 @@
1
+ import fastq from "fastq";
1
2
  import { z } from "zod";
2
3
  import { PromptBuilder } from "../prompt/prompt-builder.js";
3
4
  import { STRUCTURED_STREAM_INSTRUCTIONS } from "../prompt/prompts/structured-stream-instructions.js";
@@ -55,6 +56,7 @@ export const aiAgentOptionsSchema = agentOptionsSchema.extend({
55
56
  inputKey: z.string().optional(),
56
57
  outputKey: z.string().optional(),
57
58
  toolChoice: aiAgentToolChoiceSchema.optional(),
59
+ toolCallsConcurrency: z.number().int().min(0).optional(),
58
60
  keepTextInToolUses: z.boolean().optional(),
59
61
  memoryAgentsAsTools: z.boolean().optional(),
60
62
  memoryPromptTemplate: z.string().optional(),
@@ -114,6 +116,7 @@ export class AIAgent extends Agent {
114
116
  this.outputFileKey = options.outputFileKey || DEFAULT_OUTPUT_FILE_KEY;
115
117
  this.outputFileType = options.outputFileType;
116
118
  this.toolChoice = options.toolChoice;
119
+ this.toolCallsConcurrency = options.toolCallsConcurrency || 1;
117
120
  this.keepTextInToolUses = options.keepTextInToolUses;
118
121
  this.memoryAgentsAsTools = options.memoryAgentsAsTools;
119
122
  this.memoryPromptTemplate = options.memoryPromptTemplate;
@@ -170,6 +173,12 @@ export class AIAgent extends Agent {
170
173
  * {@includeCode ../../test/agents/ai-agent.test.ts#example-ai-agent-router}
171
174
  */
172
175
  toolChoice;
176
+ /**
177
+ * Maximum number of tool calls to execute concurrently
178
+ *
179
+ * @default 1
180
+ */
181
+ toolCallsConcurrency;
173
182
  /**
174
183
  * Whether to preserve text generated during tool usage in the final output
175
184
  */
@@ -232,7 +241,10 @@ export class AIAgent extends Agent {
232
241
  *
233
242
  * @protected
234
243
  */
235
- async *process(input, options) {
244
+ process(input, options) {
245
+ return this._process(input, options);
246
+ }
247
+ async *_process(input, options) {
236
248
  const model = this.model || options.model || options.context.model;
237
249
  if (!model)
238
250
  throw new Error("model is required to run AIAgent");
@@ -281,31 +293,43 @@ export class AIAgent extends Agent {
281
293
  yield { delta: { text: { [outputKey]: "\n" } } };
282
294
  }
283
295
  const executedToolCalls = [];
296
+ let error;
297
+ const queue = fastq.promise(async ({ tool, call }) => {
298
+ try {
299
+ // NOTE: should pass both arguments (model generated) and input (user provided) to the tool
300
+ const output = await this.invokeSkill(tool, { ...input, ...call.function.arguments }, options).catch((error) => {
301
+ if (!this.catchToolsError) {
302
+ return Promise.reject(error);
303
+ }
304
+ return {
305
+ isError: true,
306
+ error: {
307
+ message: error.message,
308
+ },
309
+ };
310
+ });
311
+ executedToolCalls.push({ call, output });
312
+ }
313
+ catch (e) {
314
+ error = e;
315
+ queue.killAndDrain();
316
+ }
317
+ }, this.toolCallsConcurrency || 1);
284
318
  // Execute tools
285
319
  for (const call of toolCalls) {
286
320
  const tool = toolsMap.get(call.function.name);
287
321
  if (!tool)
288
322
  throw new Error(`Tool not found: ${call.function.name}`);
289
- // NOTE: should pass both arguments (model generated) and input (user provided) to the tool
290
- const output = await this.invokeSkill(tool, { ...input, ...call.function.arguments }, options).catch((error) => {
291
- if (!this.catchToolsError) {
292
- return Promise.reject(error);
293
- }
294
- return {
295
- isError: true,
296
- error: {
297
- message: error.message,
298
- },
299
- };
300
- });
301
- // NOTE: Return transfer output immediately
302
- if (isTransferAgentOutput(output)) {
303
- return output;
304
- }
305
- executedToolCalls.push({ call, output });
323
+ queue.push({ tool, call });
306
324
  }
325
+ await queue.drained();
326
+ if (error)
327
+ throw error;
307
328
  // Continue LLM function calling loop if any tools were executed
308
329
  if (executedToolCalls.length) {
330
+ const transferOutput = executedToolCalls.find((i) => isTransferAgentOutput(i.output))?.output;
331
+ if (transferOutput)
332
+ return transferOutput;
309
333
  toolCallMessages.push(await AgentMessageTemplate.from(undefined, executedToolCalls.map(({ call }) => call)).format(), ...(await Promise.all(executedToolCalls.map(({ call, output }) => ToolMessageTemplate.from(output, call.id).format()))));
310
334
  continue;
311
335
  }
@@ -46,8 +46,10 @@ export interface AIAgentSchema extends BaseAgentSchema {
46
46
  type: "ai";
47
47
  instructions?: Instructions;
48
48
  inputKey?: string;
49
+ inputFileKey?: string;
49
50
  outputKey?: string;
50
51
  toolChoice?: AIAgentToolChoice;
52
+ toolCallsConcurrency?: number;
51
53
  keepTextInToolUses?: boolean;
52
54
  }
53
55
  export interface ImageAgentSchema extends BaseAgentSchema {
@@ -91,7 +91,10 @@ export async function parseAgentFile(path, data) {
91
91
  instructions: optionalize(instructionsSchema),
92
92
  inputKey: optionalize(z.string()),
93
93
  outputKey: optionalize(z.string()),
94
+ inputFileKey: optionalize(z.string()),
95
+ outputFileKey: optionalize(z.string()),
94
96
  toolChoice: optionalize(z.nativeEnum(AIAgentToolChoice)),
97
+ toolCallsConcurrency: optionalize(z.number().int().min(0)),
95
98
  keepTextInToolUses: optionalize(z.boolean()),
96
99
  structuredStreamMode: optionalize(z.boolean()),
97
100
  })
@@ -1,3 +1,4 @@
1
+ import { AFSHistory } from "@aigne/afs";
1
2
  import { nodejs } from "@aigne/platform-helpers/nodejs/index.js";
2
3
  import { stringify } from "yaml";
3
4
  import { ZodObject } from "zod";
@@ -7,7 +8,7 @@ import { DEFAULT_OUTPUT_FILE_KEY, DEFAULT_OUTPUT_KEY } from "../agents/ai-agent.
7
8
  import { fileUnionContentsSchema } from "../agents/model.js";
8
9
  import { optionalize } from "../loader/schema.js";
9
10
  import { outputSchemaToResponseFormatSchema } from "../utils/json-schema.js";
10
- import { checkArguments, flat, isRecord, unique } from "../utils/type-utils.js";
11
+ import { checkArguments, flat, isNonNullable, isRecord, unique } from "../utils/type-utils.js";
11
12
  import { MEMORY_MESSAGE_TEMPLATE } from "./prompts/memory-message-template.js";
12
13
  import { STRUCTURED_STREAM_INSTRUCTIONS } from "./prompts/structured-stream-instructions.js";
13
14
  import { AgentMessageTemplate, ChatMessagesTemplate, PromptTemplate, SystemMessageTemplate, UserMessageTemplate, } from "./template.js";
@@ -95,6 +96,18 @@ export class PromptBuilder {
95
96
  if (options.agent?.useMemoriesFromContext && options.context?.memories?.length) {
96
97
  memories.push(...options.context.memories);
97
98
  }
99
+ if (options.agent?.afs) {
100
+ const history = await options.agent.afs.list(AFSHistory.Path, {
101
+ limit: options.agent.maxRetrieveMemoryCount || 1,
102
+ orderBy: [["createdAt", "desc"]],
103
+ });
104
+ if (message) {
105
+ const result = await options.agent.afs.search("/", message);
106
+ const ms = result.list.map((entry) => ({ content: stringify(entry.content) }));
107
+ memories.push(...ms);
108
+ }
109
+ memories.push(...history.list.filter((i) => isNonNullable(i.content)));
110
+ }
98
111
  if (memories.length)
99
112
  messages.push(...(await this.convertMemoriesToMessages(memories, options)));
100
113
  // if the agent is using structured stream mode, add the instructions
@@ -1,4 +1,4 @@
1
- import type { AgentHooks } from "../agents/agent.ts";
1
+ import type { AgentHooks } from "../agents/agent.js";
2
2
  import type { AIGNECLIAgents } from "../aigne/type.js";
3
3
  export declare function sortHooks(hooks: AgentHooks[]): AgentHooks[];
4
4
  export interface CLIAgent<T> {
@@ -9,4 +9,4 @@ export interface CLIAgent<T> {
9
9
  agents?: CLIAgent<T>[];
10
10
  }
11
11
  export declare function mapCliAgent<A, O>({ agent, agents, ...input }: CLIAgent<A>, transform: (input: A) => O): CLIAgent<O>;
12
- export declare function findCliAgent(cli: AIGNECLIAgents, parent: string[] | "*", name: string): import("../agents/agent.ts").Agent<any, any> | undefined;
12
+ export declare function findCliAgent(cli: AIGNECLIAgents, parent: string[] | "*", name: string): import("../agents/agent.js").Agent<any, any> | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aigne/core",
3
- "version": "1.62.0",
3
+ "version": "1.63.0-beta.1",
4
4
  "description": "The functional core of agentic AI",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -92,7 +92,8 @@
92
92
  "zod": "^3.25.67",
93
93
  "zod-from-json-schema": "^0.0.5",
94
94
  "zod-to-json-schema": "^3.24.6",
95
- "@aigne/observability-api": "^0.11.1",
95
+ "@aigne/afs": "^1.0.0",
96
+ "@aigne/observability-api": "^0.11.2-beta",
96
97
  "@aigne/platform-helpers": "^0.6.3"
97
98
  },
98
99
  "devDependencies": {