@node-llm/core 1.9.0 → 1.11.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 (52) hide show
  1. package/README.md +89 -6
  2. package/dist/agent/Agent.d.ts +191 -0
  3. package/dist/agent/Agent.d.ts.map +1 -0
  4. package/dist/agent/Agent.js +272 -0
  5. package/dist/aliases.d.ts +102 -9
  6. package/dist/aliases.d.ts.map +1 -1
  7. package/dist/aliases.js +102 -9
  8. package/dist/chat/Chat.d.ts +1 -0
  9. package/dist/chat/Chat.d.ts.map +1 -1
  10. package/dist/chat/Chat.js +184 -131
  11. package/dist/chat/ChatOptions.d.ts +2 -0
  12. package/dist/chat/ChatOptions.d.ts.map +1 -1
  13. package/dist/chat/ChatStream.d.ts.map +1 -1
  14. package/dist/chat/ChatStream.js +109 -66
  15. package/dist/chat/Tool.d.ts +43 -2
  16. package/dist/chat/Tool.d.ts.map +1 -1
  17. package/dist/chat/Tool.js +50 -0
  18. package/dist/chat/ToolHandler.d.ts +10 -5
  19. package/dist/chat/ToolHandler.d.ts.map +1 -1
  20. package/dist/chat/ToolHandler.js +10 -2
  21. package/dist/index.d.ts +5 -0
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +3 -0
  24. package/dist/llm.d.ts +8 -1
  25. package/dist/llm.d.ts.map +1 -1
  26. package/dist/llm.js +156 -59
  27. package/dist/middlewares/CostGuardMiddleware.d.ts +24 -0
  28. package/dist/middlewares/CostGuardMiddleware.d.ts.map +1 -0
  29. package/dist/middlewares/CostGuardMiddleware.js +23 -0
  30. package/dist/middlewares/PIIMaskMiddleware.d.ts +23 -0
  31. package/dist/middlewares/PIIMaskMiddleware.d.ts.map +1 -0
  32. package/dist/middlewares/PIIMaskMiddleware.js +41 -0
  33. package/dist/middlewares/UsageLoggerMiddleware.d.ts +22 -0
  34. package/dist/middlewares/UsageLoggerMiddleware.d.ts.map +1 -0
  35. package/dist/middlewares/UsageLoggerMiddleware.js +30 -0
  36. package/dist/middlewares/index.d.ts +4 -0
  37. package/dist/middlewares/index.d.ts.map +1 -0
  38. package/dist/middlewares/index.js +3 -0
  39. package/dist/models/models.json +1458 -448
  40. package/dist/providers/BaseProvider.d.ts +6 -1
  41. package/dist/providers/BaseProvider.d.ts.map +1 -1
  42. package/dist/providers/BaseProvider.js +19 -0
  43. package/dist/providers/openai/OpenAIProvider.d.ts +1 -1
  44. package/dist/providers/openai/OpenAIProvider.d.ts.map +1 -1
  45. package/dist/providers/openai/OpenAIProvider.js +13 -2
  46. package/dist/types/Middleware.d.ts +106 -0
  47. package/dist/types/Middleware.d.ts.map +1 -0
  48. package/dist/types/Middleware.js +1 -0
  49. package/dist/utils/middleware-runner.d.ts +7 -0
  50. package/dist/utils/middleware-runner.d.ts.map +1 -0
  51. package/dist/utils/middleware-runner.js +23 -0
  52. package/package.json +6 -2
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # @node-llm/core
2
2
 
3
3
  <p align="left">
4
- <a href="https://node-llm.eshaiju.com/">
5
- <img src="https://node-llm.eshaiju.com/assets/images/logo.jpg" alt="NodeLLM logo" width="300" />
4
+ <a href="https://nodellm.dev/">
5
+ <img src="https://nodellm.dev/assets/images/logo.jpg" alt="NodeLLM logo" width="300" />
6
6
  </a>
7
7
  </p>
8
8
 
@@ -104,6 +104,89 @@ const llm = createLLM({
104
104
 
105
105
  ---
106
106
 
107
+ ## 🔌 Middleware System
108
+
109
+ NodeLLM 1.9.0 introduces a powerful lifecycle hook system for audit, security, and observability.
110
+
111
+ ```ts
112
+ import { createLLM, PIIMaskMiddleware, UsageLoggerMiddleware } from "@node-llm/core";
113
+
114
+ const llm = createLLM({
115
+ provider: "openai",
116
+ middlewares: [
117
+ new PIIMaskMiddleware(), // Redact emails/phone numbers automatically
118
+ new UsageLoggerMiddleware() // Log structured token usage & costs
119
+ ]
120
+ });
121
+
122
+ // All chats created from this instance inherit these middlewares
123
+ const chat = llm.chat("gpt-4o");
124
+ ```
125
+
126
+ ### Decisive Tool Safety
127
+
128
+ Middlewares can control the engine's recovery strategy during tool failures.
129
+
130
+ ```ts
131
+ const safetyMiddleware = {
132
+ name: "Audit",
133
+ onToolCallError: async (ctx, tool, error) => {
134
+ if (tool.function.name === "delete_user") return "STOP"; // Kill the loop
135
+ return "RETRY"; // Attempt recovery
136
+ }
137
+ };
138
+ ```
139
+
140
+ ---
141
+
142
+ ## 🤖 Agent Class
143
+
144
+ Define reusable, class-configured agents with a declarative DSL:
145
+
146
+ ```ts
147
+ import { Agent, Tool, z } from "@node-llm/core";
148
+
149
+ class LookupOrderTool extends Tool<{ orderId: string }> {
150
+ name = "lookup_order";
151
+ description = "Look up an order by ID";
152
+ schema = z.object({ orderId: z.string() });
153
+
154
+ async execute({ orderId }: { orderId: string }) {
155
+ return { status: "shipped", eta: "Tomorrow" };
156
+ }
157
+ }
158
+
159
+ class SupportAgent extends Agent {
160
+ static model = "gpt-4.1";
161
+ static instructions = "You are a helpful support agent.";
162
+ static tools = [LookupOrderTool];
163
+ static temperature = 0.2;
164
+ }
165
+
166
+ // Use anywhere in your app
167
+ const agent = new SupportAgent();
168
+ const response = await agent.ask("Where is order #123?");
169
+ console.log(response.content);
170
+ ```
171
+
172
+ ### ToolHalt - Early Loop Termination
173
+
174
+ Stop the agentic loop early when a definitive answer is found:
175
+
176
+ ```ts
177
+ class FinalAnswerTool extends Tool<{ answer: string }> {
178
+ name = "final_answer";
179
+ description = "Return the final answer to the user";
180
+ schema = z.object({ answer: z.string() });
181
+
182
+ async execute({ answer }: { answer: string }) {
183
+ return this.halt(answer); // Stops the loop, returns this result
184
+ }
185
+ }
186
+ ```
187
+
188
+ ---
189
+
107
190
  ## 💾 Ecosystem
108
191
 
109
192
  Looking for persistence? use **[@node-llm/orm](https://www.npmjs.com/package/@node-llm/orm)**.
@@ -115,11 +198,11 @@ Looking for persistence? use **[@node-llm/orm](https://www.npmjs.com/package/@no
115
198
 
116
199
  ## 📚 Full Documentation
117
200
 
118
- Visit **[node-llm.eshaiju.com](https://node-llm.eshaiju.com/)** for:
201
+ Visit **[nodellm.dev](https://nodellm.dev/)** for:
119
202
 
120
- - [Deep Dive into Tool Calling](https://node-llm.eshaiju.com/core-features/tools)
121
- - [Multi-modal Vision & Audio Guide](https://node-llm.eshaiju.com/core-features/multimodal)
122
- - [Custom Provider Plugin System](https://node-llm.eshaiju.com/advanced/custom-providers)
203
+ - [Deep Dive into Tool Calling](https://nodellm.dev/core-features/tools)
204
+ - [Multi-modal Vision & Audio Guide](https://nodellm.dev/core-features/multimodal)
205
+ - [Custom Provider Plugin System](https://nodellm.dev/advanced/custom-providers)
123
206
 
124
207
  ---
125
208
 
@@ -0,0 +1,191 @@
1
+ import { z } from "zod";
2
+ import { Chat, AskOptions } from "../chat/Chat.js";
3
+ import { ChatOptions } from "../chat/ChatOptions.js";
4
+ import { ChatResponseString } from "../chat/ChatResponse.js";
5
+ import { ToolResolvable } from "../chat/Tool.js";
6
+ import { ThinkingConfig, ThinkingResult } from "../providers/Provider.js";
7
+ import { Schema } from "../schema/Schema.js";
8
+ import { NodeLLMCore } from "../llm.js";
9
+ /**
10
+ * Configuration options that can be defined as static properties on Agent subclasses.
11
+ */
12
+ export interface AgentConfig {
13
+ /** The model ID to use (e.g., "gpt-4o", "claude-sonnet-4-20250514") */
14
+ model?: string;
15
+ /** The provider to use (e.g., "openai", "anthropic") */
16
+ provider?: string;
17
+ /** System instructions for the agent */
18
+ instructions?: string;
19
+ /** Tools available to the agent */
20
+ tools?: ToolResolvable[];
21
+ /** Temperature for response generation (0.0 - 1.0) */
22
+ temperature?: number;
23
+ /** Extended thinking configuration */
24
+ thinking?: ThinkingConfig;
25
+ /** Output schema for structured responses */
26
+ schema?: z.ZodType | Schema | Record<string, unknown>;
27
+ /** Provider-specific parameters */
28
+ params?: Record<string, unknown>;
29
+ /** Custom headers for requests */
30
+ headers?: Record<string, string>;
31
+ /** Maximum tokens in response */
32
+ maxTokens?: number;
33
+ /** Maximum tool call iterations */
34
+ maxToolCalls?: number;
35
+ /** Assume model exists without validation */
36
+ assumeModelExists?: boolean;
37
+ /** Optional LLM instance to use instead of global NodeLLM */
38
+ llm?: NodeLLMCore;
39
+ }
40
+ /**
41
+ * Base class for creating reusable, class-configured agents.
42
+ *
43
+ * Define your agent configuration using static properties, then instantiate
44
+ * and use it anywhere in your application.
45
+ *
46
+ * @example
47
+ * ```typescript
48
+ * class SupportAgent extends Agent {
49
+ * static model = "gpt-4o";
50
+ * static instructions = "You are a helpful support agent";
51
+ * static tools = [SearchDocs, LookupAccount];
52
+ * static temperature = 0.2;
53
+ * }
54
+ *
55
+ * const agent = new SupportAgent();
56
+ * const response = await agent.ask("How can I reset my password?");
57
+ * ```
58
+ *
59
+ * @example Override configuration per instance:
60
+ * ```typescript
61
+ * const agent = new SupportAgent({ model: "gpt-4o-mini" });
62
+ * ```
63
+ */
64
+ export declare abstract class Agent<S extends Record<string, unknown> = Record<string, unknown>> {
65
+ static model?: string;
66
+ static provider?: string;
67
+ static instructions?: string;
68
+ static tools?: ToolResolvable[];
69
+ static temperature?: number;
70
+ static thinking?: ThinkingConfig;
71
+ static schema?: z.ZodType | Schema | Record<string, unknown>;
72
+ static params?: Record<string, unknown>;
73
+ static headers?: Record<string, string>;
74
+ static maxTokens?: number;
75
+ static maxToolCalls?: number;
76
+ static assumeModelExists?: boolean;
77
+ /**
78
+ * Hook called when the agent starts a new session (ask/stream).
79
+ * @param context - Initial context including messages/options
80
+ */
81
+ static onStart(_context: {
82
+ messages: unknown[];
83
+ }): void | Promise<void>;
84
+ /**
85
+ * Hook called when the agent generates a reasoning trace (thinking).
86
+ * @param thinking - The content of the thinking trace
87
+ * @param result - The full response object containing the thinking
88
+ */
89
+ static onThinking(_thinking: ThinkingResult, _result: ChatResponseString): void | Promise<void>;
90
+ /**
91
+ * Hook called when a tool execution starts.
92
+ * @param toolCall - The tool call object (id, function name, arguments)
93
+ */
94
+ static onToolStart(_toolCall: unknown): void | Promise<void>;
95
+ /**
96
+ * Hook called when a tool execution ends.
97
+ * @param toolCall - The tool call object
98
+ * @param result - The result of the tool execution
99
+ */
100
+ static onToolEnd(_toolCall: unknown, _result: unknown): void | Promise<void>;
101
+ /**
102
+ * Hook called when a tool execution encounters an error.
103
+ * @param toolCall - The tool call object
104
+ * @param error - The error that occurred
105
+ */
106
+ static onToolError(_toolCall: unknown, _error: Error): void | Promise<void>;
107
+ /**
108
+ * Hook called when the agent completes a response turn.
109
+ * @param result - The final response object
110
+ */
111
+ static onComplete(_result: ChatResponseString): void | Promise<void>;
112
+ /**
113
+ * Run the agent immediately with a prompt.
114
+ * Creates a new instance of the agent, runs the prompt, and disposes it.
115
+ *
116
+ * @example
117
+ * ```typescript
118
+ * const result = await TravelAgent.ask("Find flights to Paris");
119
+ * ```
120
+ */
121
+ static ask(message: string, options?: AskOptions): Promise<ChatResponseString>;
122
+ /**
123
+ * Stream the agent response immediately.
124
+ * Creates a new instance of the agent and streams the response.
125
+ *
126
+ * @example
127
+ * ```typescript
128
+ * for await (const chunk of TravelAgent.stream("Write a poem")) {
129
+ * process.stdout.write(chunk.content);
130
+ * }
131
+ * ```
132
+ */
133
+ static stream(message: string, options?: AskOptions): import("../index.js").Stream<import("../providers/Provider.js").ChatChunk>;
134
+ /** The underlying Chat instance */
135
+ protected readonly chat: Chat<S>;
136
+ /**
137
+ * Create a new agent instance.
138
+ * @param overrides - Optional configuration to override static properties
139
+ */
140
+ constructor(overrides?: Partial<AgentConfig & ChatOptions>);
141
+ /**
142
+ * Send a message to the agent and get a response.
143
+ * @param message - The user message
144
+ * @param options - Optional request options
145
+ */
146
+ ask(message: string, options?: AskOptions): Promise<ChatResponseString>;
147
+ /**
148
+ * Alias for ask()
149
+ */
150
+ say(message: string, options?: AskOptions): Promise<ChatResponseString>;
151
+ /**
152
+ * Stream a response from the agent.
153
+ * @param message - The user message
154
+ * @param options - Optional request options
155
+ */
156
+ stream(message: string, options?: AskOptions): import("../index.js").Stream<import("../providers/Provider.js").ChatChunk>;
157
+ /**
158
+ * Get the conversation history.
159
+ */
160
+ get history(): readonly import("../index.js").Message[];
161
+ /**
162
+ * Get the model ID being used.
163
+ */
164
+ get modelId(): string;
165
+ /**
166
+ * Get aggregate token usage across the conversation.
167
+ */
168
+ get totalUsage(): import("../providers/Provider.js").Usage;
169
+ /**
170
+ * Access the underlying Chat instance for advanced operations.
171
+ */
172
+ get underlyingChat(): Chat<S>;
173
+ }
174
+ /**
175
+ * Helper function to define an agent inline without creating a class.
176
+ *
177
+ * @example
178
+ * ```typescript
179
+ * const SupportAgent = defineAgent({
180
+ * model: "gpt-4o",
181
+ * instructions: "You are a helpful support agent",
182
+ * tools: [SearchDocs, LookupAccount],
183
+ * temperature: 0.2
184
+ * });
185
+ *
186
+ * const agent = new SupportAgent();
187
+ * const response = await agent.ask("Help me!");
188
+ * ```
189
+ */
190
+ export declare function defineAgent<S extends Record<string, unknown> = Record<string, unknown>>(config: AgentConfig): new (overrides?: Partial<AgentConfig & ChatOptions>) => Agent<S>;
191
+ //# sourceMappingURL=Agent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Agent.d.ts","sourceRoot":"","sources":["../../src/agent/Agent.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,272 @@
1
+ import { NodeLLM } from "../llm.js";
2
+ /**
3
+ * Base class for creating reusable, class-configured agents.
4
+ *
5
+ * Define your agent configuration using static properties, then instantiate
6
+ * and use it anywhere in your application.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * class SupportAgent extends Agent {
11
+ * static model = "gpt-4o";
12
+ * static instructions = "You are a helpful support agent";
13
+ * static tools = [SearchDocs, LookupAccount];
14
+ * static temperature = 0.2;
15
+ * }
16
+ *
17
+ * const agent = new SupportAgent();
18
+ * const response = await agent.ask("How can I reset my password?");
19
+ * ```
20
+ *
21
+ * @example Override configuration per instance:
22
+ * ```typescript
23
+ * const agent = new SupportAgent({ model: "gpt-4o-mini" });
24
+ * ```
25
+ */
26
+ export class Agent {
27
+ // Static configuration properties - override these in subclasses
28
+ static model;
29
+ static provider;
30
+ static instructions;
31
+ static tools;
32
+ static temperature;
33
+ static thinking;
34
+ static schema;
35
+ static params;
36
+ static headers;
37
+ static maxTokens;
38
+ static maxToolCalls;
39
+ static assumeModelExists;
40
+ /**
41
+ * Hook called when the agent starts a new session (ask/stream).
42
+ * @param context - Initial context including messages/options
43
+ */
44
+ static onStart(_context) {
45
+ // Override in subclass
46
+ }
47
+ /**
48
+ * Hook called when the agent generates a reasoning trace (thinking).
49
+ * @param thinking - The content of the thinking trace
50
+ * @param result - The full response object containing the thinking
51
+ */
52
+ static onThinking(_thinking, _result) {
53
+ // Override in subclass
54
+ }
55
+ /**
56
+ * Hook called when a tool execution starts.
57
+ * @param toolCall - The tool call object (id, function name, arguments)
58
+ */
59
+ static onToolStart(_toolCall) {
60
+ // Override in subclass
61
+ }
62
+ /**
63
+ * Hook called when a tool execution ends.
64
+ * @param toolCall - The tool call object
65
+ * @param result - The result of the tool execution
66
+ */
67
+ static onToolEnd(_toolCall, _result) {
68
+ // Override in subclass
69
+ }
70
+ /**
71
+ * Hook called when a tool execution encounters an error.
72
+ * @param toolCall - The tool call object
73
+ * @param error - The error that occurred
74
+ */
75
+ static onToolError(_toolCall, _error) {
76
+ // Override in subclass
77
+ }
78
+ /**
79
+ * Hook called when the agent completes a response turn.
80
+ * @param result - The final response object
81
+ */
82
+ static onComplete(_result) {
83
+ // Override in subclass
84
+ }
85
+ // --- Static Execution API ---
86
+ /**
87
+ * Run the agent immediately with a prompt.
88
+ * Creates a new instance of the agent, runs the prompt, and disposes it.
89
+ *
90
+ * @example
91
+ * ```typescript
92
+ * const result = await TravelAgent.ask("Find flights to Paris");
93
+ * ```
94
+ */
95
+ static async ask(message, options) {
96
+ const Ctor = this;
97
+ const agent = new Ctor({});
98
+ return agent.ask(message, options);
99
+ }
100
+ /**
101
+ * Stream the agent response immediately.
102
+ * Creates a new instance of the agent and streams the response.
103
+ *
104
+ * @example
105
+ * ```typescript
106
+ * for await (const chunk of TravelAgent.stream("Write a poem")) {
107
+ * process.stdout.write(chunk.content);
108
+ * }
109
+ * ```
110
+ */
111
+ static stream(message, options) {
112
+ const Ctor = this;
113
+ const agent = new Ctor({});
114
+ return agent.stream(message, options);
115
+ }
116
+ /** The underlying Chat instance */
117
+ chat;
118
+ /**
119
+ * Create a new agent instance.
120
+ * @param overrides - Optional configuration to override static properties
121
+ */
122
+ constructor(overrides = {}) {
123
+ const ctor = this.constructor;
124
+ // Build chat options from static properties + overrides
125
+ const chatOptions = {
126
+ provider: overrides.provider ?? ctor.provider,
127
+ assumeModelExists: overrides.assumeModelExists ?? ctor.assumeModelExists,
128
+ temperature: overrides.temperature ?? ctor.temperature,
129
+ maxTokens: overrides.maxTokens ?? ctor.maxTokens,
130
+ maxToolCalls: overrides.maxToolCalls ?? ctor.maxToolCalls,
131
+ headers: { ...ctor.headers, ...overrides.headers },
132
+ params: { ...ctor.params, ...overrides.params },
133
+ thinking: overrides.thinking ?? ctor.thinking,
134
+ messages: overrides.messages // Allow history injection
135
+ };
136
+ // Determine model
137
+ const model = overrides.model ?? ctor.model;
138
+ if (!model) {
139
+ throw new Error(`[Agent] No model specified. Set static model property or pass model in constructor.`);
140
+ }
141
+ // Use provided LLM instance or fall back to global NodeLLM
142
+ const llm = overrides.llm ?? NodeLLM;
143
+ this.chat = llm.chat(model, chatOptions);
144
+ // Apply instructions
145
+ const instructions = overrides.instructions ?? ctor.instructions;
146
+ if (instructions) {
147
+ this.chat.withInstructions(instructions);
148
+ }
149
+ // Apply tools
150
+ const tools = overrides.tools ?? ctor.tools;
151
+ if (tools && tools.length > 0) {
152
+ this.chat.withTools(tools);
153
+ }
154
+ // Apply schema
155
+ const schema = overrides.schema ?? ctor.schema;
156
+ if (schema) {
157
+ this.chat.withSchema(schema);
158
+ }
159
+ // Wire up global/static telemetry hooks
160
+ // Trigger onStart immediately
161
+ if (ctor.onStart) {
162
+ this.chat.beforeRequest(async (messages) => {
163
+ if (ctor.onStart) {
164
+ await ctor.onStart({ messages });
165
+ }
166
+ });
167
+ }
168
+ if (ctor.onToolStart) {
169
+ this.chat.onToolCallStart(async (toolCall) => {
170
+ await ctor.onToolStart(toolCall);
171
+ });
172
+ }
173
+ if (ctor.onToolEnd) {
174
+ this.chat.onToolCallEnd(async (toolCall, result) => {
175
+ await ctor.onToolEnd(toolCall, result);
176
+ });
177
+ }
178
+ if (ctor.onToolError) {
179
+ this.chat.onToolCallError(async (toolCall, error) => {
180
+ await ctor.onToolError(toolCall, error);
181
+ });
182
+ }
183
+ if (ctor.onComplete || ctor.onThinking) {
184
+ this.chat.onEndMessage(async (msg) => {
185
+ if (msg.thinking && ctor.onThinking) {
186
+ await ctor.onThinking(msg.thinking, msg);
187
+ }
188
+ if (ctor.onComplete) {
189
+ await ctor.onComplete(msg);
190
+ }
191
+ });
192
+ }
193
+ }
194
+ /**
195
+ * Send a message to the agent and get a response.
196
+ * @param message - The user message
197
+ * @param options - Optional request options
198
+ */
199
+ async ask(message, options) {
200
+ return this.chat.ask(message, options);
201
+ }
202
+ /**
203
+ * Alias for ask()
204
+ */
205
+ async say(message, options) {
206
+ return this.ask(message, options);
207
+ }
208
+ /**
209
+ * Stream a response from the agent.
210
+ * @param message - The user message
211
+ * @param options - Optional request options
212
+ */
213
+ stream(message, options) {
214
+ return this.chat.stream(message, options);
215
+ }
216
+ /**
217
+ * Get the conversation history.
218
+ */
219
+ get history() {
220
+ return this.chat.history;
221
+ }
222
+ /**
223
+ * Get the model ID being used.
224
+ */
225
+ get modelId() {
226
+ return this.chat.modelId;
227
+ }
228
+ /**
229
+ * Get aggregate token usage across the conversation.
230
+ */
231
+ get totalUsage() {
232
+ return this.chat.totalUsage;
233
+ }
234
+ /**
235
+ * Access the underlying Chat instance for advanced operations.
236
+ */
237
+ get underlyingChat() {
238
+ return this.chat;
239
+ }
240
+ }
241
+ /**
242
+ * Helper function to define an agent inline without creating a class.
243
+ *
244
+ * @example
245
+ * ```typescript
246
+ * const SupportAgent = defineAgent({
247
+ * model: "gpt-4o",
248
+ * instructions: "You are a helpful support agent",
249
+ * tools: [SearchDocs, LookupAccount],
250
+ * temperature: 0.2
251
+ * });
252
+ *
253
+ * const agent = new SupportAgent();
254
+ * const response = await agent.ask("Help me!");
255
+ * ```
256
+ */
257
+ export function defineAgent(config) {
258
+ return class extends Agent {
259
+ static model = config.model;
260
+ static provider = config.provider;
261
+ static instructions = config.instructions;
262
+ static tools = config.tools;
263
+ static temperature = config.temperature;
264
+ static thinking = config.thinking;
265
+ static schema = config.schema;
266
+ static params = config.params;
267
+ static headers = config.headers;
268
+ static maxTokens = config.maxTokens;
269
+ static maxToolCalls = config.maxToolCalls;
270
+ static assumeModelExists = config.assumeModelExists;
271
+ };
272
+ }