@aigne/core 1.3.0 → 1.4.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 (44) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/lib/cjs/agents/agent.d.ts +7 -4
  3. package/lib/cjs/agents/agent.js +25 -8
  4. package/lib/cjs/index.d.ts +2 -0
  5. package/lib/cjs/index.js +2 -0
  6. package/lib/cjs/models/chat-model.d.ts +1 -0
  7. package/lib/cjs/models/chat-model.js +1 -0
  8. package/lib/cjs/models/claude-chat-model.d.ts +9 -9
  9. package/lib/cjs/models/claude-chat-model.js +76 -46
  10. package/lib/cjs/models/openai-chat-model.d.ts +10 -9
  11. package/lib/cjs/models/openai-chat-model.js +22 -12
  12. package/lib/cjs/models/xai-chat-model.d.ts +12 -0
  13. package/lib/cjs/models/xai-chat-model.js +18 -0
  14. package/lib/cjs/package.json +1 -0
  15. package/lib/cjs/prompt/prompt-builder.d.ts +2 -2
  16. package/lib/cjs/utils/json-schema.d.ts +10 -1
  17. package/lib/cjs/utils/json-schema.js +25 -0
  18. package/lib/cjs/utils/run-chat-loop.js +10 -5
  19. package/lib/dts/agents/agent.d.ts +7 -4
  20. package/lib/dts/index.d.ts +2 -0
  21. package/lib/dts/models/chat-model.d.ts +1 -0
  22. package/lib/dts/models/claude-chat-model.d.ts +9 -9
  23. package/lib/dts/models/openai-chat-model.d.ts +10 -9
  24. package/lib/dts/models/xai-chat-model.d.ts +12 -0
  25. package/lib/dts/prompt/prompt-builder.d.ts +2 -2
  26. package/lib/dts/utils/json-schema.d.ts +10 -1
  27. package/lib/esm/agents/agent.d.ts +7 -4
  28. package/lib/esm/agents/agent.js +25 -8
  29. package/lib/esm/index.d.ts +2 -0
  30. package/lib/esm/index.js +2 -0
  31. package/lib/esm/models/chat-model.d.ts +1 -0
  32. package/lib/esm/models/chat-model.js +1 -0
  33. package/lib/esm/models/claude-chat-model.d.ts +9 -9
  34. package/lib/esm/models/claude-chat-model.js +76 -46
  35. package/lib/esm/models/openai-chat-model.d.ts +10 -9
  36. package/lib/esm/models/openai-chat-model.js +22 -12
  37. package/lib/esm/models/xai-chat-model.d.ts +12 -0
  38. package/lib/esm/models/xai-chat-model.js +14 -0
  39. package/lib/esm/package.json +1 -0
  40. package/lib/esm/prompt/prompt-builder.d.ts +2 -2
  41. package/lib/esm/utils/json-schema.d.ts +10 -1
  42. package/lib/esm/utils/json-schema.js +23 -0
  43. package/lib/esm/utils/run-chat-loop.js +10 -5
  44. package/package.json +3 -2
package/CHANGELOG.md CHANGED
@@ -22,6 +22,25 @@
22
22
  * rename @aigne/core-next to @aigne/core ([3a81009](https://github.com/AIGNE-io/aigne-framework/commit/3a8100962c81813217b687ae28e8de604419c622))
23
23
  * use text resource from MCP correctly ([8b9eba8](https://github.com/AIGNE-io/aigne-framework/commit/8b9eba83352ec096a2a5d4f410d4c4bde7420bce))
24
24
 
25
+ ## [1.4.0](https://github.com/AIGNE-io/aigne-framework/compare/core-v1.3.1...core-v1.4.0) (2025-03-26)
26
+
27
+
28
+ ### Features
29
+
30
+ * **core:** add xAI chat model adapter ([#34](https://github.com/AIGNE-io/aigne-framework/issues/34)) ([b228d22](https://github.com/AIGNE-io/aigne-framework/commit/b228d22b550535ab8e511f13de9e4a65dd73e3c0))
31
+
32
+
33
+ ### Bug Fixes
34
+
35
+ * **orchestrator:** refactor and enhance orchestrator with step synthesis ([#31](https://github.com/AIGNE-io/aigne-framework/issues/31)) ([ba9fca0](https://github.com/AIGNE-io/aigne-framework/commit/ba9fca04fad71d49c8f4f52172b56668a94ea714))
36
+
37
+ ## [1.3.1](https://github.com/AIGNE-io/aigne-framework/compare/core-v1.3.0...core-v1.3.1) (2025-03-25)
38
+
39
+
40
+ ### Bug Fixes
41
+
42
+ * **core:** use system message as user message for claude model if needed ([#32](https://github.com/AIGNE-io/aigne-framework/issues/32)) ([316a6d5](https://github.com/AIGNE-io/aigne-framework/commit/316a6d51f885cceee4020c24695f6588f6b7425f))
43
+
25
44
  ## [1.2.0](https://github.com/AIGNE-io/aigne-framework/compare/core-next-v1.1.0...core-next-v1.2.0) (2025-03-18)
26
45
 
27
46
  - chore: release v1.2.0
@@ -11,8 +11,8 @@ export interface AgentOptions<I extends Message = Message, O extends Message = M
11
11
  publishTopic?: PublishTopic<O>;
12
12
  name?: string;
13
13
  description?: string;
14
- inputSchema?: ZodType<I>;
15
- outputSchema?: ZodType<O>;
14
+ inputSchema?: AgentInputOutputSchema<I>;
15
+ outputSchema?: AgentInputOutputSchema<O>;
16
16
  includeInputInOutput?: boolean;
17
17
  tools?: (Agent | FunctionAgentFn)[];
18
18
  disableLogging?: boolean;
@@ -27,8 +27,10 @@ export declare abstract class Agent<I extends Message = Message, O extends Messa
27
27
  */
28
28
  get topic(): string;
29
29
  readonly description?: string;
30
- readonly inputSchema: ZodType<I>;
31
- readonly outputSchema: ZodType<O>;
30
+ private readonly _inputSchema?;
31
+ private readonly _outputSchema?;
32
+ get inputSchema(): ZodType<I>;
33
+ get outputSchema(): ZodType<O>;
32
34
  readonly includeInputInOutput?: boolean;
33
35
  readonly subscribeTopic?: SubscribeTopic;
34
36
  readonly publishTopic?: PublishTopic<Message>;
@@ -49,6 +51,7 @@ export declare abstract class Agent<I extends Message = Message, O extends Messa
49
51
  abstract process(input: I, context?: Context): Promise<O | TransferAgentOutput>;
50
52
  shutdown(): Promise<void>;
51
53
  }
54
+ export type AgentInputOutputSchema<I extends Message = Message> = ZodType<I> | ((agent: Agent) => ZodType<I>);
52
55
  export interface FunctionAgentOptions<I extends Message = Message, O extends Message = Message> extends AgentOptions<I, O> {
53
56
  fn?: FunctionAgentFn<I, O>;
54
57
  }
@@ -11,12 +11,12 @@ class Agent {
11
11
  constructor({ inputSchema, outputSchema, ...options }) {
12
12
  this.name = options.name || this.constructor.name;
13
13
  this.description = options.description;
14
- if ((inputSchema && !(inputSchema instanceof zod_1.ZodObject)) ||
15
- (outputSchema && !(outputSchema instanceof zod_1.ZodObject))) {
16
- throw new Error("inputSchema must be a Zod object");
17
- }
18
- this.inputSchema = (inputSchema || zod_1.z.object({})).passthrough();
19
- this.outputSchema = (outputSchema || zod_1.z.object({})).passthrough();
14
+ if (inputSchema)
15
+ checkAgentInputOutputSchema(inputSchema);
16
+ if (outputSchema)
17
+ checkAgentInputOutputSchema(outputSchema);
18
+ this._inputSchema = inputSchema;
19
+ this._outputSchema = outputSchema;
20
20
  this.includeInputInOutput = options.includeInputInOutput;
21
21
  this.subscribeTopic = options.subscribeTopic;
22
22
  this.publishTopic = options.publishTopic;
@@ -41,8 +41,20 @@ class Agent {
41
41
  return `$agent_${this.name}`;
42
42
  }
43
43
  description;
44
- inputSchema;
45
- outputSchema;
44
+ _inputSchema;
45
+ _outputSchema;
46
+ get inputSchema() {
47
+ const s = this._inputSchema;
48
+ const schema = typeof s === "function" ? s(this) : s || zod_1.z.object({});
49
+ checkAgentInputOutputSchema(schema);
50
+ return schema.passthrough();
51
+ }
52
+ get outputSchema() {
53
+ const s = this._outputSchema;
54
+ const schema = typeof s === "function" ? s(this) : s || zod_1.z.object({});
55
+ checkAgentInputOutputSchema(schema);
56
+ return schema.passthrough();
57
+ }
46
58
  includeInputInOutput;
47
59
  subscribeTopic;
48
60
  publishTopic;
@@ -98,6 +110,11 @@ class Agent {
98
110
  }
99
111
  }
100
112
  exports.Agent = Agent;
113
+ function checkAgentInputOutputSchema(schema) {
114
+ if (!(schema instanceof zod_1.ZodObject) && typeof schema !== "function") {
115
+ throw new Error("schema must be a zod object or function return a zod object ");
116
+ }
117
+ }
101
118
  class FunctionAgent extends Agent {
102
119
  static from(options) {
103
120
  return typeof options === "function" ? functionToAgent(options) : new FunctionAgent(options);
@@ -8,7 +8,9 @@ export * from "./execution-engine/index.js";
8
8
  export * from "./models/chat-model.js";
9
9
  export * from "./models/claude-chat-model.js";
10
10
  export * from "./models/openai-chat-model.js";
11
+ export * from "./models/xai-chat-model.js";
11
12
  export * from "./prompt/prompt-builder.js";
12
13
  export * from "./prompt/template.js";
14
+ export * from "./utils/json-schema.js";
13
15
  export * from "./utils/logger.js";
14
16
  export * from "./utils/run-chat-loop.js";
package/lib/cjs/index.js CHANGED
@@ -24,7 +24,9 @@ __exportStar(require("./execution-engine/index.js"), exports);
24
24
  __exportStar(require("./models/chat-model.js"), exports);
25
25
  __exportStar(require("./models/claude-chat-model.js"), exports);
26
26
  __exportStar(require("./models/openai-chat-model.js"), exports);
27
+ __exportStar(require("./models/xai-chat-model.js"), exports);
27
28
  __exportStar(require("./prompt/prompt-builder.js"), exports);
28
29
  __exportStar(require("./prompt/template.js"), exports);
30
+ __exportStar(require("./utils/json-schema.js"), exports);
29
31
  __exportStar(require("./utils/logger.js"), exports);
30
32
  __exportStar(require("./utils/run-chat-loop.js"), exports);
@@ -65,6 +65,7 @@ export interface ChatModelOptions {
65
65
  topP?: number;
66
66
  frequencyPenalty?: number;
67
67
  presencePenalty?: number;
68
+ parallelToolCalls?: boolean;
68
69
  }
69
70
  export interface ChatModelOutput extends Message {
70
71
  text?: string;
@@ -68,6 +68,7 @@ const chatModelOptionsSchema = zod_1.z.object({
68
68
  topP: zod_1.z.number().optional(),
69
69
  frequencyPenalty: zod_1.z.number().optional(),
70
70
  presencePenalty: zod_1.z.number().optional(),
71
+ parallelToolCalls: zod_1.z.boolean().optional().default(true),
71
72
  });
72
73
  const chatModelInputSchema = zod_1.z.object({
73
74
  messages: zod_1.z.array(chatModelInputMessageSchema),
@@ -1,16 +1,16 @@
1
1
  import Anthropic from "@anthropic-ai/sdk";
2
- import { ChatModel, type ChatModelInput, type ChatModelOutput } from "./chat-model.js";
2
+ import { ChatModel, type ChatModelInput, type ChatModelOptions, type ChatModelOutput } from "./chat-model.js";
3
+ export interface ClaudeChatModelOptions {
4
+ apiKey?: string;
5
+ model?: string;
6
+ modelOptions?: ChatModelOptions;
7
+ }
3
8
  export declare class ClaudeChatModel extends ChatModel {
4
- config?: {
5
- apiKey?: string;
6
- model?: string;
7
- } | undefined;
8
- constructor(config?: {
9
- apiKey?: string;
10
- model?: string;
11
- } | undefined);
9
+ options?: ClaudeChatModelOptions | undefined;
10
+ constructor(options?: ClaudeChatModelOptions | undefined);
12
11
  private _client?;
13
12
  get client(): Anthropic;
13
+ get modelOptions(): ChatModelOptions | undefined;
14
14
  process(input: ChatModelInput): Promise<ChatModelOutput>;
15
15
  private extractResultFromClaudeStream;
16
16
  private requestStructuredOutput;
@@ -5,32 +5,40 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.ClaudeChatModel = void 0;
7
7
  const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
8
+ const lodash_es_1 = require("lodash-es");
9
+ const json_schema_js_1 = require("../utils/json-schema.js");
10
+ const logger_js_1 = require("../utils/logger.js");
8
11
  const type_utils_js_1 = require("../utils/type-utils.js");
9
12
  const chat_model_js_1 = require("./chat-model.js");
10
13
  const CHAT_MODEL_CLAUDE_DEFAULT_MODEL = "claude-3-7-sonnet-latest";
11
14
  class ClaudeChatModel extends chat_model_js_1.ChatModel {
12
- config;
13
- constructor(config) {
15
+ options;
16
+ constructor(options) {
14
17
  super();
15
- this.config = config;
18
+ this.options = options;
16
19
  }
17
20
  _client;
18
21
  get client() {
19
- if (!this.config?.apiKey)
22
+ if (!this.options?.apiKey)
20
23
  throw new Error("Api Key is required for ClaudeChatModel");
21
- this._client ??= new sdk_1.default({ apiKey: this.config.apiKey });
24
+ this._client ??= new sdk_1.default({ apiKey: this.options.apiKey });
22
25
  return this._client;
23
26
  }
27
+ get modelOptions() {
28
+ return this.options?.modelOptions;
29
+ }
24
30
  async process(input) {
25
- const model = this.config?.model || CHAT_MODEL_CLAUDE_DEFAULT_MODEL;
31
+ const model = this.options?.model || CHAT_MODEL_CLAUDE_DEFAULT_MODEL;
32
+ const disableParallelToolUse = input.modelOptions?.parallelToolCalls === false ||
33
+ this.modelOptions?.parallelToolCalls === false;
26
34
  const body = {
27
35
  model,
28
- temperature: input.modelOptions?.temperature,
29
- top_p: input.modelOptions?.topP,
36
+ temperature: input.modelOptions?.temperature ?? this.modelOptions?.temperature,
37
+ top_p: input.modelOptions?.topP ?? this.modelOptions?.topP,
30
38
  // TODO: make dynamic based on model https://docs.anthropic.com/en/docs/about-claude/models/all-models
31
39
  max_tokens: /claude-3-[5|7]/.test(model) ? 8192 : 4096,
32
40
  ...convertMessages(input),
33
- ...convertTools(input),
41
+ ...convertTools({ ...input, disableParallelToolUse }),
34
42
  };
35
43
  const stream = this.client.messages.stream({
36
44
  ...body,
@@ -45,41 +53,53 @@ class ClaudeChatModel extends chat_model_js_1.ChatModel {
45
53
  return result;
46
54
  }
47
55
  async extractResultFromClaudeStream(stream) {
48
- let text = "";
49
- const toolCalls = [];
50
- for await (const chunk of stream) {
51
- // handle streaming text
52
- if (chunk.type === "content_block_delta" && chunk.delta.type === "text_delta") {
53
- text += chunk.delta.text;
56
+ const logs = [];
57
+ try {
58
+ let text = "";
59
+ const toolCalls = [];
60
+ for await (const chunk of stream) {
61
+ logs.push(JSON.stringify(chunk));
62
+ // handle streaming text
63
+ if (chunk.type === "content_block_delta" && chunk.delta.type === "text_delta") {
64
+ text += chunk.delta.text;
65
+ }
66
+ if (chunk.type === "content_block_start" && chunk.content_block.type === "tool_use") {
67
+ toolCalls[chunk.index] = {
68
+ type: "function",
69
+ id: chunk.content_block.id,
70
+ function: {
71
+ name: chunk.content_block.name,
72
+ arguments: {},
73
+ },
74
+ args: "",
75
+ };
76
+ }
77
+ if (chunk.type === "content_block_delta" && chunk.delta.type === "input_json_delta") {
78
+ const call = toolCalls[chunk.index];
79
+ if (!call)
80
+ throw new Error("Tool call not found");
81
+ call.args += chunk.delta.partial_json;
82
+ }
54
83
  }
55
- if (chunk.type === "content_block_start" && chunk.content_block.type === "tool_use") {
56
- toolCalls[chunk.index] = {
57
- type: "function",
58
- id: chunk.content_block.id,
84
+ const result = { text };
85
+ if (toolCalls.length) {
86
+ result.toolCalls = toolCalls
87
+ .map(({ args, ...c }) => ({
88
+ ...c,
59
89
  function: {
60
- name: chunk.content_block.name,
61
- arguments: {},
90
+ ...c.function,
91
+ // NOTE: claude may return a blank string for empty object (the tool's input schema is a empty object)
92
+ arguments: args.trim() ? (0, json_schema_js_1.parseJSON)(args) : {},
62
93
  },
63
- args: "",
64
- };
65
- }
66
- if (chunk.type === "content_block_delta" && chunk.delta.type === "input_json_delta") {
67
- const call = toolCalls[chunk.index];
68
- if (!call)
69
- throw new Error("Tool call not found");
70
- call.args += chunk.delta.partial_json;
94
+ }))
95
+ .filter(type_utils_js_1.isNonNullable);
71
96
  }
97
+ return result;
72
98
  }
73
- const result = { text };
74
- if (toolCalls.length) {
75
- result.toolCalls = toolCalls
76
- .map(({ args, ...c }) => ({
77
- ...c,
78
- function: { ...c.function, arguments: JSON.parse(args) },
79
- }))
80
- .filter(type_utils_js_1.isNonNullable);
99
+ catch (error) {
100
+ logger_js_1.logger.debug("Failed to process Claude stream", { error, logs });
101
+ throw error;
81
102
  }
82
- return result;
83
103
  }
84
104
  async requestStructuredOutput(body, responseFormat) {
85
105
  if (responseFormat?.type !== "json_schema") {
@@ -132,7 +152,7 @@ function convertMessages({ messages, responseFormat }) {
132
152
  else if (msg.role === "user") {
133
153
  if (!msg.content)
134
154
  throw new Error("User message must have content");
135
- msgs.push({ role: "user", content: contentBlockParamFromContent(msg.content) });
155
+ msgs.push({ role: "user", content: convertContent(msg.content) });
136
156
  }
137
157
  else if (msg.role === "agent") {
138
158
  if (msg.toolCalls?.length) {
@@ -147,7 +167,7 @@ function convertMessages({ messages, responseFormat }) {
147
167
  });
148
168
  }
149
169
  else if (msg.content) {
150
- msgs.push({ role: "assistant", content: contentBlockParamFromContent(msg.content) });
170
+ msgs.push({ role: "assistant", content: convertContent(msg.content) });
151
171
  }
152
172
  else {
153
173
  throw new Error("Agent message must have content or toolCalls");
@@ -157,9 +177,16 @@ function convertMessages({ messages, responseFormat }) {
157
177
  if (responseFormat?.type === "json_schema") {
158
178
  systemMessages.push(`You should provide a json response with schema: ${JSON.stringify(responseFormat.jsonSchema.schema)}`);
159
179
  }
160
- return { messages: msgs, system: systemMessages.join("\n").trim() || undefined };
180
+ const system = systemMessages.join("\n").trim() || undefined;
181
+ // Claude requires at least one message, so we add a system message if there are no messages
182
+ if (msgs.length === 0) {
183
+ if (!system)
184
+ throw new Error("No messages provided");
185
+ return { messages: [{ role: "user", content: system }] };
186
+ }
187
+ return { messages: msgs, system };
161
188
  }
162
- function contentBlockParamFromContent(content) {
189
+ function convertContent(content) {
163
190
  if (typeof content === "string")
164
191
  return content;
165
192
  if (Array.isArray(content)) {
@@ -169,19 +196,20 @@ function contentBlockParamFromContent(content) {
169
196
  }
170
197
  throw new Error("Invalid chat message content");
171
198
  }
172
- function convertTools({ tools, toolChoice, }) {
199
+ function convertTools({ tools, toolChoice, disableParallelToolUse, }) {
173
200
  let choice;
174
201
  if (typeof toolChoice === "object" && "type" in toolChoice && toolChoice.type === "function") {
175
202
  choice = {
176
203
  type: "tool",
177
204
  name: toolChoice.function.name,
205
+ disable_parallel_tool_use: disableParallelToolUse,
178
206
  };
179
207
  }
180
208
  else if (toolChoice === "required") {
181
- choice = { type: "any" };
209
+ choice = { type: "any", disable_parallel_tool_use: disableParallelToolUse };
182
210
  }
183
211
  else if (toolChoice === "auto") {
184
- choice = { type: "auto" };
212
+ choice = { type: "auto", disable_parallel_tool_use: disableParallelToolUse };
185
213
  }
186
214
  else if (toolChoice === "none") {
187
215
  choice = { type: "none" };
@@ -191,7 +219,9 @@ function convertTools({ tools, toolChoice, }) {
191
219
  ? tools.map((i) => ({
192
220
  name: i.function.name,
193
221
  description: i.function.description,
194
- input_schema: i.function.parameters,
222
+ input_schema: (0, lodash_es_1.isEmpty)(i.function.parameters)
223
+ ? { type: "object" }
224
+ : i.function.parameters,
195
225
  }))
196
226
  : undefined,
197
227
  tool_choice: choice,
@@ -1,14 +1,15 @@
1
- import { ChatModel, type ChatModelInput, type ChatModelOutput } from "./chat-model.js";
1
+ import { ChatModel, type ChatModelInput, type ChatModelOptions, type ChatModelOutput } from "./chat-model.js";
2
+ export interface OpenAIChatModelOptions {
3
+ apiKey?: string;
4
+ baseURL?: string;
5
+ model?: string;
6
+ modelOptions?: ChatModelOptions;
7
+ }
2
8
  export declare class OpenAIChatModel extends ChatModel {
3
- config?: {
4
- apiKey?: string;
5
- model?: string;
6
- } | undefined;
7
- constructor(config?: {
8
- apiKey?: string;
9
- model?: string;
10
- } | undefined);
9
+ options?: OpenAIChatModelOptions | undefined;
10
+ constructor(options?: OpenAIChatModelOptions | undefined);
11
11
  private _client?;
12
12
  private get client();
13
+ get modelOptions(): ChatModelOptions | undefined;
13
14
  process(input: ChatModelInput): Promise<ChatModelOutput>;
14
15
  }
@@ -6,32 +6,42 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.OpenAIChatModel = void 0;
7
7
  const nanoid_1 = require("nanoid");
8
8
  const openai_1 = __importDefault(require("openai"));
9
+ const json_schema_js_1 = require("../utils/json-schema.js");
9
10
  const type_utils_js_1 = require("../utils/type-utils.js");
10
11
  const chat_model_js_1 = require("./chat-model.js");
11
12
  const CHAT_MODEL_OPENAI_DEFAULT_MODEL = "gpt-4o-mini";
12
13
  class OpenAIChatModel extends chat_model_js_1.ChatModel {
13
- config;
14
- constructor(config) {
14
+ options;
15
+ constructor(options) {
15
16
  super();
16
- this.config = config;
17
+ this.options = options;
17
18
  }
18
19
  _client;
19
20
  get client() {
20
- if (!this.config?.apiKey)
21
+ if (!this.options?.apiKey)
21
22
  throw new Error("Api Key is required for OpenAIChatModel");
22
- this._client ??= new openai_1.default({ apiKey: this.config.apiKey });
23
+ this._client ??= new openai_1.default({
24
+ baseURL: this.options.baseURL,
25
+ apiKey: this.options.apiKey,
26
+ });
23
27
  return this._client;
24
28
  }
29
+ get modelOptions() {
30
+ return this.options?.modelOptions;
31
+ }
25
32
  async process(input) {
26
33
  const res = await this.client.chat.completions.create({
27
- model: this.config?.model || CHAT_MODEL_OPENAI_DEFAULT_MODEL,
28
- temperature: input.modelOptions?.temperature,
29
- top_p: input.modelOptions?.topP,
30
- frequency_penalty: input.modelOptions?.frequencyPenalty,
31
- presence_penalty: input.modelOptions?.presencePenalty,
34
+ model: this.options?.model || CHAT_MODEL_OPENAI_DEFAULT_MODEL,
35
+ temperature: input.modelOptions?.temperature ?? this.modelOptions?.temperature,
36
+ top_p: input.modelOptions?.topP ?? this.modelOptions?.topP,
37
+ frequency_penalty: input.modelOptions?.frequencyPenalty ?? this.modelOptions?.frequencyPenalty,
38
+ presence_penalty: input.modelOptions?.presencePenalty ?? this.modelOptions?.presencePenalty,
32
39
  messages: await contentsFromInputMessages(input.messages),
33
40
  tools: toolsFromInputTools(input.tools),
34
41
  tool_choice: input.toolChoice,
42
+ parallel_tool_calls: !input.tools?.length
43
+ ? undefined
44
+ : (input.modelOptions?.parallelToolCalls ?? this.modelOptions?.parallelToolCalls),
35
45
  response_format: input.responseFormat?.type === "json_schema"
36
46
  ? {
37
47
  type: "json_schema",
@@ -69,7 +79,7 @@ class OpenAIChatModel extends chat_model_js_1.ChatModel {
69
79
  }
70
80
  const result = {};
71
81
  if (input.responseFormat?.type === "json_schema" && text) {
72
- result.json = JSON.parse(text);
82
+ result.json = (0, json_schema_js_1.parseJSON)(text);
73
83
  }
74
84
  else {
75
85
  result.text = text;
@@ -77,7 +87,7 @@ class OpenAIChatModel extends chat_model_js_1.ChatModel {
77
87
  if (toolCalls.length) {
78
88
  result.toolCalls = toolCalls.map(({ args, ...c }) => ({
79
89
  ...c,
80
- function: { ...c.function, arguments: JSON.parse(args) },
90
+ function: { ...c.function, arguments: (0, json_schema_js_1.parseJSON)(args) },
81
91
  }));
82
92
  }
83
93
  return result;
@@ -0,0 +1,12 @@
1
+ import type { ChatModelOptions } from "./chat-model.js";
2
+ import { OpenAIChatModel } from "./openai-chat-model.js";
3
+ export interface XAIChatModelOptions {
4
+ apiKey?: string;
5
+ model?: string;
6
+ modelOptions?: ChatModelOptions;
7
+ baseURL?: string;
8
+ }
9
+ export declare class XAIChatModel extends OpenAIChatModel {
10
+ options?: XAIChatModelOptions | undefined;
11
+ constructor(options?: XAIChatModelOptions | undefined);
12
+ }
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.XAIChatModel = void 0;
4
+ const openai_chat_model_js_1 = require("./openai-chat-model.js");
5
+ const XAI_DEFAULT_CHAT_MODEL = "grok-2-latest";
6
+ const XAI_BASE_URL = "https://api.x.ai/v1";
7
+ class XAIChatModel extends openai_chat_model_js_1.OpenAIChatModel {
8
+ options;
9
+ constructor(options) {
10
+ super({
11
+ ...options,
12
+ model: options?.model || XAI_DEFAULT_CHAT_MODEL,
13
+ baseURL: options?.baseURL || XAI_BASE_URL,
14
+ });
15
+ this.options = options;
16
+ }
17
+ }
18
+ exports.XAIChatModel = XAIChatModel;
@@ -0,0 +1 @@
1
+ {"type": "commonjs"}
@@ -1,5 +1,5 @@
1
1
  import type { GetPromptResult } from "@modelcontextprotocol/sdk/types.js";
2
- import { Agent, type AgentOptions, type Message } from "../agents/agent.js";
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
5
  import type { Context } from "../execution-engine/context.js";
@@ -18,7 +18,7 @@ export interface PromptBuilderBuildOptions {
18
18
  agent?: AIAgent;
19
19
  input?: Message;
20
20
  model?: ChatModel;
21
- outputSchema?: AgentOptions["outputSchema"];
21
+ outputSchema?: Agent["outputSchema"];
22
22
  }
23
23
  export declare class PromptBuilder {
24
24
  static from(instructions: string): PromptBuilder;
@@ -1,3 +1,12 @@
1
- import type { ZodType } from "zod";
1
+ import type { ZodType, z } from "zod";
2
2
  import type { Message } from "../agents/agent.js";
3
3
  export declare function outputSchemaToResponseFormatSchema(agentOutput: ZodType<Message>): Record<string, unknown>;
4
+ export declare function parseJSON(json: string): any;
5
+ /**
6
+ * Ensure that the union array has at least 1 item.
7
+ * NOTE: the zod union requires at least 2 items (just type definition, not runtime behavior)
8
+ * so we need to ensure that the union has at least 1 item.
9
+ * @param union - The union array
10
+ * @returns The union array with at least 1 item (but the type is at least 2 items for z.union)
11
+ */
12
+ export declare function ensureZodUnionArray<T extends z.ZodType>(union: T[]): [T, T, ...T[]];
@@ -1,8 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.outputSchemaToResponseFormatSchema = outputSchemaToResponseFormatSchema;
4
+ exports.parseJSON = parseJSON;
5
+ exports.ensureZodUnionArray = ensureZodUnionArray;
4
6
  const lodash_es_1 = require("lodash-es");
5
7
  const zod_to_json_schema_1 = require("zod-to-json-schema");
8
+ const logger_js_1 = require("./logger.js");
6
9
  function outputSchemaToResponseFormatSchema(agentOutput) {
7
10
  return setAdditionPropertiesDeep((0, zod_to_json_schema_1.zodToJsonSchema)(agentOutput), false);
8
11
  }
@@ -21,3 +24,25 @@ function setAdditionPropertiesDeep(schema, additionalProperties) {
21
24
  }
22
25
  return schema;
23
26
  }
27
+ function parseJSON(json) {
28
+ try {
29
+ return JSON.parse(json);
30
+ }
31
+ catch (error) {
32
+ logger_js_1.logger.debug("Failed to parse JSON", { json, error });
33
+ throw new Error(`Failed to parse JSON ${error.message}`);
34
+ }
35
+ }
36
+ /**
37
+ * Ensure that the union array has at least 1 item.
38
+ * NOTE: the zod union requires at least 2 items (just type definition, not runtime behavior)
39
+ * so we need to ensure that the union has at least 1 item.
40
+ * @param union - The union array
41
+ * @returns The union array with at least 1 item (but the type is at least 2 items for z.union)
42
+ */
43
+ function ensureZodUnionArray(union) {
44
+ if (!(union.length >= 1)) {
45
+ throw new Error(`Union must have at least 1 item, but got ${union.length}`);
46
+ }
47
+ return union;
48
+ }
@@ -57,11 +57,16 @@ async function runChatLoopInTerminal(userAgent, { log = console.log.bind(console
57
57
  isLoopExited = true;
58
58
  }
59
59
  async function callAgent(agent, input, options) {
60
- const response = await logger_js_1.logger.spinner(agent.call(options.inputKey && typeof input === "string" ? { [options.inputKey]: input } : input), "🤖");
61
- if (options?.onResponse)
62
- options.onResponse(response);
63
- else
64
- options.log(response);
60
+ try {
61
+ const response = await logger_js_1.logger.spinner(agent.call(options.inputKey && typeof input === "string" ? { [options.inputKey]: input } : input), "🤖");
62
+ if (options?.onResponse)
63
+ options.onResponse(response);
64
+ else
65
+ options.log(response);
66
+ }
67
+ catch (error) {
68
+ options.log(`ERROR: ${error.message}`);
69
+ }
65
70
  }
66
71
  const COMMANDS = {
67
72
  "/exit": () => ({ exit: true }),
@@ -11,8 +11,8 @@ export interface AgentOptions<I extends Message = Message, O extends Message = M
11
11
  publishTopic?: PublishTopic<O>;
12
12
  name?: string;
13
13
  description?: string;
14
- inputSchema?: ZodType<I>;
15
- outputSchema?: ZodType<O>;
14
+ inputSchema?: AgentInputOutputSchema<I>;
15
+ outputSchema?: AgentInputOutputSchema<O>;
16
16
  includeInputInOutput?: boolean;
17
17
  tools?: (Agent | FunctionAgentFn)[];
18
18
  disableLogging?: boolean;
@@ -27,8 +27,10 @@ export declare abstract class Agent<I extends Message = Message, O extends Messa
27
27
  */
28
28
  get topic(): string;
29
29
  readonly description?: string;
30
- readonly inputSchema: ZodType<I>;
31
- readonly outputSchema: ZodType<O>;
30
+ private readonly _inputSchema?;
31
+ private readonly _outputSchema?;
32
+ get inputSchema(): ZodType<I>;
33
+ get outputSchema(): ZodType<O>;
32
34
  readonly includeInputInOutput?: boolean;
33
35
  readonly subscribeTopic?: SubscribeTopic;
34
36
  readonly publishTopic?: PublishTopic<Message>;
@@ -49,6 +51,7 @@ export declare abstract class Agent<I extends Message = Message, O extends Messa
49
51
  abstract process(input: I, context?: Context): Promise<O | TransferAgentOutput>;
50
52
  shutdown(): Promise<void>;
51
53
  }
54
+ export type AgentInputOutputSchema<I extends Message = Message> = ZodType<I> | ((agent: Agent) => ZodType<I>);
52
55
  export interface FunctionAgentOptions<I extends Message = Message, O extends Message = Message> extends AgentOptions<I, O> {
53
56
  fn?: FunctionAgentFn<I, O>;
54
57
  }
@@ -8,7 +8,9 @@ export * from "./execution-engine/index.js";
8
8
  export * from "./models/chat-model.js";
9
9
  export * from "./models/claude-chat-model.js";
10
10
  export * from "./models/openai-chat-model.js";
11
+ export * from "./models/xai-chat-model.js";
11
12
  export * from "./prompt/prompt-builder.js";
12
13
  export * from "./prompt/template.js";
14
+ export * from "./utils/json-schema.js";
13
15
  export * from "./utils/logger.js";
14
16
  export * from "./utils/run-chat-loop.js";