@aigne/core 1.63.0-beta → 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,17 @@
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
+
3
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)
4
16
 
5
17
 
@@ -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
  */
@@ -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
  */
@@ -287,31 +299,43 @@ class AIAgent extends agent_js_1.Agent {
287
299
  yield { delta: { text: { [outputKey]: "\n" } } };
288
300
  }
289
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);
290
324
  // Execute tools
291
325
  for (const call of toolCalls) {
292
326
  const tool = toolsMap.get(call.function.name);
293
327
  if (!tool)
294
328
  throw new Error(`Tool not found: ${call.function.name}`);
295
- // NOTE: should pass both arguments (model generated) and input (user provided) to the tool
296
- const output = await this.invokeSkill(tool, { ...input, ...call.function.arguments }, options).catch((error) => {
297
- if (!this.catchToolsError) {
298
- return Promise.reject(error);
299
- }
300
- return {
301
- isError: true,
302
- error: {
303
- message: error.message,
304
- },
305
- };
306
- });
307
- // NOTE: Return transfer output immediately
308
- if ((0, types_js_1.isTransferAgentOutput)(output)) {
309
- return output;
310
- }
311
- executedToolCalls.push({ call, output });
329
+ queue.push({ tool, call });
312
330
  }
331
+ await queue.drained();
332
+ if (error)
333
+ throw error;
313
334
  // Continue LLM function calling loop if any tools were executed
314
335
  if (executedToolCalls.length) {
336
+ const transferOutput = executedToolCalls.find((i) => (0, types_js_1.isTransferAgentOutput)(i.output))?.output;
337
+ if (transferOutput)
338
+ return transferOutput;
315
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()))));
316
340
  continue;
317
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
  })
@@ -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
  */
@@ -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 {
@@ -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
  */
@@ -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
  */
@@ -284,31 +293,43 @@ export class AIAgent extends Agent {
284
293
  yield { delta: { text: { [outputKey]: "\n" } } };
285
294
  }
286
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);
287
318
  // Execute tools
288
319
  for (const call of toolCalls) {
289
320
  const tool = toolsMap.get(call.function.name);
290
321
  if (!tool)
291
322
  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 (isTransferAgentOutput(output)) {
306
- return output;
307
- }
308
- executedToolCalls.push({ call, output });
323
+ queue.push({ tool, call });
309
324
  }
325
+ await queue.drained();
326
+ if (error)
327
+ throw error;
310
328
  // Continue LLM function calling loop if any tools were executed
311
329
  if (executedToolCalls.length) {
330
+ const transferOutput = executedToolCalls.find((i) => isTransferAgentOutput(i.output))?.output;
331
+ if (transferOutput)
332
+ return transferOutput;
312
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()))));
313
334
  continue;
314
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
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aigne/core",
3
- "version": "1.63.0-beta",
3
+ "version": "1.63.0-beta.1",
4
4
  "description": "The functional core of agentic AI",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -92,9 +92,9 @@
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/afs": "^1.0.0",
95
96
  "@aigne/observability-api": "^0.11.2-beta",
96
- "@aigne/platform-helpers": "^0.6.3",
97
- "@aigne/afs": "^1.0.0"
97
+ "@aigne/platform-helpers": "^0.6.3"
98
98
  },
99
99
  "devDependencies": {
100
100
  "@types/bun": "^1.2.22",