@aigne/core 1.29.0 → 1.31.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 (54) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/lib/cjs/agents/agent.d.ts +0 -2
  3. package/lib/cjs/agents/agent.js +1 -9
  4. package/lib/cjs/agents/ai-agent.d.ts +74 -0
  5. package/lib/cjs/agents/ai-agent.js +43 -3
  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/index.d.ts +1 -1
  9. package/lib/cjs/aigne/index.js +1 -1
  10. package/lib/cjs/aigne/message-queue.js +6 -6
  11. package/lib/cjs/prompt/prompt-builder.d.ts +1 -1
  12. package/lib/cjs/prompt/prompt-builder.js +37 -10
  13. package/lib/cjs/prompt/prompts/memory-message-template.d.ts +1 -1
  14. package/lib/cjs/prompt/prompts/memory-message-template.js +2 -4
  15. package/lib/cjs/prompt/prompts/structured-stream-instructions.d.ts +7 -0
  16. package/lib/cjs/prompt/prompts/structured-stream-instructions.js +27 -0
  17. package/lib/cjs/utils/mcp-utils.d.ts +1 -1
  18. package/lib/cjs/utils/structured-stream-extractor.d.ts +14 -0
  19. package/lib/cjs/utils/structured-stream-extractor.js +60 -0
  20. package/lib/cjs/utils/type-utils.d.ts +1 -1
  21. package/lib/cjs/utils/type-utils.js +3 -3
  22. package/lib/dts/agents/agent.d.ts +0 -2
  23. package/lib/dts/agents/ai-agent.d.ts +74 -0
  24. package/lib/dts/aigne/context.d.ts +4 -0
  25. package/lib/dts/aigne/index.d.ts +1 -1
  26. package/lib/dts/prompt/prompt-builder.d.ts +1 -1
  27. package/lib/dts/prompt/prompts/memory-message-template.d.ts +1 -1
  28. package/lib/dts/prompt/prompts/structured-stream-instructions.d.ts +7 -0
  29. package/lib/dts/utils/mcp-utils.d.ts +1 -1
  30. package/lib/dts/utils/structured-stream-extractor.d.ts +14 -0
  31. package/lib/dts/utils/type-utils.d.ts +1 -1
  32. package/lib/esm/agents/agent.d.ts +0 -2
  33. package/lib/esm/agents/agent.js +2 -10
  34. package/lib/esm/agents/ai-agent.d.ts +74 -0
  35. package/lib/esm/agents/ai-agent.js +43 -3
  36. package/lib/esm/agents/user-agent.js +2 -2
  37. package/lib/esm/aigne/context.d.ts +4 -0
  38. package/lib/esm/aigne/context.js +1 -1
  39. package/lib/esm/aigne/index.d.ts +1 -1
  40. package/lib/esm/aigne/index.js +1 -1
  41. package/lib/esm/aigne/message-queue.js +7 -7
  42. package/lib/esm/loader/agent-js.js +1 -1
  43. package/lib/esm/prompt/prompt-builder.d.ts +1 -1
  44. package/lib/esm/prompt/prompt-builder.js +38 -11
  45. package/lib/esm/prompt/prompts/memory-message-template.d.ts +1 -1
  46. package/lib/esm/prompt/prompts/memory-message-template.js +2 -4
  47. package/lib/esm/prompt/prompts/structured-stream-instructions.d.ts +7 -0
  48. package/lib/esm/prompt/prompts/structured-stream-instructions.js +24 -0
  49. package/lib/esm/utils/mcp-utils.d.ts +1 -1
  50. package/lib/esm/utils/structured-stream-extractor.d.ts +14 -0
  51. package/lib/esm/utils/structured-stream-extractor.js +56 -0
  52. package/lib/esm/utils/type-utils.d.ts +1 -1
  53. package/lib/esm/utils/type-utils.js +2 -2
  54. package/package.json +14 -14
@@ -50,6 +50,48 @@ export interface AIAgentOptions<I extends Message = Message, O extends Message =
50
50
  * @default true
51
51
  */
52
52
  catchToolsError?: boolean;
53
+ /**
54
+ * Whether to enable structured stream mode
55
+ *
56
+ * When enabled, the AI model's streaming response will be processed to extract
57
+ * structured metadata. The model needs to include specific format metadata tags
58
+ * (like <metadata></metadata>) in its response, which will be parsed as JSON
59
+ * objects and passed through the stream.
60
+ *
61
+ * This is useful for scenarios that need to extract structured information
62
+ * (like classifications, scores, tags, etc.) from AI responses.
63
+ *
64
+ * @default false
65
+ */
66
+ structuredStreamMode?: boolean;
67
+ /**
68
+ * Custom structured stream instructions configuration
69
+ *
70
+ * Allows customization of structured stream mode behavior, including:
71
+ * - instructions: Prompt instructions to guide the AI model on how to output structured data
72
+ * - metadataStart: Metadata start marker (e.g., "<metadata>")
73
+ * - metadataEnd: Metadata end marker (e.g., "</metadata>")
74
+ * - parse: Function to parse metadata content, converting raw string to object
75
+ *
76
+ * If not provided, the default STRUCTURED_STREAM_INSTRUCTIONS configuration will be used,
77
+ * which outputs structured data in YAML format within <metadata> tags.
78
+ *
79
+ * @example
80
+ * ```typescript
81
+ * {
82
+ * instructions: "Output metadata in JSON format at the end of response in markdown code block with json language",
83
+ * metadataStart: "```json",
84
+ * metadataEnd: "```",
85
+ * parse: JSON.parse
86
+ * }
87
+ * ```
88
+ */
89
+ customStructuredStreamInstructions?: {
90
+ instructions: string | PromptBuilder;
91
+ metadataStart: string;
92
+ metadataEnd: string;
93
+ parse: (raw: string) => object;
94
+ };
53
95
  /**
54
96
  * Whether to include memory agents as tools for the AI model
55
97
  *
@@ -217,6 +259,38 @@ export declare class AIAgent<I extends Message = any, O extends Message = any> e
217
259
  * @default true
218
260
  */
219
261
  catchToolsError: boolean;
262
+ /**
263
+ * Whether to enable structured stream mode
264
+ *
265
+ * When enabled, the AI model's streaming response will be processed to extract
266
+ * structured metadata. The model needs to include specific format metadata tags
267
+ * (like <metadata></metadata>) in its response, which will be parsed as JSON
268
+ * objects and passed through the stream.
269
+ *
270
+ * This is useful for scenarios that need to extract structured information
271
+ * (like classifications, scores, tags, etc.) from AI responses.
272
+ *
273
+ * @default false
274
+ */
275
+ structuredStreamMode?: boolean;
276
+ /**
277
+ * Custom structured stream instructions configuration
278
+ *
279
+ * Allows customization of structured stream mode behavior, including:
280
+ * - instructions: Prompt instructions to guide the AI model on how to output structured data
281
+ * - metadataStart: Metadata start marker (e.g., "<metadata>")
282
+ * - metadataEnd: Metadata end marker (e.g., "</metadata>")
283
+ * - parse: Function to parse metadata content, converting raw string to object
284
+ *
285
+ * If not provided, the default STRUCTURED_STREAM_INSTRUCTIONS configuration will be used,
286
+ * which outputs structured data in YAML format within <metadata> tags.
287
+ */
288
+ customStructuredStreamInstructions?: {
289
+ instructions: PromptBuilder;
290
+ metadataStart: string;
291
+ metadataEnd: string;
292
+ parse: (raw: string) => object;
293
+ };
220
294
  /**
221
295
  * Process an input message and generate a response
222
296
  *
@@ -54,11 +54,15 @@ export interface InvokeOptions<U extends UserContext = UserContext> extends Part
54
54
  * @default true
55
55
  */
56
56
  newContext?: boolean;
57
+ userContext?: U;
58
+ memories?: Pick<Memory, "content">[];
57
59
  }
58
60
  /**
59
61
  * @hidden
60
62
  */
61
63
  export interface UserContext extends Record<string, unknown> {
64
+ userId?: string;
65
+ sessionId?: string;
62
66
  }
63
67
  /**
64
68
  * @hidden
@@ -1,4 +1,4 @@
1
- export * from "./context.js";
2
1
  export * from "./aigne.js";
2
+ export * from "./context.js";
3
3
  export * from "./message-queue.js";
4
4
  export * from "./usage.js";
@@ -6,7 +6,7 @@ import { ChatMessagesTemplate } from "./template.js";
6
6
  export interface PromptBuilderOptions {
7
7
  instructions?: string | ChatMessagesTemplate;
8
8
  }
9
- export interface PromptBuildOptions extends Pick<AgentInvokeOptions, "context"> {
9
+ export interface PromptBuildOptions extends Partial<Pick<AgentInvokeOptions, "context">> {
10
10
  agent?: AIAgent;
11
11
  input?: Message;
12
12
  model?: ChatModel;
@@ -1 +1 @@
1
- export declare const MEMORY_MESSAGE_TEMPLATE = "Here are some useful memories to consider:\n\n<memories>\n{{memories}}\n</memories>\n";
1
+ export declare const MEMORY_MESSAGE_TEMPLATE = "<related-memories>\n{{memories}}\n</related-memories>\n";
@@ -0,0 +1,7 @@
1
+ import { parse } from "yaml";
2
+ export declare const STRUCTURED_STREAM_INSTRUCTIONS: {
3
+ instructions: string;
4
+ metadataStart: string;
5
+ metadataEnd: string;
6
+ parse: typeof parse;
7
+ };
@@ -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;
@@ -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
+ }
@@ -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
  };
@@ -137,8 +137,6 @@ export interface AgentInvokeOptions<U extends UserContext = UserContext> {
137
137
  * and returns the final JSON result
138
138
  */
139
139
  streaming?: boolean;
140
- userContext?: U;
141
- memories?: Pick<Memory, "content">[];
142
140
  }
143
141
  /**
144
142
  * Agent is the base class for all agents.
@@ -2,7 +2,7 @@ import { nodejs } from "@aigne/platform-helpers/nodejs/index.js";
2
2
  import { ZodObject, z } from "zod";
3
3
  import { logger } from "../utils/logger.js";
4
4
  import { agentResponseStreamToObject, asyncGeneratorToReadableStream, isAsyncGenerator, objectToAgentResponseStream, onAgentResponseStreamEnd, } from "../utils/stream-utils.js";
5
- import { checkArguments, createAccessorArray, isEmpty, orArrayToArray, } from "../utils/type-utils.js";
5
+ import { checkArguments, createAccessorArray, flat, isEmpty, } from "../utils/type-utils.js";
6
6
  import { replaceTransferAgentToName, transferToAgentOutput, } from "./types.js";
7
7
  export * from "./types.js";
8
8
  export const agentOptionsSchema = z.object({
@@ -222,7 +222,7 @@ export class Agent {
222
222
  this.subscribeToTopics(context);
223
223
  }
224
224
  subscribeToTopics(context) {
225
- for (const topic of orArrayToArray(this.subscribeTopic).concat(this.topic)) {
225
+ for (const topic of flat(this.subscribeTopic).concat(this.topic)) {
226
226
  this.subscriptions.push(context.subscribe(topic, (payload) => this.onMessage(payload)));
227
227
  }
228
228
  }
@@ -291,14 +291,6 @@ export class Agent {
291
291
  ...options,
292
292
  context: options.context ?? (await this.newDefaultContext()),
293
293
  };
294
- if (options.userContext) {
295
- Object.assign(opts.context.userContext, options.userContext);
296
- options.userContext = undefined;
297
- }
298
- if (options.memories?.length) {
299
- opts.context.memories.push(...options.memories);
300
- options.memories = undefined;
301
- }
302
294
  logger.debug("Invoke agent %s started with input: %O", this.name, input);
303
295
  if (!this.disableEvents)
304
296
  opts.context.emit("agentStarted", { agent: this, input });
@@ -50,6 +50,48 @@ export interface AIAgentOptions<I extends Message = Message, O extends Message =
50
50
  * @default true
51
51
  */
52
52
  catchToolsError?: boolean;
53
+ /**
54
+ * Whether to enable structured stream mode
55
+ *
56
+ * When enabled, the AI model's streaming response will be processed to extract
57
+ * structured metadata. The model needs to include specific format metadata tags
58
+ * (like <metadata></metadata>) in its response, which will be parsed as JSON
59
+ * objects and passed through the stream.
60
+ *
61
+ * This is useful for scenarios that need to extract structured information
62
+ * (like classifications, scores, tags, etc.) from AI responses.
63
+ *
64
+ * @default false
65
+ */
66
+ structuredStreamMode?: boolean;
67
+ /**
68
+ * Custom structured stream instructions configuration
69
+ *
70
+ * Allows customization of structured stream mode behavior, including:
71
+ * - instructions: Prompt instructions to guide the AI model on how to output structured data
72
+ * - metadataStart: Metadata start marker (e.g., "<metadata>")
73
+ * - metadataEnd: Metadata end marker (e.g., "</metadata>")
74
+ * - parse: Function to parse metadata content, converting raw string to object
75
+ *
76
+ * If not provided, the default STRUCTURED_STREAM_INSTRUCTIONS configuration will be used,
77
+ * which outputs structured data in YAML format within <metadata> tags.
78
+ *
79
+ * @example
80
+ * ```typescript
81
+ * {
82
+ * instructions: "Output metadata in JSON format at the end of response in markdown code block with json language",
83
+ * metadataStart: "```json",
84
+ * metadataEnd: "```",
85
+ * parse: JSON.parse
86
+ * }
87
+ * ```
88
+ */
89
+ customStructuredStreamInstructions?: {
90
+ instructions: string | PromptBuilder;
91
+ metadataStart: string;
92
+ metadataEnd: string;
93
+ parse: (raw: string) => object;
94
+ };
53
95
  /**
54
96
  * Whether to include memory agents as tools for the AI model
55
97
  *
@@ -217,6 +259,38 @@ export declare class AIAgent<I extends Message = any, O extends Message = any> e
217
259
  * @default true
218
260
  */
219
261
  catchToolsError: boolean;
262
+ /**
263
+ * Whether to enable structured stream mode
264
+ *
265
+ * When enabled, the AI model's streaming response will be processed to extract
266
+ * structured metadata. The model needs to include specific format metadata tags
267
+ * (like <metadata></metadata>) in its response, which will be parsed as JSON
268
+ * objects and passed through the stream.
269
+ *
270
+ * This is useful for scenarios that need to extract structured information
271
+ * (like classifications, scores, tags, etc.) from AI responses.
272
+ *
273
+ * @default false
274
+ */
275
+ structuredStreamMode?: boolean;
276
+ /**
277
+ * Custom structured stream instructions configuration
278
+ *
279
+ * Allows customization of structured stream mode behavior, including:
280
+ * - instructions: Prompt instructions to guide the AI model on how to output structured data
281
+ * - metadataStart: Metadata start marker (e.g., "<metadata>")
282
+ * - metadataEnd: Metadata end marker (e.g., "</metadata>")
283
+ * - parse: Function to parse metadata content, converting raw string to object
284
+ *
285
+ * If not provided, the default STRUCTURED_STREAM_INSTRUCTIONS configuration will be used,
286
+ * which outputs structured data in YAML format within <metadata> tags.
287
+ */
288
+ customStructuredStreamInstructions?: {
289
+ instructions: PromptBuilder;
290
+ metadataStart: string;
291
+ metadataEnd: string;
292
+ parse: (raw: string) => object;
293
+ };
220
294
  /**
221
295
  * Process an input message and generate a response
222
296
  *
@@ -1,6 +1,8 @@
1
1
  import { z } from "zod";
2
2
  import { PromptBuilder } from "../prompt/prompt-builder.js";
3
+ import { STRUCTURED_STREAM_INSTRUCTIONS } from "../prompt/prompts/structured-stream-instructions.js";
3
4
  import { AgentMessageTemplate, ToolMessageTemplate } from "../prompt/template.js";
5
+ import { ExtractMetadataTransform } from "../utils/structured-stream-extractor.js";
4
6
  import { checkArguments, isEmpty } from "../utils/type-utils.js";
5
7
  import { Agent, agentOptionsSchema, isAgentResponseDelta, } from "./agent.js";
6
8
  import { isTransferAgentOutput } from "./types.js";
@@ -112,6 +114,13 @@ export class AIAgent extends Agent {
112
114
  this.memoryPromptTemplate = options.memoryPromptTemplate;
113
115
  if (typeof options.catchToolsError === "boolean")
114
116
  this.catchToolsError = options.catchToolsError;
117
+ this.structuredStreamMode = options.structuredStreamMode;
118
+ this.customStructuredStreamInstructions = options.customStructuredStreamInstructions && {
119
+ ...options.customStructuredStreamInstructions,
120
+ instructions: typeof options.customStructuredStreamInstructions.instructions === "string"
121
+ ? PromptBuilder.from(options.customStructuredStreamInstructions.instructions)
122
+ : options.customStructuredStreamInstructions.instructions,
123
+ };
115
124
  if (!this.inputKey && !this.instructions) {
116
125
  throw new Error("AIAgent requires either inputKey or instructions to be set");
117
126
  }
@@ -181,6 +190,33 @@ export class AIAgent extends Agent {
181
190
  * @default true
182
191
  */
183
192
  catchToolsError = true;
193
+ /**
194
+ * Whether to enable structured stream mode
195
+ *
196
+ * When enabled, the AI model's streaming response will be processed to extract
197
+ * structured metadata. The model needs to include specific format metadata tags
198
+ * (like <metadata></metadata>) in its response, which will be parsed as JSON
199
+ * objects and passed through the stream.
200
+ *
201
+ * This is useful for scenarios that need to extract structured information
202
+ * (like classifications, scores, tags, etc.) from AI responses.
203
+ *
204
+ * @default false
205
+ */
206
+ structuredStreamMode;
207
+ /**
208
+ * Custom structured stream instructions configuration
209
+ *
210
+ * Allows customization of structured stream mode behavior, including:
211
+ * - instructions: Prompt instructions to guide the AI model on how to output structured data
212
+ * - metadataStart: Metadata start marker (e.g., "<metadata>")
213
+ * - metadataEnd: Metadata end marker (e.g., "</metadata>")
214
+ * - parse: Function to parse metadata content, converting raw string to object
215
+ *
216
+ * If not provided, the default STRUCTURED_STREAM_INSTRUCTIONS configuration will be used,
217
+ * which outputs structured data in YAML format within <metadata> tags.
218
+ */
219
+ customStructuredStreamInstructions;
184
220
  /**
185
221
  * Process an input message and generate a response
186
222
  *
@@ -204,7 +240,11 @@ export class AIAgent extends Agent {
204
240
  const outputKey = this.outputKey;
205
241
  for (;;) {
206
242
  const modelOutput = {};
207
- const stream = await options.context.invoke(model, { ...modelInput, messages: modelInput.messages.concat(toolCallMessages) }, { streaming: true });
243
+ let stream = await options.context.invoke(model, { ...modelInput, messages: modelInput.messages.concat(toolCallMessages) }, { streaming: true });
244
+ if (this.structuredStreamMode) {
245
+ const { metadataStart, metadataEnd, parse } = this.customStructuredStreamInstructions || STRUCTURED_STREAM_INSTRUCTIONS;
246
+ stream = stream.pipeThrough(new ExtractMetadataTransform({ start: metadataStart, end: metadataEnd, parse }));
247
+ }
208
248
  for await (const value of stream) {
209
249
  if (isAgentResponseDelta(value)) {
210
250
  if (value.delta.text?.text) {
@@ -251,7 +291,7 @@ export class AIAgent extends Agent {
251
291
  if (json) {
252
292
  Object.assign(result, json);
253
293
  }
254
- else if (text) {
294
+ if (text) {
255
295
  Object.assign(result, { [outputKey]: text });
256
296
  }
257
297
  if (!isEmpty(result)) {
@@ -275,7 +315,7 @@ export class AIAgent extends Agent {
275
315
  * @protected
276
316
  */
277
317
  async *_processRouter(input, model, modelInput, options, toolsMap) {
278
- const { toolCalls: [call] = [], } = await options.context.invoke(model, modelInput);
318
+ const { toolCalls: [call] = [] } = await options.context.invoke(model, modelInput);
279
319
  if (!call) {
280
320
  throw new Error("Router toolChoice requires exactly one tool to be executed");
281
321
  }
@@ -1,5 +1,5 @@
1
1
  import { toMessagePayload } from "../aigne/message-queue.js";
2
- import { orArrayToArray } from "../utils/type-utils.js";
2
+ import { flat } from "../utils/type-utils.js";
3
3
  import { Agent, } from "./agent.js";
4
4
  export class UserAgent extends Agent {
5
5
  tag = "UserAgent";
@@ -67,7 +67,7 @@ export class UserAgent extends Agent {
67
67
  let subscriptions = [];
68
68
  return new ReadableStream({
69
69
  start: (controller) => {
70
- const subscribeTopic = orArrayToArray(this.subscribeTopic);
70
+ const subscribeTopic = flat(this.subscribeTopic);
71
71
  subscriptions = subscribeTopic.map((topic) => this.subscribe(topic, (message) => {
72
72
  controller.enqueue({ ...message, topic });
73
73
  }));
@@ -54,11 +54,15 @@ export interface InvokeOptions<U extends UserContext = UserContext> extends Part
54
54
  * @default true
55
55
  */
56
56
  newContext?: boolean;
57
+ userContext?: U;
58
+ memories?: Pick<Memory, "content">[];
57
59
  }
58
60
  /**
59
61
  * @hidden
60
62
  */
61
63
  export interface UserContext extends Record<string, unknown> {
64
+ userId?: string;
65
+ sessionId?: string;
62
66
  }
63
67
  /**
64
68
  * @hidden
@@ -1,4 +1,4 @@
1
- import { SpanStatusCode, context, trace } from "@opentelemetry/api";
1
+ import { context, SpanStatusCode, trace } from "@opentelemetry/api";
2
2
  import equal from "fast-deep-equal";
3
3
  import { Emitter } from "strict-event-emitter";
4
4
  import { v7 } from "uuid";
@@ -1,4 +1,4 @@
1
- export * from "./context.js";
2
1
  export * from "./aigne.js";
2
+ export * from "./context.js";
3
3
  export * from "./message-queue.js";
4
4
  export * from "./usage.js";
@@ -1,4 +1,4 @@
1
- export * from "./context.js";
2
1
  export * from "./aigne.js";
2
+ export * from "./context.js";
3
3
  export * from "./message-queue.js";
4
4
  export * from "./usage.js";
@@ -1,6 +1,6 @@
1
1
  import { Emitter } from "strict-event-emitter";
2
2
  import { z } from "zod";
3
- import { checkArguments, isNil, orArrayToArray } from "../utils/type-utils.js";
3
+ import { checkArguments, flat, isNil } from "../utils/type-utils.js";
4
4
  /**
5
5
  * @hidden
6
6
  */
@@ -41,7 +41,7 @@ export class MessageQueue {
41
41
  topic,
42
42
  payload,
43
43
  });
44
- for (const t of orArrayToArray(topic)) {
44
+ for (const t of flat(topic)) {
45
45
  this.events.emit(t, payload);
46
46
  }
47
47
  }
@@ -72,18 +72,18 @@ export class MessageQueue {
72
72
  topic,
73
73
  listener,
74
74
  });
75
- for (const t of orArrayToArray(topic)) {
75
+ for (const t of flat(topic)) {
76
76
  this.events.off(t, listener);
77
77
  }
78
78
  }
79
79
  }
80
80
  function on(events, event, listener) {
81
- orArrayToArray(event).forEach((e) => events.on(e, listener));
82
- return () => orArrayToArray(event).forEach((e) => events.off(e, listener));
81
+ flat(event).forEach((e) => events.on(e, listener));
82
+ return () => flat(event).forEach((e) => events.off(e, listener));
83
83
  }
84
84
  function once(events, event, listener) {
85
- orArrayToArray(event).forEach((e) => events.once(e, listener));
86
- return () => orArrayToArray(event).forEach((e) => events.off(e, listener));
85
+ flat(event).forEach((e) => events.once(e, listener));
86
+ return () => flat(event).forEach((e) => events.off(e, listener));
87
87
  }
88
88
  const subscribeArgsSchema = z.object({
89
89
  topic: z.union([z.string(), z.array(z.string())]),
@@ -12,7 +12,7 @@ const agentJsFileSchema = z.object({
12
12
  process: z.custom(),
13
13
  });
14
14
  export async function loadAgentFromJsFile(path) {
15
- const { default: agent } = await tryOrThrow(() => import(path), (error) => new Error(`Failed to load agent definition from ${path}: ${error.message}`));
15
+ const { default: agent } = await tryOrThrow(() => import(/* @vite-ignore */ path), (error) => new Error(`Failed to load agent definition from ${path}: ${error.message}`));
16
16
  if (agent instanceof Agent)
17
17
  return agent;
18
18
  if (typeof agent !== "function") {
@@ -6,7 +6,7 @@ import { ChatMessagesTemplate } from "./template.js";
6
6
  export interface PromptBuilderOptions {
7
7
  instructions?: string | ChatMessagesTemplate;
8
8
  }
9
- export interface PromptBuildOptions extends Pick<AgentInvokeOptions, "context"> {
9
+ export interface PromptBuildOptions extends Partial<Pick<AgentInvokeOptions, "context">> {
10
10
  agent?: AIAgent;
11
11
  input?: Message;
12
12
  model?: ChatModel;
@@ -1,10 +1,12 @@
1
1
  import { nodejs } from "@aigne/platform-helpers/nodejs/index.js";
2
2
  import { stringify } from "yaml";
3
3
  import { ZodObject } from "zod";
4
+ import { zodToJsonSchema } from "zod-to-json-schema";
4
5
  import { Agent } from "../agents/agent.js";
5
6
  import { outputSchemaToResponseFormatSchema } from "../utils/json-schema.js";
6
- import { unique } from "../utils/type-utils.js";
7
+ import { isRecord, unique } from "../utils/type-utils.js";
7
8
  import { MEMORY_MESSAGE_TEMPLATE } from "./prompts/memory-message-template.js";
9
+ import { STRUCTURED_STREAM_INSTRUCTIONS } from "./prompts/structured-stream-instructions.js";
8
10
  import { AgentMessageTemplate, ChatMessagesTemplate, PromptTemplate, SystemMessageTemplate, UserMessageTemplate, } from "./template.js";
9
11
  export class PromptBuilder {
10
12
  static from(instructions) {
@@ -55,7 +57,9 @@ export class PromptBuilder {
55
57
  async build(options) {
56
58
  return {
57
59
  messages: await this.buildMessages(options),
58
- responseFormat: this.buildResponseFormat(options),
60
+ responseFormat: options.agent?.structuredStreamMode
61
+ ? undefined
62
+ : this.buildResponseFormat(options),
59
63
  ...this.buildTools(options),
60
64
  };
61
65
  }
@@ -67,14 +71,26 @@ export class PromptBuilder {
67
71
  ? ChatMessagesTemplate.from([SystemMessageTemplate.from(this.instructions)])
68
72
  : this.instructions)?.format(options.input) ?? [];
69
73
  const memories = [];
70
- if (options.agent?.inputKey) {
71
- memories.push(...(await options.agent.retrieveMemories({ search: message }, options)));
74
+ if (options.agent && options.context) {
75
+ memories.push(...(await options.agent.retrieveMemories({ search: message }, { context: options.context })));
72
76
  }
73
- if (options.context.memories?.length) {
77
+ if (options.context?.memories?.length) {
74
78
  memories.push(...options.context.memories);
75
79
  }
76
80
  if (memories.length)
77
81
  messages.push(...this.convertMemoriesToMessages(memories, options));
82
+ // if the agent is using structured stream mode, add the instructions
83
+ const { structuredStreamMode, outputSchema } = options.agent || {};
84
+ if (structuredStreamMode && outputSchema) {
85
+ const instructions = options.agent?.customStructuredStreamInstructions?.instructions ||
86
+ PromptBuilder.from(STRUCTURED_STREAM_INSTRUCTIONS.instructions);
87
+ messages.push(...(await instructions.buildMessages({
88
+ input: {
89
+ ...input,
90
+ outputJsonSchema: zodToJsonSchema(outputSchema),
91
+ },
92
+ })));
93
+ }
78
94
  if (message) {
79
95
  messages.push({
80
96
  role: "user",
@@ -84,13 +100,24 @@ export class PromptBuilder {
84
100
  return messages;
85
101
  }
86
102
  convertMemoriesToMessages(memories, options) {
87
- const str = stringify(memories.map((i) => i.content));
88
- return [
89
- {
103
+ const messages = [];
104
+ const other = [];
105
+ const stringOrStringify = (value) => typeof value === "string" ? value : stringify(value);
106
+ for (const { content } of memories) {
107
+ if (isRecord(content) && "input" in content && "output" in content) {
108
+ messages.push({ role: "user", content: stringOrStringify(content.input) }, { role: "agent", content: stringOrStringify(content.output) });
109
+ }
110
+ else {
111
+ other.push(content);
112
+ }
113
+ }
114
+ if (other.length) {
115
+ messages.unshift({
90
116
  role: "system",
91
- content: PromptTemplate.from(options.agent?.memoryPromptTemplate || MEMORY_MESSAGE_TEMPLATE).format({ memories: str }),
92
- },
93
- ];
117
+ content: PromptTemplate.from(options.agent?.memoryPromptTemplate || MEMORY_MESSAGE_TEMPLATE).format({ memories: stringify(other) }),
118
+ });
119
+ }
120
+ return messages;
94
121
  }
95
122
  buildResponseFormat(options) {
96
123
  const outputSchema = options.outputSchema || options.agent?.outputSchema;
@@ -1 +1 @@
1
- export declare const MEMORY_MESSAGE_TEMPLATE = "Here are some useful memories to consider:\n\n<memories>\n{{memories}}\n</memories>\n";
1
+ export declare const MEMORY_MESSAGE_TEMPLATE = "<related-memories>\n{{memories}}\n</related-memories>\n";
@@ -1,7 +1,5 @@
1
1
  export const MEMORY_MESSAGE_TEMPLATE = `\
2
- Here are some useful memories to consider:
3
-
4
- <memories>
2
+ <related-memories>
5
3
  {{memories}}
6
- </memories>
4
+ </related-memories>
7
5
  `;
@@ -0,0 +1,7 @@
1
+ import { parse } from "yaml";
2
+ export declare const STRUCTURED_STREAM_INSTRUCTIONS: {
3
+ instructions: string;
4
+ metadataStart: string;
5
+ metadataEnd: string;
6
+ parse: typeof parse;
7
+ };
@@ -0,0 +1,24 @@
1
+ import { parse } from "yaml";
2
+ export const STRUCTURED_STREAM_INSTRUCTIONS = {
3
+ instructions: `\
4
+ <output-rules>
5
+ - First, output the regular response content.
6
+ - At the end of the response, use <metadata></metadata> tags to output metadata. The metadata should be output in YAML format as structured data, and must conform to the format defined in <metadata-schema></metadata-schema>.
7
+ </output-rules>
8
+
9
+ <metadata-schema>
10
+ {{outputJsonSchema}}
11
+ </metadata-schema>
12
+
13
+ <output-example>
14
+ Here is the regular response content
15
+ <metadata>
16
+ foo: bar
17
+ baz: 123
18
+ </metadata>
19
+ </output-example>
20
+ `,
21
+ metadataStart: "<metadata>",
22
+ metadataEnd: "</metadata>",
23
+ parse,
24
+ };
@@ -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;