@aigne/core 1.11.0 → 1.13.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 (143) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +11 -10
  3. package/README.zh.md +7 -6
  4. package/lib/cjs/agents/agent.d.ts +20 -20
  5. package/lib/cjs/agents/agent.js +25 -26
  6. package/lib/cjs/agents/ai-agent.d.ts +17 -18
  7. package/lib/cjs/agents/ai-agent.js +6 -6
  8. package/lib/cjs/agents/mcp-agent.d.ts +10 -4
  9. package/lib/cjs/agents/mcp-agent.js +12 -6
  10. package/lib/cjs/agents/memory.d.ts +1 -1
  11. package/lib/cjs/agents/team-agent.d.ts +28 -0
  12. package/lib/cjs/agents/team-agent.js +93 -0
  13. package/lib/cjs/agents/user-agent.d.ts +9 -10
  14. package/lib/cjs/agents/user-agent.js +10 -13
  15. package/lib/{esm/execution-engine/execution-engine.d.ts → cjs/aigne/aigne.d.ts} +9 -12
  16. package/lib/cjs/{execution-engine/execution-engine.js → aigne/aigne.js} +19 -19
  17. package/lib/cjs/{execution-engine → aigne}/context.d.ts +31 -32
  18. package/lib/cjs/{execution-engine → aigne}/context.js +30 -40
  19. package/lib/cjs/aigne/index.d.ts +4 -0
  20. package/lib/cjs/{execution-engine → aigne}/index.js +2 -2
  21. package/lib/cjs/{execution-engine → aigne}/usage.d.ts +1 -1
  22. package/lib/cjs/client/client.d.ts +19 -0
  23. package/lib/cjs/client/client.js +49 -0
  24. package/lib/cjs/index.d.ts +2 -1
  25. package/lib/cjs/index.js +2 -1
  26. package/lib/cjs/loader/agent-yaml.d.ts +3 -3
  27. package/lib/cjs/loader/agent-yaml.js +10 -3
  28. package/lib/cjs/loader/index.d.ts +9 -9
  29. package/lib/cjs/loader/index.js +8 -6
  30. package/lib/cjs/models/bedrock-chat-model.d.ts +70 -0
  31. package/lib/cjs/models/bedrock-chat-model.js +273 -0
  32. package/lib/cjs/models/chat-model.d.ts +2 -1
  33. package/lib/cjs/models/chat-model.js +8 -0
  34. package/lib/cjs/models/claude-chat-model.d.ts +3 -3
  35. package/lib/cjs/models/gemini-chat-model.d.ts +1 -0
  36. package/lib/cjs/models/gemini-chat-model.js +1 -0
  37. package/lib/cjs/models/openai-chat-model.d.ts +6 -3
  38. package/lib/cjs/models/openai-chat-model.js +100 -100
  39. package/lib/cjs/prompt/prompt-builder.d.ts +1 -1
  40. package/lib/cjs/prompt/prompt-builder.js +3 -3
  41. package/lib/cjs/server/error.d.ts +4 -0
  42. package/lib/cjs/server/error.js +11 -0
  43. package/lib/cjs/server/server.d.ts +54 -0
  44. package/lib/cjs/server/server.js +130 -0
  45. package/lib/cjs/utils/event-stream.d.ts +11 -0
  46. package/lib/cjs/utils/event-stream.js +91 -0
  47. package/lib/cjs/utils/mcp-utils.js +4 -1
  48. package/lib/cjs/utils/prompts.js +1 -1
  49. package/lib/cjs/utils/stream-utils.d.ts +7 -2
  50. package/lib/cjs/utils/stream-utils.js +43 -34
  51. package/lib/cjs/utils/type-utils.d.ts +5 -2
  52. package/lib/cjs/utils/type-utils.js +22 -2
  53. package/lib/dts/agents/agent.d.ts +20 -20
  54. package/lib/dts/agents/ai-agent.d.ts +17 -18
  55. package/lib/dts/agents/mcp-agent.d.ts +10 -4
  56. package/lib/dts/agents/memory.d.ts +1 -1
  57. package/lib/dts/agents/team-agent.d.ts +28 -0
  58. package/lib/dts/agents/user-agent.d.ts +9 -10
  59. package/lib/dts/{execution-engine/execution-engine.d.ts → aigne/aigne.d.ts} +9 -12
  60. package/lib/dts/{execution-engine → aigne}/context.d.ts +31 -32
  61. package/lib/dts/aigne/index.d.ts +4 -0
  62. package/lib/dts/{execution-engine → aigne}/usage.d.ts +1 -1
  63. package/lib/dts/client/client.d.ts +19 -0
  64. package/lib/dts/index.d.ts +2 -1
  65. package/lib/dts/loader/agent-yaml.d.ts +3 -3
  66. package/lib/dts/loader/index.d.ts +9 -9
  67. package/lib/dts/models/bedrock-chat-model.d.ts +70 -0
  68. package/lib/dts/models/chat-model.d.ts +2 -1
  69. package/lib/dts/models/claude-chat-model.d.ts +3 -3
  70. package/lib/dts/models/gemini-chat-model.d.ts +1 -0
  71. package/lib/dts/models/openai-chat-model.d.ts +6 -3
  72. package/lib/dts/prompt/prompt-builder.d.ts +1 -1
  73. package/lib/dts/server/error.d.ts +4 -0
  74. package/lib/dts/server/server.d.ts +54 -0
  75. package/lib/dts/utils/event-stream.d.ts +11 -0
  76. package/lib/dts/utils/stream-utils.d.ts +7 -2
  77. package/lib/dts/utils/type-utils.d.ts +5 -2
  78. package/lib/esm/agents/agent.d.ts +20 -20
  79. package/lib/esm/agents/agent.js +25 -26
  80. package/lib/esm/agents/ai-agent.d.ts +17 -18
  81. package/lib/esm/agents/ai-agent.js +6 -6
  82. package/lib/esm/agents/mcp-agent.d.ts +10 -4
  83. package/lib/esm/agents/mcp-agent.js +12 -6
  84. package/lib/esm/agents/memory.d.ts +1 -1
  85. package/lib/esm/agents/team-agent.d.ts +28 -0
  86. package/lib/esm/agents/team-agent.js +89 -0
  87. package/lib/esm/agents/user-agent.d.ts +9 -10
  88. package/lib/esm/agents/user-agent.js +11 -14
  89. package/lib/{cjs/execution-engine/execution-engine.d.ts → esm/aigne/aigne.d.ts} +9 -12
  90. package/lib/esm/{execution-engine/execution-engine.js → aigne/aigne.js} +18 -18
  91. package/lib/esm/{execution-engine → aigne}/context.d.ts +31 -32
  92. package/lib/esm/{execution-engine → aigne}/context.js +28 -38
  93. package/lib/esm/aigne/index.d.ts +4 -0
  94. package/lib/esm/aigne/index.js +4 -0
  95. package/lib/esm/{execution-engine → aigne}/usage.d.ts +1 -1
  96. package/lib/esm/client/client.d.ts +19 -0
  97. package/lib/esm/client/client.js +45 -0
  98. package/lib/esm/index.d.ts +2 -1
  99. package/lib/esm/index.js +2 -1
  100. package/lib/esm/loader/agent-yaml.d.ts +3 -3
  101. package/lib/esm/loader/agent-yaml.js +10 -3
  102. package/lib/esm/loader/index.d.ts +9 -9
  103. package/lib/esm/loader/index.js +8 -6
  104. package/lib/esm/models/bedrock-chat-model.d.ts +70 -0
  105. package/lib/esm/models/bedrock-chat-model.js +268 -0
  106. package/lib/esm/models/chat-model.d.ts +2 -1
  107. package/lib/esm/models/chat-model.js +8 -0
  108. package/lib/esm/models/claude-chat-model.d.ts +3 -3
  109. package/lib/esm/models/gemini-chat-model.d.ts +1 -0
  110. package/lib/esm/models/gemini-chat-model.js +1 -0
  111. package/lib/esm/models/openai-chat-model.d.ts +6 -3
  112. package/lib/esm/models/openai-chat-model.js +100 -100
  113. package/lib/esm/prompt/prompt-builder.d.ts +1 -1
  114. package/lib/esm/prompt/prompt-builder.js +4 -4
  115. package/lib/esm/server/error.d.ts +4 -0
  116. package/lib/esm/server/error.js +7 -0
  117. package/lib/esm/server/server.d.ts +54 -0
  118. package/lib/esm/server/server.js +123 -0
  119. package/lib/esm/utils/event-stream.d.ts +11 -0
  120. package/lib/esm/utils/event-stream.js +85 -0
  121. package/lib/esm/utils/mcp-utils.js +4 -1
  122. package/lib/esm/utils/prompts.js +1 -1
  123. package/lib/esm/utils/stream-utils.d.ts +7 -2
  124. package/lib/esm/utils/stream-utils.js +42 -33
  125. package/lib/esm/utils/type-utils.d.ts +5 -2
  126. package/lib/esm/utils/type-utils.js +20 -2
  127. package/package.json +20 -4
  128. package/lib/cjs/execution-engine/index.d.ts +0 -4
  129. package/lib/cjs/execution-engine/utils.d.ts +0 -4
  130. package/lib/cjs/execution-engine/utils.js +0 -34
  131. package/lib/dts/execution-engine/index.d.ts +0 -4
  132. package/lib/dts/execution-engine/utils.d.ts +0 -4
  133. package/lib/esm/execution-engine/index.d.ts +0 -4
  134. package/lib/esm/execution-engine/index.js +0 -4
  135. package/lib/esm/execution-engine/utils.d.ts +0 -4
  136. package/lib/esm/execution-engine/utils.js +0 -30
  137. /package/lib/cjs/{execution-engine → aigne}/message-queue.d.ts +0 -0
  138. /package/lib/cjs/{execution-engine → aigne}/message-queue.js +0 -0
  139. /package/lib/cjs/{execution-engine → aigne}/usage.js +0 -0
  140. /package/lib/dts/{execution-engine → aigne}/message-queue.d.ts +0 -0
  141. /package/lib/esm/{execution-engine → aigne}/message-queue.d.ts +0 -0
  142. /package/lib/esm/{execution-engine → aigne}/message-queue.js +0 -0
  143. /package/lib/esm/{execution-engine → aigne}/usage.js +0 -0
@@ -0,0 +1,70 @@
1
+ import { BedrockRuntimeClient } from "@aws-sdk/client-bedrock-runtime";
2
+ import { z } from "zod";
3
+ import type { AgentInvokeOptions, AgentResponse } from "../agents/agent.js";
4
+ import type { Context } from "../aigne/context.js";
5
+ import { ChatModel, type ChatModelInput, type ChatModelOptions, type ChatModelOutput } from "./chat-model.js";
6
+ export declare function extractLastJsonObject(text: string): string | null;
7
+ export interface BedrockChatModelOptions {
8
+ accessKeyId?: string;
9
+ secretAccessKey?: string;
10
+ region?: string;
11
+ model?: string;
12
+ modelOptions?: ChatModelOptions;
13
+ }
14
+ export declare const bedrockChatModelOptionsSchema: z.ZodObject<{
15
+ region: z.ZodOptional<z.ZodString>;
16
+ model: z.ZodOptional<z.ZodString>;
17
+ modelOptions: z.ZodOptional<z.ZodObject<{
18
+ model: z.ZodOptional<z.ZodString>;
19
+ temperature: z.ZodOptional<z.ZodNumber>;
20
+ topP: z.ZodOptional<z.ZodNumber>;
21
+ frequencyPenalty: z.ZodOptional<z.ZodNumber>;
22
+ presencePenalty: z.ZodOptional<z.ZodNumber>;
23
+ parallelToolCalls: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
24
+ }, "strip", z.ZodTypeAny, {
25
+ parallelToolCalls: boolean;
26
+ model?: string | undefined;
27
+ temperature?: number | undefined;
28
+ topP?: number | undefined;
29
+ frequencyPenalty?: number | undefined;
30
+ presencePenalty?: number | undefined;
31
+ }, {
32
+ model?: string | undefined;
33
+ temperature?: number | undefined;
34
+ topP?: number | undefined;
35
+ frequencyPenalty?: number | undefined;
36
+ presencePenalty?: number | undefined;
37
+ parallelToolCalls?: boolean | undefined;
38
+ }>>;
39
+ }, "strip", z.ZodTypeAny, {
40
+ modelOptions?: {
41
+ parallelToolCalls: boolean;
42
+ model?: string | undefined;
43
+ temperature?: number | undefined;
44
+ topP?: number | undefined;
45
+ frequencyPenalty?: number | undefined;
46
+ presencePenalty?: number | undefined;
47
+ } | undefined;
48
+ model?: string | undefined;
49
+ region?: string | undefined;
50
+ }, {
51
+ modelOptions?: {
52
+ model?: string | undefined;
53
+ temperature?: number | undefined;
54
+ topP?: number | undefined;
55
+ frequencyPenalty?: number | undefined;
56
+ presencePenalty?: number | undefined;
57
+ parallelToolCalls?: boolean | undefined;
58
+ } | undefined;
59
+ model?: string | undefined;
60
+ region?: string | undefined;
61
+ }>;
62
+ export declare class BedrockChatModel extends ChatModel {
63
+ options?: BedrockChatModelOptions | undefined;
64
+ constructor(options?: BedrockChatModelOptions | undefined);
65
+ protected _client?: BedrockRuntimeClient;
66
+ get client(): BedrockRuntimeClient;
67
+ get modelOptions(): ChatModelOptions | undefined;
68
+ process(input: ChatModelInput, _context: Context, options?: AgentInvokeOptions): Promise<AgentResponse<ChatModelOutput>>;
69
+ private extractResultFromStream;
70
+ }
@@ -0,0 +1,268 @@
1
+ import { BedrockRuntimeClient, ConverseStreamCommand, } from "@aws-sdk/client-bedrock-runtime";
2
+ import { nanoid } from "nanoid";
3
+ import { z } from "zod";
4
+ import { parseJSON } from "../utils/json-schema.js";
5
+ import { getJsonOutputPrompt } from "../utils/prompts.js";
6
+ import { agentResponseStreamToObject } from "../utils/stream-utils.js";
7
+ import { checkArguments, isNonNullable } from "../utils/type-utils.js";
8
+ import { ChatModel, } from "./chat-model.js";
9
+ export function extractLastJsonObject(text) {
10
+ return text.replace(/<thinking>[\s\S]*?<\/thinking>/g, "").trim();
11
+ }
12
+ const BEDROCK_DEFAULT_CHAT_MODEL = "us.amazon.nova-lite-v1:0";
13
+ export const bedrockChatModelOptionsSchema = z.object({
14
+ region: z.string().optional(),
15
+ model: z.string().optional(),
16
+ modelOptions: z
17
+ .object({
18
+ model: z.string().optional(),
19
+ temperature: z.number().optional(),
20
+ topP: z.number().optional(),
21
+ frequencyPenalty: z.number().optional(),
22
+ presencePenalty: z.number().optional(),
23
+ parallelToolCalls: z.boolean().optional().default(true),
24
+ })
25
+ .optional(),
26
+ });
27
+ export class BedrockChatModel extends ChatModel {
28
+ options;
29
+ constructor(options) {
30
+ if (options)
31
+ checkArguments("BedrockChatModel", bedrockChatModelOptionsSchema, options);
32
+ super();
33
+ this.options = options;
34
+ }
35
+ _client;
36
+ get client() {
37
+ const credentials = this.options?.accessKeyId && this.options?.secretAccessKey
38
+ ? {
39
+ accessKeyId: this.options.accessKeyId,
40
+ secretAccessKey: this.options.secretAccessKey,
41
+ }
42
+ : undefined;
43
+ this._client ??= new BedrockRuntimeClient({
44
+ region: this.options?.region,
45
+ credentials,
46
+ });
47
+ return this._client;
48
+ }
49
+ get modelOptions() {
50
+ return this.options?.modelOptions;
51
+ }
52
+ async process(input, _context, options) {
53
+ const modelId = input.modelOptions?.model ?? this.modelOptions?.model ?? BEDROCK_DEFAULT_CHAT_MODEL;
54
+ const { messages, system } = getRunMessages(input);
55
+ const toolConfig = convertTools(input);
56
+ const body = {
57
+ modelId,
58
+ messages,
59
+ system,
60
+ toolConfig,
61
+ inferenceConfig: {
62
+ temperature: input.modelOptions?.temperature ?? this.modelOptions?.temperature,
63
+ topP: input.modelOptions?.topP ?? this.modelOptions?.topP,
64
+ },
65
+ };
66
+ const command = new ConverseStreamCommand(body);
67
+ const response = await this.client.send(command);
68
+ const jsonMode = input.responseFormat?.type === "json_schema";
69
+ if (options?.streaming && !jsonMode) {
70
+ return this.extractResultFromStream(response.stream, modelId, false, true);
71
+ }
72
+ const result = await this.extractResultFromStream(response.stream, modelId, jsonMode, false);
73
+ return result;
74
+ }
75
+ async extractResultFromStream(stream, modelId, jsonMode, streaming) {
76
+ if (!stream)
77
+ throw new Error("Unable to get AI model response.");
78
+ const result = new ReadableStream({
79
+ start: async (controller) => {
80
+ try {
81
+ controller.enqueue({ delta: { json: { model: modelId } } });
82
+ const toolCalls = [];
83
+ let text = "";
84
+ let usage;
85
+ for await (const chunk of stream) {
86
+ if (chunk.contentBlockStart?.start?.toolUse) {
87
+ const toolUse = chunk.contentBlockStart.start.toolUse;
88
+ if (!toolUse.name)
89
+ throw new Error("Tool use is invalid");
90
+ if (chunk.contentBlockStart.contentBlockIndex === undefined)
91
+ throw new Error("Tool use content block index is required");
92
+ toolCalls[chunk.contentBlockStart.contentBlockIndex] = {
93
+ type: "function",
94
+ id: toolUse.toolUseId || nanoid(),
95
+ function: {
96
+ name: toolUse.name,
97
+ arguments: {},
98
+ },
99
+ args: "",
100
+ };
101
+ }
102
+ if (chunk.contentBlockDelta) {
103
+ const block = chunk.contentBlockDelta;
104
+ const delta = block.delta;
105
+ if (delta?.text) {
106
+ text += delta.text;
107
+ if (!jsonMode) {
108
+ controller.enqueue({ delta: { text: { text: delta.text } } });
109
+ }
110
+ }
111
+ if (delta?.toolUse) {
112
+ if (block.contentBlockIndex === undefined)
113
+ throw new Error("Content block index is required");
114
+ const call = toolCalls[block.contentBlockIndex];
115
+ if (!call)
116
+ throw new Error("Tool call not found");
117
+ call.args += delta.toolUse.input;
118
+ }
119
+ }
120
+ if (chunk.metadata) {
121
+ usage = chunk.metadata.usage;
122
+ }
123
+ }
124
+ if (jsonMode && text) {
125
+ const match = extractLastJsonObject(text);
126
+ if (!match)
127
+ throw new Error("Failed to extract JSON object from model output");
128
+ controller.enqueue({
129
+ delta: { json: { json: parseJSON(match) } },
130
+ });
131
+ }
132
+ if (toolCalls.length) {
133
+ controller.enqueue({
134
+ delta: {
135
+ json: {
136
+ toolCalls: toolCalls
137
+ .map(({ args, ...c }) => ({
138
+ ...c,
139
+ function: { ...c.function, arguments: parseJSON(args) },
140
+ }))
141
+ .filter(isNonNullable),
142
+ },
143
+ },
144
+ });
145
+ }
146
+ controller.enqueue({ delta: { json: { usage } } });
147
+ controller.close();
148
+ }
149
+ catch (error) {
150
+ controller.error(error);
151
+ }
152
+ },
153
+ });
154
+ return streaming ? result : await agentResponseStreamToObject(result);
155
+ }
156
+ }
157
+ const getRunMessages = ({ messages: msgs, responseFormat, }) => {
158
+ const system = [];
159
+ const messages = [];
160
+ for (const msg of msgs) {
161
+ if (msg.role === "system") {
162
+ if (typeof msg.content !== "string")
163
+ throw new Error("System message must have content");
164
+ system.push({ text: msg.content });
165
+ }
166
+ else if (msg.role === "tool") {
167
+ if (!msg.toolCallId)
168
+ throw new Error("Tool message must have toolCallId");
169
+ if (typeof msg.content !== "string")
170
+ throw new Error("Tool message must have string content");
171
+ if (messages.at(-1)?.role === "user") {
172
+ messages.at(-1)?.content?.push({
173
+ toolResult: { toolUseId: msg.toolCallId, content: [{ json: parseJSON(msg.content) }] },
174
+ });
175
+ }
176
+ else {
177
+ messages.push({
178
+ role: "user",
179
+ content: [
180
+ {
181
+ toolResult: {
182
+ toolUseId: msg.toolCallId,
183
+ content: [{ json: parseJSON(msg.content) }],
184
+ },
185
+ },
186
+ ],
187
+ });
188
+ }
189
+ }
190
+ else if (msg.role === "user") {
191
+ if (!msg.content)
192
+ throw new Error("User message must have content");
193
+ messages.push({ role: "user", content: convertContent(msg.content) });
194
+ }
195
+ else if (msg.role === "agent") {
196
+ if (msg.toolCalls?.length) {
197
+ messages.push({
198
+ role: "assistant",
199
+ content: msg.toolCalls.map((i) => ({
200
+ toolUse: {
201
+ toolUseId: i.id,
202
+ name: i.function.name,
203
+ input: i.function.arguments,
204
+ },
205
+ })),
206
+ });
207
+ }
208
+ else if (msg.content) {
209
+ messages.push({ role: "assistant", content: convertContent(msg.content) });
210
+ }
211
+ else {
212
+ throw new Error("Agent message must have content or toolCalls");
213
+ }
214
+ }
215
+ }
216
+ if (messages.at(0)?.role !== "user") {
217
+ messages.unshift({ role: "user", content: [{ text: "." }] });
218
+ }
219
+ if (responseFormat?.type === "json_schema") {
220
+ system.push({
221
+ text: getJsonOutputPrompt(responseFormat.jsonSchema.schema),
222
+ });
223
+ }
224
+ return { messages, system };
225
+ };
226
+ function convertContent(content) {
227
+ if (typeof content === "string")
228
+ return [{ text: content }];
229
+ if (Array.isArray(content)) {
230
+ const blocks = [];
231
+ for (const item of content) {
232
+ if (item.type === "text")
233
+ blocks.push({ text: item.text });
234
+ }
235
+ return blocks;
236
+ }
237
+ throw new Error("Invalid chat message content");
238
+ }
239
+ function convertTools({ tools, toolChoice }) {
240
+ if (!tools?.length || toolChoice === "none")
241
+ return undefined;
242
+ let choice;
243
+ if (typeof toolChoice === "object" && "type" in toolChoice && toolChoice.type === "function") {
244
+ choice = { tool: { name: toolChoice.function.name } };
245
+ }
246
+ else if (toolChoice === "required") {
247
+ choice = { any: {} };
248
+ }
249
+ else if (toolChoice === "auto") {
250
+ choice = { auto: {} };
251
+ }
252
+ return {
253
+ tools: tools.map((i) => {
254
+ const parameters = i.function.parameters;
255
+ if (Object.keys(parameters).length === 0) {
256
+ parameters.type = "object";
257
+ }
258
+ return {
259
+ toolSpec: {
260
+ name: i.function.name,
261
+ description: i.function.description,
262
+ inputSchema: { json: parameters },
263
+ },
264
+ };
265
+ }),
266
+ toolChoice: choice,
267
+ };
268
+ }
@@ -1,11 +1,12 @@
1
1
  import { Agent, type Message } from "../agents/agent.js";
2
- import type { Context } from "../execution-engine/context.js";
2
+ import type { Context } from "../aigne/context.js";
3
3
  export declare abstract class ChatModel extends Agent<ChatModelInput, ChatModelOutput> {
4
4
  constructor();
5
5
  protected supportsParallelToolCalls: boolean;
6
6
  getModelCapabilities(): {
7
7
  supportsParallelToolCalls: boolean;
8
8
  };
9
+ private validateToolNames;
9
10
  protected preprocess(input: ChatModelInput, context: Context): void;
10
11
  protected postprocess(input: ChatModelInput, output: ChatModelOutput, context: Context): void;
11
12
  }
@@ -13,6 +13,13 @@ export class ChatModel extends Agent {
13
13
  supportsParallelToolCalls: this.supportsParallelToolCalls,
14
14
  };
15
15
  }
16
+ validateToolNames(tools) {
17
+ for (const tool of tools ?? []) {
18
+ if (!/^[a-zA-Z0-9_]+$/.test(tool.function.name)) {
19
+ throw new Error(`Tool name "${tool.function.name}" can only contain letters, numbers, and underscores`);
20
+ }
21
+ }
22
+ }
16
23
  preprocess(input, context) {
17
24
  super.preprocess(input, context);
18
25
  const { limits, usage } = context;
@@ -20,6 +27,7 @@ export class ChatModel extends Agent {
20
27
  if (limits?.maxTokens && usedTokens >= limits.maxTokens) {
21
28
  throw new Error(`Exceeded max tokens ${usedTokens}/${limits.maxTokens}`);
22
29
  }
30
+ this.validateToolNames(input.tools);
23
31
  }
24
32
  postprocess(input, output, context) {
25
33
  super.postprocess(input, output, context);
@@ -1,7 +1,7 @@
1
1
  import Anthropic from "@anthropic-ai/sdk";
2
2
  import { z } from "zod";
3
- import type { AgentCallOptions, AgentResponse } from "../agents/agent.js";
4
- import type { Context } from "../execution-engine/context.js";
3
+ import type { AgentInvokeOptions, AgentResponse } from "../agents/agent.js";
4
+ import type { Context } from "../aigne/context.js";
5
5
  import { ChatModel, type ChatModelInput, type ChatModelOptions, type ChatModelOutput } from "./chat-model.js";
6
6
  export interface ClaudeChatModelOptions {
7
7
  apiKey?: string;
@@ -62,7 +62,7 @@ export declare class ClaudeChatModel extends ChatModel {
62
62
  protected _client?: Anthropic;
63
63
  get client(): Anthropic;
64
64
  get modelOptions(): ChatModelOptions | undefined;
65
- process(input: ChatModelInput, _context: Context, options?: AgentCallOptions): Promise<AgentResponse<ChatModelOutput>>;
65
+ process(input: ChatModelInput, _context: Context, options?: AgentInvokeOptions): Promise<AgentResponse<ChatModelOutput>>;
66
66
  private extractResultFromClaudeStream;
67
67
  private requestStructuredOutput;
68
68
  }
@@ -5,4 +5,5 @@ export declare class GeminiChatModel extends OpenAIChatModel {
5
5
  protected supportsEndWithSystemMessage: boolean;
6
6
  protected supportsToolsUseWithJsonSchema: boolean;
7
7
  protected supportsParallelToolCalls: boolean;
8
+ protected supportsToolStreaming: boolean;
8
9
  }
@@ -13,4 +13,5 @@ export class GeminiChatModel extends OpenAIChatModel {
13
13
  supportsEndWithSystemMessage = false;
14
14
  supportsToolsUseWithJsonSchema = false;
15
15
  supportsParallelToolCalls = false;
16
+ supportsToolStreaming = false;
16
17
  }
@@ -1,8 +1,8 @@
1
1
  import OpenAI from "openai";
2
2
  import type { ChatCompletionMessageParam, ChatCompletionTool } from "openai/resources";
3
3
  import { z } from "zod";
4
- import type { AgentCallOptions, AgentResponse } from "../agents/agent.js";
5
- import type { Context } from "../execution-engine/context.js";
4
+ import type { AgentInvokeOptions, AgentResponse } from "../agents/agent.js";
5
+ import type { Context } from "../aigne/context.js";
6
6
  import { ChatModel, type ChatModelInput, type ChatModelInputMessage, type ChatModelInputTool, type ChatModelOptions, type ChatModelOutput, type Role } from "./chat-model.js";
7
7
  export interface OpenAIChatModelCapabilities {
8
8
  supportsNativeStructuredOutputs: boolean;
@@ -10,6 +10,7 @@ export interface OpenAIChatModelCapabilities {
10
10
  supportsToolsUseWithJsonSchema: boolean;
11
11
  supportsParallelToolCalls: boolean;
12
12
  supportsToolsEmptyParameters: boolean;
13
+ supportsToolStreaming: boolean;
13
14
  supportsTemperature: boolean;
14
15
  }
15
16
  export interface OpenAIChatModelOptions {
@@ -80,14 +81,16 @@ export declare class OpenAIChatModel extends ChatModel {
80
81
  protected supportsToolsUseWithJsonSchema: boolean;
81
82
  protected supportsParallelToolCalls: boolean;
82
83
  protected supportsToolsEmptyParameters: boolean;
84
+ protected supportsToolStreaming: boolean;
83
85
  protected supportsTemperature: boolean;
84
86
  get client(): OpenAI;
85
87
  get modelOptions(): ChatModelOptions | undefined;
86
- process(input: ChatModelInput, _context: Context, options?: AgentCallOptions): Promise<AgentResponse<ChatModelOutput>>;
88
+ process(input: ChatModelInput, _context: Context, options?: AgentInvokeOptions): Promise<AgentResponse<ChatModelOutput>>;
87
89
  private getParallelToolCalls;
88
90
  private getRunMessages;
89
91
  private getRunResponseFormat;
90
92
  private requestStructuredOutput;
93
+ private extractResultFromStream;
91
94
  }
92
95
  export declare const ROLE_MAP: {
93
96
  [key in Role]: ChatCompletionMessageParam["role"];
@@ -45,6 +45,7 @@ export class OpenAIChatModel extends ChatModel {
45
45
  supportsToolsUseWithJsonSchema = true;
46
46
  supportsParallelToolCalls = true;
47
47
  supportsToolsEmptyParameters = true;
48
+ supportsToolStreaming = true;
48
49
  supportsTemperature = true;
49
50
  get client() {
50
51
  const apiKey = this.options?.apiKey || process.env[this.apiKeyEnvName] || this.apiKeyDefault;
@@ -86,9 +87,9 @@ export class OpenAIChatModel extends ChatModel {
86
87
  response_format: responseFormat,
87
88
  });
88
89
  if (options?.streaming && input.responseFormat?.type !== "json_schema") {
89
- return await extractResultFromStream(stream, false, true);
90
+ return await this.extractResultFromStream(stream, false, true);
90
91
  }
91
- const result = await extractResultFromStream(stream, jsonMode);
92
+ const result = await this.extractResultFromStream(stream, jsonMode);
92
93
  if (!this.supportsToolsUseWithJsonSchema &&
93
94
  !result.toolCalls?.length &&
94
95
  input.responseFormat?.type === "json_schema" &&
@@ -154,7 +155,103 @@ export class OpenAIChatModel extends ChatModel {
154
155
  ...body,
155
156
  response_format: resolvedResponseFormat,
156
157
  });
157
- return extractResultFromStream(res, jsonMode);
158
+ return this.extractResultFromStream(res, jsonMode);
159
+ }
160
+ async extractResultFromStream(stream, jsonMode, streaming) {
161
+ const result = new ReadableStream({
162
+ start: async (controller) => {
163
+ try {
164
+ let text = "";
165
+ let refusal = "";
166
+ const toolCalls = [];
167
+ let model;
168
+ for await (const chunk of stream) {
169
+ const choice = chunk.choices?.[0];
170
+ if (!model) {
171
+ model = chunk.model;
172
+ controller.enqueue({
173
+ delta: {
174
+ json: {
175
+ model,
176
+ },
177
+ },
178
+ });
179
+ }
180
+ if (choice?.delta.tool_calls?.length) {
181
+ for (const call of choice.delta.tool_calls) {
182
+ if (this.supportsToolStreaming && call.index !== undefined) {
183
+ handleToolCallDelta(toolCalls, call);
184
+ }
185
+ else {
186
+ handleCompleteToolCall(toolCalls, call);
187
+ }
188
+ }
189
+ }
190
+ if (choice?.delta.content) {
191
+ text += choice.delta.content;
192
+ if (!jsonMode) {
193
+ controller.enqueue({
194
+ delta: {
195
+ text: {
196
+ text: choice.delta.content,
197
+ },
198
+ },
199
+ });
200
+ }
201
+ }
202
+ if (choice?.delta.refusal) {
203
+ refusal += choice.delta.refusal;
204
+ if (!jsonMode) {
205
+ controller.enqueue({
206
+ delta: {
207
+ text: { text: choice.delta.refusal },
208
+ },
209
+ });
210
+ }
211
+ }
212
+ if (chunk.usage) {
213
+ controller.enqueue({
214
+ delta: {
215
+ json: {
216
+ usage: {
217
+ inputTokens: chunk.usage.prompt_tokens,
218
+ outputTokens: chunk.usage.completion_tokens,
219
+ },
220
+ },
221
+ },
222
+ });
223
+ }
224
+ }
225
+ text = text || refusal;
226
+ if (jsonMode && text) {
227
+ controller.enqueue({
228
+ delta: {
229
+ json: {
230
+ json: parseJSON(text),
231
+ },
232
+ },
233
+ });
234
+ }
235
+ if (toolCalls.length) {
236
+ controller.enqueue({
237
+ delta: {
238
+ json: {
239
+ toolCalls: toolCalls.map(({ args, ...c }) => ({
240
+ ...c,
241
+ function: { ...c.function, arguments: parseJSON(args) },
242
+ })),
243
+ },
244
+ },
245
+ });
246
+ }
247
+ controller.close();
248
+ }
249
+ catch (error) {
250
+ controller.error(error);
251
+ }
252
+ },
253
+ });
254
+ return streaming ? result : await agentResponseStreamToObject(result);
158
255
  }
159
256
  }
160
257
  export const ROLE_MAP = {
@@ -235,103 +332,6 @@ export function jsonSchemaToOpenAIJsonSchema(schema) {
235
332
  }
236
333
  return schema;
237
334
  }
238
- async function extractResultFromStream(stream, jsonMode, streaming) {
239
- const result = new ReadableStream({
240
- async start(controller) {
241
- try {
242
- let text = "";
243
- let refusal = "";
244
- const toolCalls = [];
245
- let model;
246
- for await (const chunk of stream) {
247
- const choice = chunk.choices?.[0];
248
- if (!model) {
249
- model = chunk.model;
250
- controller.enqueue({
251
- delta: {
252
- json: {
253
- model,
254
- },
255
- },
256
- });
257
- }
258
- if (choice?.delta.tool_calls?.length) {
259
- for (const call of choice.delta.tool_calls) {
260
- // Gemini not support tool call delta
261
- if (call.index !== undefined) {
262
- handleToolCallDelta(toolCalls, call);
263
- }
264
- else {
265
- handleCompleteToolCall(toolCalls, call);
266
- }
267
- }
268
- }
269
- if (choice?.delta.content) {
270
- text += choice.delta.content;
271
- if (!jsonMode) {
272
- controller.enqueue({
273
- delta: {
274
- text: {
275
- text: choice.delta.content,
276
- },
277
- },
278
- });
279
- }
280
- }
281
- if (choice?.delta.refusal) {
282
- refusal += choice.delta.refusal;
283
- if (!jsonMode) {
284
- controller.enqueue({
285
- delta: {
286
- text: { text: choice.delta.refusal },
287
- },
288
- });
289
- }
290
- }
291
- if (chunk.usage) {
292
- controller.enqueue({
293
- delta: {
294
- json: {
295
- usage: {
296
- inputTokens: chunk.usage.prompt_tokens,
297
- outputTokens: chunk.usage.completion_tokens,
298
- },
299
- },
300
- },
301
- });
302
- }
303
- }
304
- text = text || refusal;
305
- if (jsonMode && text) {
306
- controller.enqueue({
307
- delta: {
308
- json: {
309
- json: parseJSON(text),
310
- },
311
- },
312
- });
313
- }
314
- if (toolCalls.length) {
315
- controller.enqueue({
316
- delta: {
317
- json: {
318
- toolCalls: toolCalls.map(({ args, ...c }) => ({
319
- ...c,
320
- function: { ...c.function, arguments: parseJSON(args) },
321
- })),
322
- },
323
- },
324
- });
325
- }
326
- controller.close();
327
- }
328
- catch (error) {
329
- controller.error(error);
330
- }
331
- },
332
- });
333
- return streaming ? result : await agentResponseStreamToObject(result);
334
- }
335
335
  function handleToolCallDelta(toolCalls, call) {
336
336
  toolCalls[call.index] ??= {
337
337
  id: call.id || nanoid(),
@@ -2,7 +2,7 @@ import type { GetPromptResult } from "@modelcontextprotocol/sdk/types.js";
2
2
  import { Agent, type Message } from "../agents/agent.js";
3
3
  import type { AIAgent } from "../agents/ai-agent.js";
4
4
  import type { AgentMemory } from "../agents/memory.js";
5
- import type { Context } from "../execution-engine/context.js";
5
+ import type { Context } from "../aigne/context.js";
6
6
  import type { ChatModel, ChatModelInput } from "../models/chat-model.js";
7
7
  import { ChatMessagesTemplate } from "./template.js";
8
8
  export declare const MESSAGE_KEY = "$message";