@aigne/core 1.29.1 → 1.32.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.
Files changed (64) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/lib/cjs/agents/agent.d.ts +0 -2
  3. package/lib/cjs/agents/agent.js +4 -9
  4. package/lib/cjs/agents/ai-agent.d.ts +74 -0
  5. package/lib/cjs/agents/ai-agent.js +44 -4
  6. package/lib/cjs/agents/user-agent.js +1 -1
  7. package/lib/cjs/aigne/context.d.ts +4 -0
  8. package/lib/cjs/aigne/context.js +0 -10
  9. package/lib/cjs/aigne/index.d.ts +1 -1
  10. package/lib/cjs/aigne/index.js +1 -1
  11. package/lib/cjs/aigne/message-queue.js +6 -6
  12. package/lib/cjs/loader/index.js +3 -0
  13. package/lib/cjs/prompt/prompt-builder.d.ts +6 -2
  14. package/lib/cjs/prompt/prompt-builder.js +48 -19
  15. package/lib/cjs/prompt/prompts/memory-message-template.d.ts +1 -1
  16. package/lib/cjs/prompt/prompts/memory-message-template.js +2 -4
  17. package/lib/cjs/prompt/prompts/structured-stream-instructions.d.ts +7 -0
  18. package/lib/cjs/prompt/prompts/structured-stream-instructions.js +27 -0
  19. package/lib/cjs/prompt/template.d.ts +21 -7
  20. package/lib/cjs/prompt/template.js +57 -19
  21. package/lib/cjs/utils/mcp-utils.d.ts +1 -1
  22. package/lib/cjs/utils/stream-utils.js +3 -0
  23. package/lib/cjs/utils/structured-stream-extractor.d.ts +14 -0
  24. package/lib/cjs/utils/structured-stream-extractor.js +60 -0
  25. package/lib/cjs/utils/type-utils.d.ts +1 -1
  26. package/lib/cjs/utils/type-utils.js +3 -3
  27. package/lib/dts/agents/agent.d.ts +0 -2
  28. package/lib/dts/agents/ai-agent.d.ts +74 -0
  29. package/lib/dts/aigne/context.d.ts +4 -0
  30. package/lib/dts/aigne/index.d.ts +1 -1
  31. package/lib/dts/prompt/prompt-builder.d.ts +6 -2
  32. package/lib/dts/prompt/prompts/memory-message-template.d.ts +1 -1
  33. package/lib/dts/prompt/prompts/structured-stream-instructions.d.ts +7 -0
  34. package/lib/dts/prompt/template.d.ts +21 -7
  35. package/lib/dts/utils/mcp-utils.d.ts +1 -1
  36. package/lib/dts/utils/structured-stream-extractor.d.ts +14 -0
  37. package/lib/dts/utils/type-utils.d.ts +1 -1
  38. package/lib/esm/agents/agent.d.ts +0 -2
  39. package/lib/esm/agents/agent.js +5 -10
  40. package/lib/esm/agents/ai-agent.d.ts +74 -0
  41. package/lib/esm/agents/ai-agent.js +44 -4
  42. package/lib/esm/agents/user-agent.js +2 -2
  43. package/lib/esm/aigne/context.d.ts +4 -0
  44. package/lib/esm/aigne/context.js +1 -11
  45. package/lib/esm/aigne/index.d.ts +1 -1
  46. package/lib/esm/aigne/index.js +1 -1
  47. package/lib/esm/aigne/message-queue.js +7 -7
  48. package/lib/esm/loader/agent-js.js +1 -1
  49. package/lib/esm/loader/index.js +3 -0
  50. package/lib/esm/prompt/prompt-builder.d.ts +6 -2
  51. package/lib/esm/prompt/prompt-builder.js +49 -20
  52. package/lib/esm/prompt/prompts/memory-message-template.d.ts +1 -1
  53. package/lib/esm/prompt/prompts/memory-message-template.js +2 -4
  54. package/lib/esm/prompt/prompts/structured-stream-instructions.d.ts +7 -0
  55. package/lib/esm/prompt/prompts/structured-stream-instructions.js +24 -0
  56. package/lib/esm/prompt/template.d.ts +21 -7
  57. package/lib/esm/prompt/template.js +54 -17
  58. package/lib/esm/utils/mcp-utils.d.ts +1 -1
  59. package/lib/esm/utils/stream-utils.js +4 -1
  60. package/lib/esm/utils/structured-stream-extractor.d.ts +14 -0
  61. package/lib/esm/utils/structured-stream-extractor.js +56 -0
  62. package/lib/esm/utils/type-utils.d.ts +1 -1
  63. package/lib/esm/utils/type-utils.js +2 -2
  64. package/package.json +16 -14
@@ -1,5 +1,12 @@
1
- import Mustache from "mustache";
1
+ import { nodejs } from "@aigne/platform-helpers/nodejs/index.js";
2
+ import nunjucks from "nunjucks";
2
3
  import { z } from "zod";
4
+ import { isNil } from "../utils/type-utils.js";
5
+ nunjucks.runtime.suppressValue = (v) => {
6
+ if (isNil(v))
7
+ return "";
8
+ return typeof v === "object" ? JSON.stringify(v) : v;
9
+ };
3
10
  export class PromptTemplate {
4
11
  template;
5
12
  static from(template) {
@@ -8,12 +15,42 @@ export class PromptTemplate {
8
15
  constructor(template) {
9
16
  this.template = template;
10
17
  }
11
- format(variables) {
12
- return Mustache.render(this.template, variables, undefined, {
13
- escape: (v) => {
14
- return typeof v === "object" ? JSON.stringify(v) : v;
15
- },
18
+ async format(variables = {}, options) {
19
+ let env = new nunjucks.Environment();
20
+ if (options?.workingDir) {
21
+ env = new nunjucks.Environment(new CustomLoader({ workingDir: options.workingDir }));
22
+ }
23
+ return new Promise((resolve, reject) => env.renderString(this.template, variables, (err, res) => {
24
+ if (err || !res) {
25
+ reject(err || new Error(`Failed to render template: ${this.template}`));
26
+ }
27
+ else {
28
+ resolve(res);
29
+ }
30
+ }));
31
+ }
32
+ }
33
+ export class CustomLoader extends nunjucks.Loader {
34
+ options;
35
+ constructor(options) {
36
+ super();
37
+ this.options = options;
38
+ }
39
+ async = true;
40
+ getSource(name, callback) {
41
+ let result = null;
42
+ nodejs.fs.readFile(nodejs.path.join(this.options.workingDir, name), "utf-8").then((content) => {
43
+ result = {
44
+ src: content,
45
+ path: name,
46
+ noCache: true,
47
+ };
48
+ callback(null, result);
49
+ }, (error) => {
50
+ callback(error, null);
16
51
  });
52
+ // nunjucks expects return LoaderSource synchronously, but we handle it asynchronously.
53
+ return result;
17
54
  }
18
55
  }
19
56
  export class ChatMessageTemplate {
@@ -25,17 +62,17 @@ export class ChatMessageTemplate {
25
62
  this.content = content;
26
63
  this.name = name;
27
64
  }
28
- format(variables) {
65
+ async format(variables, options) {
29
66
  let { content } = this;
30
67
  if (Array.isArray(content)) {
31
- content = content.map((i) => {
68
+ content = await Promise.all(content.map(async (i) => {
32
69
  if (i.type === "text")
33
- return { ...i, text: PromptTemplate.from(i.text).format(variables) };
70
+ return { ...i, text: await PromptTemplate.from(i.text).format(variables, options) };
34
71
  return i;
35
- });
72
+ }));
36
73
  }
37
74
  else if (typeof content === "string") {
38
- content = PromptTemplate.from(content).format(variables);
75
+ content = await PromptTemplate.from(content).format(variables, options);
39
76
  }
40
77
  return {
41
78
  role: this.role,
@@ -63,9 +100,9 @@ export class AgentMessageTemplate extends ChatMessageTemplate {
63
100
  super("agent", content, name);
64
101
  this.toolCalls = toolCalls;
65
102
  }
66
- format(variables) {
103
+ async format(variables, options) {
67
104
  return {
68
- ...super.format(variables),
105
+ ...(await super.format(variables, options)),
69
106
  toolCalls: this.toolCalls,
70
107
  };
71
108
  }
@@ -81,9 +118,9 @@ export class ToolMessageTemplate extends ChatMessageTemplate {
81
118
  : JSON.stringify(content, (_, value) => typeof value === "bigint" ? value.toString() : value), name);
82
119
  this.toolCallId = toolCallId;
83
120
  }
84
- format(variables) {
121
+ async format(variables, options) {
85
122
  return {
86
- ...super.format(variables),
123
+ ...(await super.format(variables, options)),
87
124
  toolCallId: this.toolCallId,
88
125
  };
89
126
  }
@@ -96,8 +133,8 @@ export class ChatMessagesTemplate {
96
133
  constructor(messages) {
97
134
  this.messages = messages;
98
135
  }
99
- format(variables) {
100
- return this.messages.map((message) => message.format(variables));
136
+ async format(variables, options) {
137
+ return Promise.all(this.messages.map((message) => message.format(variables, options)));
101
138
  }
102
139
  }
103
140
  const systemChatMessageSchema = z.object({
@@ -1,4 +1,4 @@
1
- import { type ListPromptsResult, type ListResourceTemplatesResult, type ListResourcesResult, type ListToolsResult } from "@modelcontextprotocol/sdk/types.js";
1
+ import { type ListPromptsResult, type ListResourcesResult, type ListResourceTemplatesResult, type ListToolsResult } from "@modelcontextprotocol/sdk/types.js";
2
2
  import { type MCPBaseOptions, MCPPrompt, MCPResource, MCPTool } from "../agents/mcp-agent.js";
3
3
  export declare function toolFromMCPTool(tool: ListToolsResult["tools"][number], options: MCPBaseOptions): MCPTool;
4
4
  export declare function promptFromMCPPrompt(prompt: ListPromptsResult["prompts"][number], options: MCPBaseOptions): MCPPrompt;
@@ -1,8 +1,11 @@
1
1
  import equal from "fast-deep-equal";
2
2
  import { isAgentResponseDelta, isEmptyChunk, } from "../agents/agent.js";
3
- import { omitBy } from "./type-utils.js";
3
+ import { isRecord, omitBy } from "./type-utils.js";
4
4
  import "./stream-polyfill.js";
5
5
  export function objectToAgentResponseStream(json) {
6
+ if (!isRecord(json)) {
7
+ throw new Error(`expect to return a record type such as {result: ...}, but got (${typeof json}): ${json}`);
8
+ }
6
9
  return new ReadableStream({
7
10
  pull(controller) {
8
11
  controller.enqueue({ delta: { json } });
@@ -0,0 +1,14 @@
1
+ import { type AgentResponseChunk } from "../agents/agent.js";
2
+ import type { ChatModelOutput } from "../agents/chat-model.js";
3
+ export declare class ExtractMetadataTransform extends TransformStream<AgentResponseChunk<ChatModelOutput>, AgentResponseChunk<ChatModelOutput & {
4
+ metadata?: string;
5
+ }>> {
6
+ private buffer;
7
+ private cursor;
8
+ private state;
9
+ constructor({ start, end, parse, }: {
10
+ start: string;
11
+ end: string;
12
+ parse: (raw: string) => object;
13
+ });
14
+ }
@@ -0,0 +1,56 @@
1
+ import { isAgentResponseDelta } from "../agents/agent.js";
2
+ export class ExtractMetadataTransform extends TransformStream {
3
+ buffer = "";
4
+ cursor = 0;
5
+ state = "none";
6
+ constructor({ start, end, parse, }) {
7
+ super({
8
+ transform: async (chunk, controller) => {
9
+ if (isAgentResponseDelta(chunk) && chunk.delta.text?.text) {
10
+ const text = chunk.delta.text.text;
11
+ this.buffer += text;
12
+ for (;;) {
13
+ if (this.state === "none") {
14
+ const found = findMatchIndex(this.buffer, this.cursor, start);
15
+ if (found.start > this.cursor) {
16
+ const text = this.buffer.slice(this.cursor, found.start);
17
+ this.cursor = found.start;
18
+ controller.enqueue({ delta: { text: { text } } });
19
+ }
20
+ if (found.end) {
21
+ this.state = "start";
22
+ this.cursor = found.end;
23
+ }
24
+ }
25
+ if (this.state === "start") {
26
+ const found = findMatchIndex(this.buffer, this.cursor, end);
27
+ if (found.end) {
28
+ const metadata = this.buffer.slice(this.cursor, found.start);
29
+ const json = parse(metadata);
30
+ controller.enqueue({ delta: { json: { json } } });
31
+ this.state = "none";
32
+ this.cursor = found.end;
33
+ continue;
34
+ }
35
+ }
36
+ break;
37
+ }
38
+ return;
39
+ }
40
+ controller.enqueue(chunk);
41
+ },
42
+ });
43
+ }
44
+ }
45
+ function findMatchIndex(str, position, match) {
46
+ const i = str.indexOf(match, position);
47
+ if (i >= 0)
48
+ return { start: i, end: i + match.length };
49
+ for (let i = match.length - 1; i > 0; i--) {
50
+ const m = match.slice(0, i);
51
+ if (str.endsWith(m)) {
52
+ return { start: str.length - m.length };
53
+ }
54
+ }
55
+ return { start: str.length };
56
+ }
@@ -19,7 +19,7 @@ export declare function pick<T extends object, K extends keyof T>(obj: T, ...key
19
19
  export declare function omit<T extends object, K extends keyof T>(obj: T, ...keys: (K | K[])[]): Omit<T, K>;
20
20
  export declare function omitDeep<T, K>(obj: T, ...keys: (K | K[])[]): unknown;
21
21
  export declare function omitBy<T extends Record<string, unknown>, K extends keyof T>(obj: T, predicate: (value: T[K], key: K) => boolean): Partial<T>;
22
- export declare function orArrayToArray<T>(value?: T | T[]): T[];
22
+ export declare function flat<T>(value?: T | T[]): T[];
23
23
  export declare function createAccessorArray<T>(array: T[], accessor: (array: T[], name: string) => T | undefined): T[] & {
24
24
  [key: string]: T;
25
25
  };
@@ -37,7 +37,7 @@ export function duplicates(arr, key = (item) => item) {
37
37
  export function remove(arr, remove) {
38
38
  const removed = [];
39
39
  for (let i = 0; i < arr.length; i++) {
40
- // biome-ignore lint/style/noNonNullAssertion: <explanation>
40
+ // biome-ignore lint/style/noNonNullAssertion: false positive
41
41
  const item = arr[i];
42
42
  if ((Array.isArray(remove) && remove.includes(item)) ||
43
43
  (typeof remove === "function" && remove(item))) {
@@ -84,7 +84,7 @@ export function omitBy(obj, predicate) {
84
84
  return !predicate(value, k);
85
85
  }));
86
86
  }
87
- export function orArrayToArray(value) {
87
+ export function flat(value) {
88
88
  if (isNil(value))
89
89
  return [];
90
90
  return Array.isArray(value) ? value : [value];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aigne/core",
3
- "version": "1.29.1",
3
+ "version": "1.32.0",
4
4
  "description": "AIGNE core library for building AI-powered applications",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -63,35 +63,37 @@
63
63
  },
64
64
  "dependencies": {
65
65
  "@aigne/json-schema-to-zod": "^1.3.3",
66
- "@modelcontextprotocol/sdk": "^1.11.0",
66
+ "@modelcontextprotocol/sdk": "^1.13.3",
67
67
  "@types/debug": "^4.1.12",
68
68
  "camelize-ts": "^3.0.0",
69
69
  "content-type": "^1.0.5",
70
- "debug": "^4.4.0",
71
- "eventsource-parser": "^3.0.1",
70
+ "debug": "^4.4.1",
71
+ "eventsource-parser": "^3.0.3",
72
72
  "fast-deep-equal": "^3.1.3",
73
73
  "immer": "^10.1.1",
74
74
  "jsonata": "^2.0.6",
75
75
  "mustache": "^4.2.0",
76
76
  "nanoid": "^5.1.5",
77
+ "nunjucks": "^3.2.4",
77
78
  "p-retry": "^6.2.1",
78
79
  "raw-body": "^3.0.0",
79
80
  "strict-event-emitter": "^0.5.1",
80
81
  "ufo": "^1.6.1",
81
82
  "uuid": "^11.1.0",
82
- "yaml": "^2.7.1",
83
- "zod": "^3.24.4",
84
- "zod-to-json-schema": "^3.24.5",
85
- "@aigne/platform-helpers": "^0.1.2",
86
- "@aigne/observability-api": "^0.3.3"
83
+ "yaml": "^2.8.0",
84
+ "zod": "^3.25.67",
85
+ "zod-to-json-schema": "^3.24.6",
86
+ "@aigne/observability-api": "^0.6.0",
87
+ "@aigne/platform-helpers": "^0.3.0"
87
88
  },
88
89
  "devDependencies": {
89
- "@types/bun": "^1.2.12",
90
- "@types/compression": "^1.7.5",
91
- "@types/content-type": "^1.1.8",
92
- "@types/express": "^5.0.1",
90
+ "@types/bun": "^1.2.17",
91
+ "@types/compression": "^1.8.1",
92
+ "@types/content-type": "^1.1.9",
93
+ "@types/express": "^5.0.3",
93
94
  "@types/mustache": "^4.2.6",
94
- "@types/node": "^22.15.15",
95
+ "@types/node": "^24.0.10",
96
+ "@types/nunjucks": "^3.2.6",
95
97
  "compression": "^1.8.0",
96
98
  "detect-port": "^2.1.0",
97
99
  "express": "^5.1.0",