@aigne/core 1.63.0-beta → 1.63.0-beta.10
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 +97 -0
- package/lib/cjs/agents/agent.d.ts +8 -1
- package/lib/cjs/agents/agent.js +14 -4
- package/lib/cjs/agents/ai-agent.d.ts +18 -1
- package/lib/cjs/agents/ai-agent.js +60 -17
- package/lib/cjs/agents/chat-model.js +1 -1
- package/lib/cjs/loader/agent-yaml.d.ts +14 -1
- package/lib/cjs/loader/agent-yaml.js +24 -0
- package/lib/cjs/loader/index.d.ts +7 -0
- package/lib/cjs/loader/index.js +22 -0
- package/lib/cjs/prompt/prompt-builder.d.ts +1 -0
- package/lib/cjs/prompt/prompt-builder.js +57 -16
- package/lib/cjs/prompt/prompts/afs-builtin-prompt.d.ts +2 -0
- package/lib/cjs/prompt/prompts/afs-builtin-prompt.js +23 -0
- package/lib/cjs/prompt/skills/afs.d.ts +3 -0
- package/lib/cjs/prompt/skills/afs.js +75 -0
- package/lib/cjs/utils/type-utils.d.ts +1 -0
- package/lib/cjs/utils/type-utils.js +14 -0
- package/lib/dts/agents/agent.d.ts +8 -1
- package/lib/dts/agents/ai-agent.d.ts +18 -1
- package/lib/dts/loader/agent-yaml.d.ts +14 -1
- package/lib/dts/loader/index.d.ts +7 -0
- package/lib/dts/prompt/prompt-builder.d.ts +1 -0
- package/lib/dts/prompt/prompts/afs-builtin-prompt.d.ts +2 -0
- package/lib/dts/prompt/skills/afs.d.ts +3 -0
- package/lib/dts/utils/type-utils.d.ts +1 -0
- package/lib/esm/agents/agent.d.ts +8 -1
- package/lib/esm/agents/agent.js +14 -4
- package/lib/esm/agents/ai-agent.d.ts +18 -1
- package/lib/esm/agents/ai-agent.js +57 -17
- package/lib/esm/agents/chat-model.js +1 -1
- package/lib/esm/loader/agent-yaml.d.ts +14 -1
- package/lib/esm/loader/agent-yaml.js +24 -0
- package/lib/esm/loader/index.d.ts +7 -0
- package/lib/esm/loader/index.js +22 -0
- package/lib/esm/prompt/prompt-builder.d.ts +1 -0
- package/lib/esm/prompt/prompt-builder.js +58 -17
- package/lib/esm/prompt/prompts/afs-builtin-prompt.d.ts +2 -0
- package/lib/esm/prompt/prompts/afs-builtin-prompt.js +20 -0
- package/lib/esm/prompt/skills/afs.d.ts +3 -0
- package/lib/esm/prompt/skills/afs.js +72 -0
- package/lib/esm/utils/type-utils.d.ts +1 -0
- package/lib/esm/utils/type-utils.js +13 -0
- package/package.json +3 -3
|
@@ -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";
|
|
@@ -5,6 +6,7 @@ import { AgentMessageTemplate, ToolMessageTemplate } from "../prompt/template.js
|
|
|
5
6
|
import { ExtractMetadataTransform } from "../utils/structured-stream-extractor.js";
|
|
6
7
|
import { checkArguments, isEmpty } from "../utils/type-utils.js";
|
|
7
8
|
import { Agent, agentOptionsSchema, isAgentResponseDelta, } from "./agent.js";
|
|
9
|
+
import { fileUnionContentsSchema } from "./model.js";
|
|
8
10
|
import { isTransferAgentOutput } from "./types.js";
|
|
9
11
|
export const DEFAULT_OUTPUT_KEY = "message";
|
|
10
12
|
export const DEFAULT_OUTPUT_FILE_KEY = "files";
|
|
@@ -55,6 +57,7 @@ export const aiAgentOptionsSchema = agentOptionsSchema.extend({
|
|
|
55
57
|
inputKey: z.string().optional(),
|
|
56
58
|
outputKey: z.string().optional(),
|
|
57
59
|
toolChoice: aiAgentToolChoiceSchema.optional(),
|
|
60
|
+
toolCallsConcurrency: z.number().int().min(0).optional(),
|
|
58
61
|
keepTextInToolUses: z.boolean().optional(),
|
|
59
62
|
memoryAgentsAsTools: z.boolean().optional(),
|
|
60
63
|
memoryPromptTemplate: z.string().optional(),
|
|
@@ -108,12 +111,15 @@ export class AIAgent extends Agent {
|
|
|
108
111
|
typeof options.instructions === "string"
|
|
109
112
|
? PromptBuilder.from(options.instructions)
|
|
110
113
|
: (options.instructions ?? new PromptBuilder());
|
|
114
|
+
this.autoReorderSystemMessages = options.autoReorderSystemMessages ?? false;
|
|
115
|
+
this.autoMergeSystemMessages = options.autoMergeSystemMessages ?? false;
|
|
111
116
|
this.inputKey = options.inputKey;
|
|
112
117
|
this.inputFileKey = options.inputFileKey;
|
|
113
118
|
this.outputKey = options.outputKey || DEFAULT_OUTPUT_KEY;
|
|
114
119
|
this.outputFileKey = options.outputFileKey || DEFAULT_OUTPUT_FILE_KEY;
|
|
115
120
|
this.outputFileType = options.outputFileType;
|
|
116
121
|
this.toolChoice = options.toolChoice;
|
|
122
|
+
this.toolCallsConcurrency = options.toolCallsConcurrency || 1;
|
|
117
123
|
this.keepTextInToolUses = options.keepTextInToolUses;
|
|
118
124
|
this.memoryAgentsAsTools = options.memoryAgentsAsTools;
|
|
119
125
|
this.memoryPromptTemplate = options.memoryPromptTemplate;
|
|
@@ -143,6 +149,8 @@ export class AIAgent extends Agent {
|
|
|
143
149
|
* {@includeCode ../../test/agents/ai-agent.test.ts#example-ai-agent-prompt-builder}
|
|
144
150
|
*/
|
|
145
151
|
instructions;
|
|
152
|
+
autoReorderSystemMessages;
|
|
153
|
+
autoMergeSystemMessages;
|
|
146
154
|
/**
|
|
147
155
|
* Pick a message from input to use as the user's message
|
|
148
156
|
*/
|
|
@@ -170,6 +178,12 @@ export class AIAgent extends Agent {
|
|
|
170
178
|
* {@includeCode ../../test/agents/ai-agent.test.ts#example-ai-agent-router}
|
|
171
179
|
*/
|
|
172
180
|
toolChoice;
|
|
181
|
+
/**
|
|
182
|
+
* Maximum number of tool calls to execute concurrently
|
|
183
|
+
*
|
|
184
|
+
* @default 1
|
|
185
|
+
*/
|
|
186
|
+
toolCallsConcurrency;
|
|
173
187
|
/**
|
|
174
188
|
* Whether to preserve text generated during tool usage in the final output
|
|
175
189
|
*/
|
|
@@ -227,6 +241,20 @@ export class AIAgent extends Agent {
|
|
|
227
241
|
* which outputs structured data in YAML format within <metadata> tags.
|
|
228
242
|
*/
|
|
229
243
|
customStructuredStreamInstructions;
|
|
244
|
+
get inputSchema() {
|
|
245
|
+
let schema = super.inputSchema;
|
|
246
|
+
if (this.inputKey) {
|
|
247
|
+
schema = schema.extend({
|
|
248
|
+
[this.inputKey]: z.string().nullish(),
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
if (this.inputFileKey) {
|
|
252
|
+
schema = schema.extend({
|
|
253
|
+
[this.inputFileKey]: fileUnionContentsSchema.nullish(),
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
return schema;
|
|
257
|
+
}
|
|
230
258
|
/**
|
|
231
259
|
* Process an input message and generate a response
|
|
232
260
|
*
|
|
@@ -284,31 +312,43 @@ export class AIAgent extends Agent {
|
|
|
284
312
|
yield { delta: { text: { [outputKey]: "\n" } } };
|
|
285
313
|
}
|
|
286
314
|
const executedToolCalls = [];
|
|
315
|
+
let error;
|
|
316
|
+
const queue = fastq.promise(async ({ tool, call }) => {
|
|
317
|
+
try {
|
|
318
|
+
// NOTE: should pass both arguments (model generated) and input (user provided) to the tool
|
|
319
|
+
const output = await this.invokeSkill(tool, { ...input, ...call.function.arguments }, options).catch((error) => {
|
|
320
|
+
if (!this.catchToolsError) {
|
|
321
|
+
return Promise.reject(error);
|
|
322
|
+
}
|
|
323
|
+
return {
|
|
324
|
+
isError: true,
|
|
325
|
+
error: {
|
|
326
|
+
message: error.message,
|
|
327
|
+
},
|
|
328
|
+
};
|
|
329
|
+
});
|
|
330
|
+
executedToolCalls.push({ call, output });
|
|
331
|
+
}
|
|
332
|
+
catch (e) {
|
|
333
|
+
error = e;
|
|
334
|
+
queue.killAndDrain();
|
|
335
|
+
}
|
|
336
|
+
}, this.toolCallsConcurrency || 1);
|
|
287
337
|
// Execute tools
|
|
288
338
|
for (const call of toolCalls) {
|
|
289
339
|
const tool = toolsMap.get(call.function.name);
|
|
290
340
|
if (!tool)
|
|
291
341
|
throw new Error(`Tool not found: ${call.function.name}`);
|
|
292
|
-
|
|
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 });
|
|
342
|
+
queue.push({ tool, call });
|
|
309
343
|
}
|
|
344
|
+
await queue.drained();
|
|
345
|
+
if (error)
|
|
346
|
+
throw error;
|
|
310
347
|
// Continue LLM function calling loop if any tools were executed
|
|
311
348
|
if (executedToolCalls.length) {
|
|
349
|
+
const transferOutput = executedToolCalls.find((i) => isTransferAgentOutput(i.output))?.output;
|
|
350
|
+
if (transferOutput)
|
|
351
|
+
return transferOutput;
|
|
312
352
|
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
353
|
continue;
|
|
314
354
|
}
|
|
@@ -6,7 +6,7 @@ import { checkArguments, isNil, omitByDeep } from "../utils/type-utils.js";
|
|
|
6
6
|
import { agentOptionsSchema, } from "./agent.js";
|
|
7
7
|
import { fileContentSchema, fileUnionContentSchema, localContentSchema, Model, urlContentSchema, } from "./model.js";
|
|
8
8
|
const CHAT_MODEL_DEFAULT_RETRY_OPTIONS = {
|
|
9
|
-
retries:
|
|
9
|
+
retries: 10,
|
|
10
10
|
shouldRetry: async (error) => error instanceof StructuredOutputError || (await import("is-network-error")).default(error),
|
|
11
11
|
};
|
|
12
12
|
export class StructuredOutputError extends Error {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import type { AFSOptions } from "@aigne/afs";
|
|
1
2
|
import { type ZodType, z } from "zod";
|
|
2
|
-
import type { AgentHooks, FunctionAgentFn, TaskRenderMode } from "../agents/agent.js";
|
|
3
|
+
import type { AFSConfig, AgentHooks, FunctionAgentFn, TaskRenderMode } from "../agents/agent.js";
|
|
3
4
|
import { AIAgentToolChoice } from "../agents/ai-agent.js";
|
|
4
5
|
import { type Role } from "../agents/chat-model.js";
|
|
5
6
|
import { ProcessMode, type ReflectionMode } from "../agents/team-agent.js";
|
|
@@ -19,6 +20,10 @@ export type NestAgentSchema = string | {
|
|
|
19
20
|
defaultInput?: Record<string, any>;
|
|
20
21
|
hooks?: HooksSchema | HooksSchema[];
|
|
21
22
|
} | AgentSchema;
|
|
23
|
+
export type AFSModuleSchema = string | {
|
|
24
|
+
module: string;
|
|
25
|
+
options?: Record<string, any>;
|
|
26
|
+
};
|
|
22
27
|
export interface BaseAgentSchema {
|
|
23
28
|
name?: string;
|
|
24
29
|
description?: string;
|
|
@@ -36,6 +41,10 @@ export interface BaseAgentSchema {
|
|
|
36
41
|
provider: string;
|
|
37
42
|
subscribeTopic?: string[];
|
|
38
43
|
};
|
|
44
|
+
afs?: boolean | (Omit<AFSOptions, "modules"> & {
|
|
45
|
+
modules?: AFSModuleSchema[];
|
|
46
|
+
});
|
|
47
|
+
afsConfig?: AFSConfig;
|
|
39
48
|
}
|
|
40
49
|
export type Instructions = {
|
|
41
50
|
role: Exclude<Role, "tool">;
|
|
@@ -45,9 +54,13 @@ export type Instructions = {
|
|
|
45
54
|
export interface AIAgentSchema extends BaseAgentSchema {
|
|
46
55
|
type: "ai";
|
|
47
56
|
instructions?: Instructions;
|
|
57
|
+
autoReorderSystemMessages?: boolean;
|
|
58
|
+
autoMergeSystemMessages?: boolean;
|
|
48
59
|
inputKey?: string;
|
|
60
|
+
inputFileKey?: string;
|
|
49
61
|
outputKey?: string;
|
|
50
62
|
toolChoice?: AIAgentToolChoice;
|
|
63
|
+
toolCallsConcurrency?: number;
|
|
51
64
|
keepTextInToolUses?: boolean;
|
|
52
65
|
}
|
|
53
66
|
export interface ImageAgentSchema extends BaseAgentSchema {
|
|
@@ -49,6 +49,25 @@ export async function parseAgentFile(path, data) {
|
|
|
49
49
|
subscribeTopic: optionalize(z.array(z.string())),
|
|
50
50
|
})),
|
|
51
51
|
])),
|
|
52
|
+
afs: optionalize(z.union([
|
|
53
|
+
z.boolean(),
|
|
54
|
+
camelizeSchema(z.object({
|
|
55
|
+
storage: optionalize(z.object({
|
|
56
|
+
url: optionalize(z.string()),
|
|
57
|
+
})),
|
|
58
|
+
modules: optionalize(z.array(z.union([
|
|
59
|
+
z.string(),
|
|
60
|
+
z.object({
|
|
61
|
+
module: z.string(),
|
|
62
|
+
options: optionalize(z.record(z.any())),
|
|
63
|
+
}),
|
|
64
|
+
]))),
|
|
65
|
+
})),
|
|
66
|
+
])),
|
|
67
|
+
afsConfig: optionalize(camelizeSchema(z.object({
|
|
68
|
+
injectHistory: optionalize(z.boolean()),
|
|
69
|
+
historyWindowSize: optionalize(z.number().int().min(1)),
|
|
70
|
+
}))),
|
|
52
71
|
});
|
|
53
72
|
const instructionItemSchema = z.union([
|
|
54
73
|
z.object({
|
|
@@ -89,9 +108,14 @@ export async function parseAgentFile(path, data) {
|
|
|
89
108
|
.object({
|
|
90
109
|
type: z.literal("ai"),
|
|
91
110
|
instructions: optionalize(instructionsSchema),
|
|
111
|
+
autoReorderSystemMessages: optionalize(z.boolean()),
|
|
112
|
+
autoMergeSystemMessages: optionalize(z.boolean()),
|
|
92
113
|
inputKey: optionalize(z.string()),
|
|
93
114
|
outputKey: optionalize(z.string()),
|
|
115
|
+
inputFileKey: optionalize(z.string()),
|
|
116
|
+
outputFileKey: optionalize(z.string()),
|
|
94
117
|
toolChoice: optionalize(z.nativeEnum(AIAgentToolChoice)),
|
|
118
|
+
toolCallsConcurrency: optionalize(z.number().int().min(0)),
|
|
95
119
|
keepTextInToolUses: optionalize(z.boolean()),
|
|
96
120
|
structuredStreamMode: optionalize(z.boolean()),
|
|
97
121
|
})
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type AFSModule } from "@aigne/afs";
|
|
1
2
|
import { type ZodType, z } from "zod";
|
|
2
3
|
import { Agent, type AgentOptions } from "../agents/agent.js";
|
|
3
4
|
import type { ChatModel } from "../agents/chat-model.js";
|
|
@@ -11,6 +12,12 @@ export interface LoadOptions {
|
|
|
11
12
|
}[];
|
|
12
13
|
model?: ChatModel | ((model?: z.infer<typeof aigneFileSchema>["model"]) => PromiseOrValue<ChatModel | undefined>);
|
|
13
14
|
imageModel?: ImageModel | ((model?: z.infer<typeof aigneFileSchema>["imageModel"]) => PromiseOrValue<ImageModel | undefined>);
|
|
15
|
+
afs?: {
|
|
16
|
+
availableModules?: {
|
|
17
|
+
module: string;
|
|
18
|
+
create: (options?: Record<string, any>) => PromiseOrValue<AFSModule>;
|
|
19
|
+
}[];
|
|
20
|
+
};
|
|
14
21
|
}
|
|
15
22
|
export declare function load(path: string, options?: LoadOptions): Promise<AIGNEOptions>;
|
|
16
23
|
export declare function loadAgent(path: string, options?: LoadOptions, agentOptions?: AgentOptions): Promise<Agent>;
|
package/lib/esm/loader/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AFS } from "@aigne/afs";
|
|
1
2
|
import { nodejs } from "@aigne/platform-helpers/nodejs/index.js";
|
|
2
3
|
import { parse } from "yaml";
|
|
3
4
|
import { z } from "zod";
|
|
@@ -107,6 +108,26 @@ async function parseAgent(path, agent, options, agentOptions) {
|
|
|
107
108
|
const memory = "memory" in agent && options?.memories?.length
|
|
108
109
|
? await loadMemory(options.memories, typeof agent.memory === "object" ? agent.memory.provider : undefined, typeof agent.memory === "object" ? agent.memory : {})
|
|
109
110
|
: undefined;
|
|
111
|
+
let afs;
|
|
112
|
+
if (typeof agent.afs === "boolean") {
|
|
113
|
+
if (agent.afs) {
|
|
114
|
+
afs = new AFS();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else if (agent.afs) {
|
|
118
|
+
afs = new AFS({
|
|
119
|
+
...agent.afs,
|
|
120
|
+
modules: agent.afs.modules &&
|
|
121
|
+
(await Promise.all(agent.afs.modules.map((m) => {
|
|
122
|
+
const mod = typeof m === "string"
|
|
123
|
+
? options?.afs?.availableModules?.find((mod) => mod.module === m)
|
|
124
|
+
: options?.afs?.availableModules?.find((mod) => mod.module === m.module);
|
|
125
|
+
if (!mod)
|
|
126
|
+
throw new Error(`AFS module not found: ${typeof m === "string" ? m : m.module}`);
|
|
127
|
+
return mod.create(typeof m === "string" ? {} : m.options);
|
|
128
|
+
}))),
|
|
129
|
+
});
|
|
130
|
+
}
|
|
110
131
|
const model = agent.model && typeof options?.model === "function"
|
|
111
132
|
? await options.model(agent.model)
|
|
112
133
|
: undefined;
|
|
@@ -124,6 +145,7 @@ async function parseAgent(path, agent, options, agentOptions) {
|
|
|
124
145
|
...((await parseHooks(path, agent.hooks, options)) ?? []),
|
|
125
146
|
...[agentOptions?.hooks].flat().filter(isNonNullable),
|
|
126
147
|
],
|
|
148
|
+
afs,
|
|
127
149
|
};
|
|
128
150
|
let instructions;
|
|
129
151
|
if ("instructions" in agent && agent.instructions) {
|
|
@@ -8,9 +8,11 @@ import { DEFAULT_OUTPUT_FILE_KEY, DEFAULT_OUTPUT_KEY } from "../agents/ai-agent.
|
|
|
8
8
|
import { fileUnionContentsSchema } from "../agents/model.js";
|
|
9
9
|
import { optionalize } from "../loader/schema.js";
|
|
10
10
|
import { outputSchemaToResponseFormatSchema } from "../utils/json-schema.js";
|
|
11
|
-
import { checkArguments, flat, isNonNullable, isRecord, unique } from "../utils/type-utils.js";
|
|
11
|
+
import { checkArguments, flat, isNonNullable, isRecord, partition, unique, } from "../utils/type-utils.js";
|
|
12
|
+
import { getAFSSystemPrompt } from "./prompts/afs-builtin-prompt.js";
|
|
12
13
|
import { MEMORY_MESSAGE_TEMPLATE } from "./prompts/memory-message-template.js";
|
|
13
14
|
import { STRUCTURED_STREAM_INSTRUCTIONS } from "./prompts/structured-stream-instructions.js";
|
|
15
|
+
import { getAFSSkills } from "./skills/afs.js";
|
|
14
16
|
import { AgentMessageTemplate, ChatMessagesTemplate, PromptTemplate, SystemMessageTemplate, UserMessageTemplate, } from "./template.js";
|
|
15
17
|
export class PromptBuilder {
|
|
16
18
|
static from(instructions, { workingDir } = {}) {
|
|
@@ -67,7 +69,7 @@ export class PromptBuilder {
|
|
|
67
69
|
? undefined
|
|
68
70
|
: this.buildResponseFormat(options),
|
|
69
71
|
outputFileType: options.agent?.outputFileType,
|
|
70
|
-
...this.buildTools(options),
|
|
72
|
+
...(await this.buildTools(options)),
|
|
71
73
|
};
|
|
72
74
|
}
|
|
73
75
|
async buildImagePrompt(options) {
|
|
@@ -82,9 +84,9 @@ export class PromptBuilder {
|
|
|
82
84
|
const { input } = options;
|
|
83
85
|
const inputKey = options.agent?.inputKey;
|
|
84
86
|
const message = inputKey && typeof input?.[inputKey] === "string" ? input[inputKey] : undefined;
|
|
85
|
-
const messages = (await (typeof this.instructions === "string"
|
|
87
|
+
const [messages, otherCustomMessages] = partition((await (typeof this.instructions === "string"
|
|
86
88
|
? ChatMessagesTemplate.from([SystemMessageTemplate.from(this.instructions)])
|
|
87
|
-
: this.instructions)?.format(options.input, { workingDir: this.workingDir })) ?? [];
|
|
89
|
+
: this.instructions)?.format(options.input, { workingDir: this.workingDir })) ?? [], (i) => i.role === "system");
|
|
88
90
|
const inputFileKey = options.agent?.inputFileKey;
|
|
89
91
|
const files = flat(inputFileKey
|
|
90
92
|
? checkArguments("Check input files", optionalize(fileUnionContentsSchema), input?.[inputFileKey])
|
|
@@ -97,16 +99,19 @@ export class PromptBuilder {
|
|
|
97
99
|
memories.push(...options.context.memories);
|
|
98
100
|
}
|
|
99
101
|
if (options.agent?.afs) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
102
|
+
messages.push(await SystemMessageTemplate.from(await getAFSSystemPrompt(options.agent.afs)).format({}));
|
|
103
|
+
if (options.agent.afsConfig?.injectHistory) {
|
|
104
|
+
const history = await options.agent.afs.list(AFSHistory.Path, {
|
|
105
|
+
limit: options.agent.afsConfig.historyWindowSize || 10,
|
|
106
|
+
orderBy: [["createdAt", "desc"]],
|
|
107
|
+
});
|
|
108
|
+
if (message) {
|
|
109
|
+
const result = await options.agent.afs.search("/", message);
|
|
110
|
+
const ms = result.list.map((entry) => ({ content: stringify(entry.content) }));
|
|
111
|
+
memories.push(...ms);
|
|
112
|
+
}
|
|
113
|
+
memories.push(...history.list.filter((i) => isNonNullable(i.content)));
|
|
108
114
|
}
|
|
109
|
-
memories.push(...history.list.filter((i) => isNonNullable(i.content)));
|
|
110
115
|
}
|
|
111
116
|
if (memories.length)
|
|
112
117
|
messages.push(...(await this.convertMemoriesToMessages(memories, options)));
|
|
@@ -124,13 +129,46 @@ export class PromptBuilder {
|
|
|
124
129
|
}
|
|
125
130
|
if (message || files.length) {
|
|
126
131
|
const content = [];
|
|
127
|
-
if (message
|
|
132
|
+
if (message &&
|
|
133
|
+
// avoid duplicate user messages: developer may have already included the message in the custom user messages
|
|
134
|
+
!otherCustomMessages.some((i) => i.role === "user" &&
|
|
135
|
+
(typeof i.content === "string"
|
|
136
|
+
? i.content.includes(message)
|
|
137
|
+
: i.content?.some((c) => c.type === "text" && c.text.includes(message))))) {
|
|
128
138
|
content.push({ type: "text", text: message });
|
|
139
|
+
}
|
|
129
140
|
if (files.length)
|
|
130
141
|
content.push(...files);
|
|
131
|
-
|
|
142
|
+
if (content.length) {
|
|
143
|
+
messages.push({ role: "user", content });
|
|
144
|
+
}
|
|
132
145
|
}
|
|
133
|
-
|
|
146
|
+
messages.push(...otherCustomMessages);
|
|
147
|
+
return this.refineMessages(options, messages);
|
|
148
|
+
}
|
|
149
|
+
refineMessages(options, messages) {
|
|
150
|
+
const { autoReorderSystemMessages, autoMergeSystemMessages } = options.agent ?? {};
|
|
151
|
+
if (!autoReorderSystemMessages && !autoMergeSystemMessages)
|
|
152
|
+
return messages;
|
|
153
|
+
const [systemMessages, otherMessages] = partition(messages, (m) => m.role === "system");
|
|
154
|
+
if (!autoMergeSystemMessages) {
|
|
155
|
+
return systemMessages.concat(otherMessages);
|
|
156
|
+
}
|
|
157
|
+
const result = [];
|
|
158
|
+
if (systemMessages.length) {
|
|
159
|
+
result.push({
|
|
160
|
+
role: "system",
|
|
161
|
+
content: systemMessages
|
|
162
|
+
.map((i) => typeof i.content === "string"
|
|
163
|
+
? i.content
|
|
164
|
+
: i.content
|
|
165
|
+
?.map((c) => (c.type === "text" ? c.text : null))
|
|
166
|
+
.filter(isNonNullable)
|
|
167
|
+
.join("\n"))
|
|
168
|
+
.join("\n"),
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
return result.concat(otherMessages);
|
|
134
172
|
}
|
|
135
173
|
async convertMemoriesToMessages(memories, options) {
|
|
136
174
|
const messages = [];
|
|
@@ -213,11 +251,14 @@ export class PromptBuilder {
|
|
|
213
251
|
}
|
|
214
252
|
: undefined;
|
|
215
253
|
}
|
|
216
|
-
buildTools(options) {
|
|
254
|
+
async buildTools(options) {
|
|
217
255
|
const toolAgents = unique((options.context?.skills ?? [])
|
|
218
256
|
.concat(options.agent?.skills ?? [])
|
|
219
257
|
.concat(options.agent?.memoryAgentsAsTools ? options.agent.memories : [])
|
|
220
258
|
.flatMap((i) => (i.isInvokable ? i : i.skills)), (i) => i.name);
|
|
259
|
+
if (options.agent?.afs) {
|
|
260
|
+
toolAgents.push(...(await getAFSSkills(options.agent.afs)));
|
|
261
|
+
}
|
|
221
262
|
const tools = toolAgents.map((i) => ({
|
|
222
263
|
type: "function",
|
|
223
264
|
function: {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { stringify } from "yaml";
|
|
2
|
+
export async function getAFSSystemPrompt(afs) {
|
|
3
|
+
return `\
|
|
4
|
+
|
|
5
|
+
<afs_usage>
|
|
6
|
+
AFS (AIGNE File System) provides tools to interact with a virtual file system, allowing you to list, search, read, and write files. Use these tools to manage and retrieve files as needed.
|
|
7
|
+
|
|
8
|
+
Modules:
|
|
9
|
+
${stringify(await afs.listModules())}
|
|
10
|
+
|
|
11
|
+
Available Tools:
|
|
12
|
+
1. afs_list: Browse directory contents like filesystem ls/tree command - shows files and folders in a given path
|
|
13
|
+
2. afs_search: Find files by content keywords - use specific keywords related to what you're looking for
|
|
14
|
+
3. afs_read: Read file contents - path must be an exact file path from list or search results
|
|
15
|
+
4. afs_write: Write content to a file in the AFS
|
|
16
|
+
|
|
17
|
+
Workflow: Use afs_list to browse directories, afs_search to find specific content, then afs_read to access file contents.
|
|
18
|
+
</afs_usage>
|
|
19
|
+
`;
|
|
20
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { FunctionAgent } from "../../agents/agent.js";
|
|
3
|
+
export async function getAFSSkills(afs) {
|
|
4
|
+
return [
|
|
5
|
+
FunctionAgent.from({
|
|
6
|
+
name: "afs_list",
|
|
7
|
+
description: "Browse directory contents in the AFS like filesystem ls/tree command - shows files and folders in the specified path",
|
|
8
|
+
inputSchema: z.object({
|
|
9
|
+
path: z.string().describe("The directory path to browse (e.g., '/', '/docs', '/src')"),
|
|
10
|
+
options: z
|
|
11
|
+
.object({
|
|
12
|
+
recursive: z.boolean().optional().describe("Whether to list files recursively"),
|
|
13
|
+
maxDepth: z.number().optional().describe("Maximum depth to list files"),
|
|
14
|
+
limit: z.number().optional().describe("Maximum number of entries to return"),
|
|
15
|
+
})
|
|
16
|
+
.optional(),
|
|
17
|
+
}),
|
|
18
|
+
process: async (input) => {
|
|
19
|
+
return afs.list(input.path, input.options);
|
|
20
|
+
},
|
|
21
|
+
}),
|
|
22
|
+
FunctionAgent.from({
|
|
23
|
+
name: "afs_search",
|
|
24
|
+
description: "Find files by searching content using keywords - returns matching files with their paths",
|
|
25
|
+
inputSchema: z.object({
|
|
26
|
+
path: z.string().describe("The directory path to search in (e.g., '/', '/docs')"),
|
|
27
|
+
query: z
|
|
28
|
+
.string()
|
|
29
|
+
.describe("Keywords to search for in file contents (e.g., 'function authentication', 'database config')"),
|
|
30
|
+
options: z
|
|
31
|
+
.object({
|
|
32
|
+
limit: z.number().optional().describe("Maximum number of entries to return"),
|
|
33
|
+
})
|
|
34
|
+
.optional(),
|
|
35
|
+
}),
|
|
36
|
+
process: async (input) => {
|
|
37
|
+
return afs.search(input.path, input.query, input.options);
|
|
38
|
+
},
|
|
39
|
+
}),
|
|
40
|
+
FunctionAgent.from({
|
|
41
|
+
name: "afs_read",
|
|
42
|
+
description: "Read file contents from the AFS - path must be an exact file path from list or search results",
|
|
43
|
+
inputSchema: z.object({
|
|
44
|
+
path: z
|
|
45
|
+
.string()
|
|
46
|
+
.describe("Exact file path from list or search results (e.g., '/docs/api.md', '/src/utils/helper.js')"),
|
|
47
|
+
}),
|
|
48
|
+
process: async (input) => {
|
|
49
|
+
const file = await afs.read(input.path);
|
|
50
|
+
return {
|
|
51
|
+
file,
|
|
52
|
+
};
|
|
53
|
+
},
|
|
54
|
+
}),
|
|
55
|
+
FunctionAgent.from({
|
|
56
|
+
name: "afs_write",
|
|
57
|
+
description: "Create or update a file in the AFS with new content - overwrites existing files",
|
|
58
|
+
inputSchema: z.object({
|
|
59
|
+
path: z
|
|
60
|
+
.string()
|
|
61
|
+
.describe("Full file path where to write content (e.g., '/docs/new-file.md', '/src/component.js')"),
|
|
62
|
+
content: z.string().describe("The text content to write to the file"),
|
|
63
|
+
}),
|
|
64
|
+
process: async (input) => {
|
|
65
|
+
const file = await afs.write(input.path, {
|
|
66
|
+
content: input.content,
|
|
67
|
+
});
|
|
68
|
+
return { file };
|
|
69
|
+
},
|
|
70
|
+
}),
|
|
71
|
+
];
|
|
72
|
+
}
|
|
@@ -27,3 +27,4 @@ export declare function createAccessorArray<T>(array: T[], accessor: (array: T[]
|
|
|
27
27
|
export declare function checkArguments<T extends ZodType>(prefix: string, schema: T, args: unknown): z.infer<T>;
|
|
28
28
|
export declare function tryOrThrow<P extends PromiseOrValue<unknown>>(fn: () => P, error: string | Error | ((error: Error) => Error)): P;
|
|
29
29
|
export declare function tryOrThrow<P extends PromiseOrValue<unknown>>(fn: () => P, error?: Nullish<string | Error | ((error: Error) => Nullish<Error>)>): P | undefined;
|
|
30
|
+
export declare function partition<T>(array: T[], predicate: (item: T) => boolean): [T[], T[]];
|
|
@@ -177,3 +177,16 @@ export function tryOrThrow(fn, error) {
|
|
|
177
177
|
throw error;
|
|
178
178
|
}
|
|
179
179
|
}
|
|
180
|
+
export function partition(array, predicate) {
|
|
181
|
+
const pass = [];
|
|
182
|
+
const fail = [];
|
|
183
|
+
for (const item of array) {
|
|
184
|
+
if (predicate(item)) {
|
|
185
|
+
pass.push(item);
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
fail.push(item);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return [pass, fail];
|
|
192
|
+
}
|
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.10",
|
|
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/observability-api": "^0.11.2-beta",
|
|
95
|
+
"@aigne/observability-api": "^0.11.2-beta.5",
|
|
96
96
|
"@aigne/platform-helpers": "^0.6.3",
|
|
97
|
-
"@aigne/afs": "^1.
|
|
97
|
+
"@aigne/afs": "^1.1.0-beta"
|
|
98
98
|
},
|
|
99
99
|
"devDependencies": {
|
|
100
100
|
"@types/bun": "^1.2.22",
|