@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
@@ -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;
@@ -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;
@@ -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
  }
@@ -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
+ }
@@ -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[]];
@@ -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,12 +8,12 @@ export class Agent {
8
8
  constructor({ inputSchema, outputSchema, ...options }) {
9
9
  this.name = options.name || this.constructor.name;
10
10
  this.description = options.description;
11
- if ((inputSchema && !(inputSchema instanceof ZodObject)) ||
12
- (outputSchema && !(outputSchema instanceof ZodObject))) {
13
- throw new Error("inputSchema must be a Zod object");
14
- }
15
- this.inputSchema = (inputSchema || z.object({})).passthrough();
16
- this.outputSchema = (outputSchema || z.object({})).passthrough();
11
+ if (inputSchema)
12
+ checkAgentInputOutputSchema(inputSchema);
13
+ if (outputSchema)
14
+ checkAgentInputOutputSchema(outputSchema);
15
+ this._inputSchema = inputSchema;
16
+ this._outputSchema = outputSchema;
17
17
  this.includeInputInOutput = options.includeInputInOutput;
18
18
  this.subscribeTopic = options.subscribeTopic;
19
19
  this.publishTopic = options.publishTopic;
@@ -38,8 +38,20 @@ export class Agent {
38
38
  return `$agent_${this.name}`;
39
39
  }
40
40
  description;
41
- inputSchema;
42
- outputSchema;
41
+ _inputSchema;
42
+ _outputSchema;
43
+ get inputSchema() {
44
+ const s = this._inputSchema;
45
+ const schema = typeof s === "function" ? s(this) : s || z.object({});
46
+ checkAgentInputOutputSchema(schema);
47
+ return schema.passthrough();
48
+ }
49
+ get outputSchema() {
50
+ const s = this._outputSchema;
51
+ const schema = typeof s === "function" ? s(this) : s || z.object({});
52
+ checkAgentInputOutputSchema(schema);
53
+ return schema.passthrough();
54
+ }
43
55
  includeInputInOutput;
44
56
  subscribeTopic;
45
57
  publishTopic;
@@ -94,6 +106,11 @@ export class Agent {
94
106
  this.memory?.detach();
95
107
  }
96
108
  }
109
+ function checkAgentInputOutputSchema(schema) {
110
+ if (!(schema instanceof ZodObject) && typeof schema !== "function") {
111
+ throw new Error("schema must be a zod object or function return a zod object ");
112
+ }
113
+ }
97
114
  export class FunctionAgent extends Agent {
98
115
  static from(options) {
99
116
  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/esm/index.js CHANGED
@@ -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";
@@ -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;
@@ -64,6 +64,7 @@ const chatModelOptionsSchema = z.object({
64
64
  topP: z.number().optional(),
65
65
  frequencyPenalty: z.number().optional(),
66
66
  presencePenalty: z.number().optional(),
67
+ parallelToolCalls: z.boolean().optional().default(true),
67
68
  });
68
69
  const chatModelInputSchema = z.object({
69
70
  messages: 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;
@@ -1,30 +1,38 @@
1
1
  import Anthropic from "@anthropic-ai/sdk";
2
+ import { isEmpty } from "lodash-es";
3
+ import { parseJSON } from "../utils/json-schema.js";
4
+ import { logger } from "../utils/logger.js";
2
5
  import { isNonNullable } from "../utils/type-utils.js";
3
6
  import { ChatModel, } from "./chat-model.js";
4
7
  const CHAT_MODEL_CLAUDE_DEFAULT_MODEL = "claude-3-7-sonnet-latest";
5
8
  export class ClaudeChatModel extends ChatModel {
6
- config;
7
- constructor(config) {
9
+ options;
10
+ constructor(options) {
8
11
  super();
9
- this.config = config;
12
+ this.options = options;
10
13
  }
11
14
  _client;
12
15
  get client() {
13
- if (!this.config?.apiKey)
16
+ if (!this.options?.apiKey)
14
17
  throw new Error("Api Key is required for ClaudeChatModel");
15
- this._client ??= new Anthropic({ apiKey: this.config.apiKey });
18
+ this._client ??= new Anthropic({ apiKey: this.options.apiKey });
16
19
  return this._client;
17
20
  }
21
+ get modelOptions() {
22
+ return this.options?.modelOptions;
23
+ }
18
24
  async process(input) {
19
- const model = this.config?.model || CHAT_MODEL_CLAUDE_DEFAULT_MODEL;
25
+ const model = this.options?.model || CHAT_MODEL_CLAUDE_DEFAULT_MODEL;
26
+ const disableParallelToolUse = input.modelOptions?.parallelToolCalls === false ||
27
+ this.modelOptions?.parallelToolCalls === false;
20
28
  const body = {
21
29
  model,
22
- temperature: input.modelOptions?.temperature,
23
- top_p: input.modelOptions?.topP,
30
+ temperature: input.modelOptions?.temperature ?? this.modelOptions?.temperature,
31
+ top_p: input.modelOptions?.topP ?? this.modelOptions?.topP,
24
32
  // TODO: make dynamic based on model https://docs.anthropic.com/en/docs/about-claude/models/all-models
25
33
  max_tokens: /claude-3-[5|7]/.test(model) ? 8192 : 4096,
26
34
  ...convertMessages(input),
27
- ...convertTools(input),
35
+ ...convertTools({ ...input, disableParallelToolUse }),
28
36
  };
29
37
  const stream = this.client.messages.stream({
30
38
  ...body,
@@ -39,41 +47,53 @@ export class ClaudeChatModel extends ChatModel {
39
47
  return result;
40
48
  }
41
49
  async extractResultFromClaudeStream(stream) {
42
- let text = "";
43
- const toolCalls = [];
44
- for await (const chunk of stream) {
45
- // handle streaming text
46
- if (chunk.type === "content_block_delta" && chunk.delta.type === "text_delta") {
47
- text += chunk.delta.text;
50
+ const logs = [];
51
+ try {
52
+ let text = "";
53
+ const toolCalls = [];
54
+ for await (const chunk of stream) {
55
+ logs.push(JSON.stringify(chunk));
56
+ // handle streaming text
57
+ if (chunk.type === "content_block_delta" && chunk.delta.type === "text_delta") {
58
+ text += chunk.delta.text;
59
+ }
60
+ if (chunk.type === "content_block_start" && chunk.content_block.type === "tool_use") {
61
+ toolCalls[chunk.index] = {
62
+ type: "function",
63
+ id: chunk.content_block.id,
64
+ function: {
65
+ name: chunk.content_block.name,
66
+ arguments: {},
67
+ },
68
+ args: "",
69
+ };
70
+ }
71
+ if (chunk.type === "content_block_delta" && chunk.delta.type === "input_json_delta") {
72
+ const call = toolCalls[chunk.index];
73
+ if (!call)
74
+ throw new Error("Tool call not found");
75
+ call.args += chunk.delta.partial_json;
76
+ }
48
77
  }
49
- if (chunk.type === "content_block_start" && chunk.content_block.type === "tool_use") {
50
- toolCalls[chunk.index] = {
51
- type: "function",
52
- id: chunk.content_block.id,
78
+ const result = { text };
79
+ if (toolCalls.length) {
80
+ result.toolCalls = toolCalls
81
+ .map(({ args, ...c }) => ({
82
+ ...c,
53
83
  function: {
54
- name: chunk.content_block.name,
55
- arguments: {},
84
+ ...c.function,
85
+ // NOTE: claude may return a blank string for empty object (the tool's input schema is a empty object)
86
+ arguments: args.trim() ? parseJSON(args) : {},
56
87
  },
57
- args: "",
58
- };
59
- }
60
- if (chunk.type === "content_block_delta" && chunk.delta.type === "input_json_delta") {
61
- const call = toolCalls[chunk.index];
62
- if (!call)
63
- throw new Error("Tool call not found");
64
- call.args += chunk.delta.partial_json;
88
+ }))
89
+ .filter(isNonNullable);
65
90
  }
91
+ return result;
66
92
  }
67
- const result = { text };
68
- if (toolCalls.length) {
69
- result.toolCalls = toolCalls
70
- .map(({ args, ...c }) => ({
71
- ...c,
72
- function: { ...c.function, arguments: JSON.parse(args) },
73
- }))
74
- .filter(isNonNullable);
93
+ catch (error) {
94
+ logger.debug("Failed to process Claude stream", { error, logs });
95
+ throw error;
75
96
  }
76
- return result;
77
97
  }
78
98
  async requestStructuredOutput(body, responseFormat) {
79
99
  if (responseFormat?.type !== "json_schema") {
@@ -125,7 +145,7 @@ function convertMessages({ messages, responseFormat }) {
125
145
  else if (msg.role === "user") {
126
146
  if (!msg.content)
127
147
  throw new Error("User message must have content");
128
- msgs.push({ role: "user", content: contentBlockParamFromContent(msg.content) });
148
+ msgs.push({ role: "user", content: convertContent(msg.content) });
129
149
  }
130
150
  else if (msg.role === "agent") {
131
151
  if (msg.toolCalls?.length) {
@@ -140,7 +160,7 @@ function convertMessages({ messages, responseFormat }) {
140
160
  });
141
161
  }
142
162
  else if (msg.content) {
143
- msgs.push({ role: "assistant", content: contentBlockParamFromContent(msg.content) });
163
+ msgs.push({ role: "assistant", content: convertContent(msg.content) });
144
164
  }
145
165
  else {
146
166
  throw new Error("Agent message must have content or toolCalls");
@@ -150,9 +170,16 @@ function convertMessages({ messages, responseFormat }) {
150
170
  if (responseFormat?.type === "json_schema") {
151
171
  systemMessages.push(`You should provide a json response with schema: ${JSON.stringify(responseFormat.jsonSchema.schema)}`);
152
172
  }
153
- return { messages: msgs, system: systemMessages.join("\n").trim() || undefined };
173
+ const system = systemMessages.join("\n").trim() || undefined;
174
+ // Claude requires at least one message, so we add a system message if there are no messages
175
+ if (msgs.length === 0) {
176
+ if (!system)
177
+ throw new Error("No messages provided");
178
+ return { messages: [{ role: "user", content: system }] };
179
+ }
180
+ return { messages: msgs, system };
154
181
  }
155
- function contentBlockParamFromContent(content) {
182
+ function convertContent(content) {
156
183
  if (typeof content === "string")
157
184
  return content;
158
185
  if (Array.isArray(content)) {
@@ -162,19 +189,20 @@ function contentBlockParamFromContent(content) {
162
189
  }
163
190
  throw new Error("Invalid chat message content");
164
191
  }
165
- function convertTools({ tools, toolChoice, }) {
192
+ function convertTools({ tools, toolChoice, disableParallelToolUse, }) {
166
193
  let choice;
167
194
  if (typeof toolChoice === "object" && "type" in toolChoice && toolChoice.type === "function") {
168
195
  choice = {
169
196
  type: "tool",
170
197
  name: toolChoice.function.name,
198
+ disable_parallel_tool_use: disableParallelToolUse,
171
199
  };
172
200
  }
173
201
  else if (toolChoice === "required") {
174
- choice = { type: "any" };
202
+ choice = { type: "any", disable_parallel_tool_use: disableParallelToolUse };
175
203
  }
176
204
  else if (toolChoice === "auto") {
177
- choice = { type: "auto" };
205
+ choice = { type: "auto", disable_parallel_tool_use: disableParallelToolUse };
178
206
  }
179
207
  else if (toolChoice === "none") {
180
208
  choice = { type: "none" };
@@ -184,7 +212,9 @@ function convertTools({ tools, toolChoice, }) {
184
212
  ? tools.map((i) => ({
185
213
  name: i.function.name,
186
214
  description: i.function.description,
187
- input_schema: i.function.parameters,
215
+ input_schema: isEmpty(i.function.parameters)
216
+ ? { type: "object" }
217
+ : i.function.parameters,
188
218
  }))
189
219
  : undefined,
190
220
  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
  }
@@ -1,31 +1,41 @@
1
1
  import { nanoid } from "nanoid";
2
2
  import OpenAI from "openai";
3
+ import { parseJSON } from "../utils/json-schema.js";
3
4
  import { isNonNullable } from "../utils/type-utils.js";
4
5
  import { ChatModel, } from "./chat-model.js";
5
6
  const CHAT_MODEL_OPENAI_DEFAULT_MODEL = "gpt-4o-mini";
6
7
  export class OpenAIChatModel extends ChatModel {
7
- config;
8
- constructor(config) {
8
+ options;
9
+ constructor(options) {
9
10
  super();
10
- this.config = config;
11
+ this.options = options;
11
12
  }
12
13
  _client;
13
14
  get client() {
14
- if (!this.config?.apiKey)
15
+ if (!this.options?.apiKey)
15
16
  throw new Error("Api Key is required for OpenAIChatModel");
16
- this._client ??= new OpenAI({ apiKey: this.config.apiKey });
17
+ this._client ??= new OpenAI({
18
+ baseURL: this.options.baseURL,
19
+ apiKey: this.options.apiKey,
20
+ });
17
21
  return this._client;
18
22
  }
23
+ get modelOptions() {
24
+ return this.options?.modelOptions;
25
+ }
19
26
  async process(input) {
20
27
  const res = await this.client.chat.completions.create({
21
- model: this.config?.model || CHAT_MODEL_OPENAI_DEFAULT_MODEL,
22
- temperature: input.modelOptions?.temperature,
23
- top_p: input.modelOptions?.topP,
24
- frequency_penalty: input.modelOptions?.frequencyPenalty,
25
- presence_penalty: input.modelOptions?.presencePenalty,
28
+ model: this.options?.model || CHAT_MODEL_OPENAI_DEFAULT_MODEL,
29
+ temperature: input.modelOptions?.temperature ?? this.modelOptions?.temperature,
30
+ top_p: input.modelOptions?.topP ?? this.modelOptions?.topP,
31
+ frequency_penalty: input.modelOptions?.frequencyPenalty ?? this.modelOptions?.frequencyPenalty,
32
+ presence_penalty: input.modelOptions?.presencePenalty ?? this.modelOptions?.presencePenalty,
26
33
  messages: await contentsFromInputMessages(input.messages),
27
34
  tools: toolsFromInputTools(input.tools),
28
35
  tool_choice: input.toolChoice,
36
+ parallel_tool_calls: !input.tools?.length
37
+ ? undefined
38
+ : (input.modelOptions?.parallelToolCalls ?? this.modelOptions?.parallelToolCalls),
29
39
  response_format: input.responseFormat?.type === "json_schema"
30
40
  ? {
31
41
  type: "json_schema",
@@ -63,7 +73,7 @@ export class OpenAIChatModel extends ChatModel {
63
73
  }
64
74
  const result = {};
65
75
  if (input.responseFormat?.type === "json_schema" && text) {
66
- result.json = JSON.parse(text);
76
+ result.json = parseJSON(text);
67
77
  }
68
78
  else {
69
79
  result.text = text;
@@ -71,7 +81,7 @@ export class OpenAIChatModel extends ChatModel {
71
81
  if (toolCalls.length) {
72
82
  result.toolCalls = toolCalls.map(({ args, ...c }) => ({
73
83
  ...c,
74
- function: { ...c.function, arguments: JSON.parse(args) },
84
+ function: { ...c.function, arguments: parseJSON(args) },
75
85
  }));
76
86
  }
77
87
  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,14 @@
1
+ import { OpenAIChatModel } from "./openai-chat-model.js";
2
+ const XAI_DEFAULT_CHAT_MODEL = "grok-2-latest";
3
+ const XAI_BASE_URL = "https://api.x.ai/v1";
4
+ export class XAIChatModel extends OpenAIChatModel {
5
+ options;
6
+ constructor(options) {
7
+ super({
8
+ ...options,
9
+ model: options?.model || XAI_DEFAULT_CHAT_MODEL,
10
+ baseURL: options?.baseURL || XAI_BASE_URL,
11
+ });
12
+ this.options = options;
13
+ }
14
+ }
@@ -0,0 +1 @@
1
+ {"type": "module"}
@@ -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,5 +1,6 @@
1
1
  import { isObject } from "lodash-es";
2
2
  import { zodToJsonSchema } from "zod-to-json-schema";
3
+ import { logger } from "./logger.js";
3
4
  export function outputSchemaToResponseFormatSchema(agentOutput) {
4
5
  return setAdditionPropertiesDeep(zodToJsonSchema(agentOutput), false);
5
6
  }
@@ -18,3 +19,25 @@ function setAdditionPropertiesDeep(schema, additionalProperties) {
18
19
  }
19
20
  return schema;
20
21
  }
22
+ export function parseJSON(json) {
23
+ try {
24
+ return JSON.parse(json);
25
+ }
26
+ catch (error) {
27
+ logger.debug("Failed to parse JSON", { json, error });
28
+ throw new Error(`Failed to parse JSON ${error.message}`);
29
+ }
30
+ }
31
+ /**
32
+ * Ensure that the union array has at least 1 item.
33
+ * NOTE: the zod union requires at least 2 items (just type definition, not runtime behavior)
34
+ * so we need to ensure that the union has at least 1 item.
35
+ * @param union - The union array
36
+ * @returns The union array with at least 1 item (but the type is at least 2 items for z.union)
37
+ */
38
+ export function ensureZodUnionArray(union) {
39
+ if (!(union.length >= 1)) {
40
+ throw new Error(`Union must have at least 1 item, but got ${union.length}`);
41
+ }
42
+ return union;
43
+ }