matimo-examples 0.1.0-alpha.7

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.
@@ -0,0 +1,368 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Matimo Decorator Pattern - True AI Agent with TypeScript @tool Decorators
4
+ *
5
+ * The agent receives a prompt, uses OpenAI to decide which tool to use,
6
+ * then executes that tool via Matimo using real TypeScript @tool decorators.
7
+ *
8
+ * Run: npm run agent:decorator
9
+ */
10
+
11
+ import 'dotenv/config';
12
+ import path from 'path';
13
+ import { fileURLToPath } from 'url';
14
+ import { ChatOpenAI } from '@langchain/openai';
15
+ import { MatimoInstance, tool, setGlobalMatimoInstance } from 'matimo';
16
+
17
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
18
+
19
+ /**
20
+ * Decorator Pattern Agent - Uses @tool decorators for automatic execution
21
+ */
22
+ class DecoratorPatternAgent {
23
+ private llm: ChatOpenAI;
24
+
25
+ constructor(
26
+ private matimo: MatimoInstance,
27
+ llm: ChatOpenAI
28
+ ) {
29
+ this.llm = llm;
30
+ }
31
+
32
+ /**
33
+ * Calculator tool - automatically executes via @tool decorator
34
+ * Parameters map to tool parameters in order: operation, a, b
35
+ */
36
+ @tool('calculator')
37
+ async calculate(operation: string, a: number, b: number): Promise<unknown> {
38
+ // Decorator automatically calls: matimo.execute('calculator', { operation, a, b })
39
+ // Method body is not executed - decorator intercepts the call
40
+ return undefined;
41
+ }
42
+
43
+ /**
44
+ * Echo tool - automatically executes via @tool decorator
45
+ * Parameters map to tool parameter: message
46
+ */
47
+ @tool('echo-tool')
48
+ async echo(message: string): Promise<unknown> {
49
+ // Decorator automatically calls: matimo.execute('echo-tool', { message })
50
+ return undefined;
51
+ }
52
+
53
+ /**
54
+ * HTTP client tool - automatically executes via @tool decorator
55
+ * Parameters map to tool parameters: method, url
56
+ */
57
+ @tool('http-client')
58
+ async fetch(method: string, url: string): Promise<unknown> {
59
+ // Decorator automatically calls: matimo.execute('http-client', { method, url })
60
+ return undefined;
61
+ }
62
+
63
+ /**
64
+ * Generate OpenAI function schemas from Matimo tool definitions
65
+ * Single source of truth - schemas come from tool YAML, not duplicated here
66
+ */
67
+ private getToolSchemas() {
68
+ return this.matimo.listTools().map((tool) => ({
69
+ type: 'function',
70
+ function: {
71
+ name: tool.name,
72
+ description: tool.description,
73
+ parameters: {
74
+ type: 'object',
75
+ properties: Object.entries(tool.parameters || {}).reduce(
76
+ (acc, [paramName, param]) => ({
77
+ ...acc,
78
+ [paramName]: {
79
+ type: param.type,
80
+ enum: param.enum,
81
+ description: param.description,
82
+ },
83
+ }),
84
+ {}
85
+ ),
86
+ required: Object.entries(tool.parameters || {})
87
+ .filter(([_, param]) => param.required)
88
+ .map(([paramName]) => paramName),
89
+ },
90
+ },
91
+ }));
92
+ }
93
+
94
+ /**
95
+ * Process a prompt - AI decides which tool to call
96
+ */
97
+ async process(prompt: string): Promise<void> {
98
+ console.info(`\nโ“ Prompt: "${prompt}"`);
99
+
100
+ try {
101
+ // Prepare system message for tool calling
102
+ const systemMessage = `You are an AI assistant with access to tools.
103
+ Based on the user's request, decide which tool to use and extract the EXACT required parameters.
104
+ IMPORTANT: Use exact parameter names and enum values as specified.
105
+ Respond ONLY with valid JSON in this format: {"tool": "<tool_name>", "parameters": {<exact_params>}}`;
106
+
107
+ // Get tool schemas dynamically from Matimo
108
+ const toolSchemas = this.getToolSchemas();
109
+
110
+ // Build detailed tool specifications for the prompt
111
+ const toolSpecifications = toolSchemas
112
+ .map((t) => {
113
+ const paramSpecs = Object.entries(t.function.parameters.properties)
114
+ .map(([paramName, prop]: [string, any]) => {
115
+ let spec = `${paramName} (${prop.type})`;
116
+ if (prop.enum) {
117
+ spec += ` - valid values: [${prop.enum.join(', ')}]`;
118
+ }
119
+ spec += ` - ${prop.description}`;
120
+ return spec;
121
+ })
122
+ .join('; ');
123
+ return `${t.function.name}: ${t.function.description}\n Parameters: ${paramSpecs}`;
124
+ })
125
+ .join('\n\n');
126
+
127
+ // Create a message with proper formatting for function calling
128
+ const messages = [
129
+ {
130
+ type: 'system',
131
+ content: systemMessage,
132
+ },
133
+ {
134
+ type: 'human',
135
+ content: `User request: "${prompt}"\n\nAvailable tools:\n${toolSpecifications}`,
136
+ },
137
+ ];
138
+
139
+ // Call LLM with tool definitions
140
+ const response = await this.llm.invoke(messages as any);
141
+
142
+ // Parse the LLM response
143
+ let toolName: string | null = null;
144
+ let toolParams: Record<string, unknown> | null = null;
145
+
146
+ // Try to extract tool call from response
147
+ const content = response.content;
148
+
149
+ if (typeof content === 'string') {
150
+ // Try to parse as JSON
151
+ try {
152
+ const parsed = JSON.parse(content);
153
+ toolName = parsed.tool || parsed.function?.name;
154
+ toolParams = parsed.parameters || parsed.function?.parameters || parsed;
155
+ } catch {
156
+ // If not JSON, try to extract from text
157
+ const jsonMatch = content.match(/\{[^{}]*"tool"[^{}]*\}/);
158
+ if (jsonMatch) {
159
+ try {
160
+ const parsed = JSON.parse(jsonMatch[0]);
161
+ toolName = parsed.tool;
162
+ toolParams = parsed.parameters;
163
+ } catch {
164
+ // Continue without params
165
+ }
166
+ }
167
+ }
168
+ }
169
+
170
+ // If we found a tool, execute it via decorated method
171
+ if (toolName && toolParams) {
172
+ await this.executeTool(toolName, toolParams);
173
+ } else {
174
+ console.info(`\nโš ๏ธ No tool call detected in response`);
175
+ console.info(
176
+ `Response: ${typeof content === 'string' ? content.substring(0, 200) : content}`
177
+ );
178
+ }
179
+ } catch (error) {
180
+ console.error(`\nโŒ Error: ${error instanceof Error ? error.message : String(error)}`);
181
+ }
182
+ }
183
+
184
+ /**
185
+ * Map of tool names to their decorated methods
186
+ * Built at runtime to discover which tools this agent supports
187
+ */
188
+ private getToolMethodMap(): Map<string, string> {
189
+ const toolMap = new Map<string, string>();
190
+
191
+ // Get all methods decorated with @tool
192
+ // The decorator stores tool name in a metadata property
193
+ for (const [methodName, descriptor] of Object.entries(
194
+ Object.getOwnPropertyDescriptors(Object.getPrototypeOf(this))
195
+ )) {
196
+ if (typeof descriptor.value === 'function') {
197
+ // Check if method has tool metadata (added by decorator)
198
+ const method = descriptor.value as any;
199
+ if (method.__toolName) {
200
+ toolMap.set(method.__toolName, methodName);
201
+ }
202
+ }
203
+ }
204
+
205
+ // Manual mapping as fallback (decorators should set __toolName)
206
+ // This ensures we catch all @tool decorated methods
207
+ toolMap.set('calculator', 'calculate');
208
+ toolMap.set('echo-tool', 'echo');
209
+ toolMap.set('http-client', 'fetch');
210
+
211
+ return toolMap;
212
+ }
213
+
214
+ /**
215
+ * Execute a tool via decorated method
216
+ * Uses reflection to dynamically call the appropriate decorated method
217
+ * This keeps decorators as the source of truth for agent API
218
+ */
219
+ private async executeTool(toolName: string, params: Record<string, unknown>): Promise<void> {
220
+ try {
221
+ // Normalize parameters based on tool
222
+ let normalizedParams = params;
223
+
224
+ // Calculator: Handle "operands" array format by converting to a, b
225
+ if (toolName === 'calculator' && params.operands && Array.isArray(params.operands)) {
226
+ const [a, b] = params.operands as number[];
227
+ normalizedParams = {
228
+ operation: params.operation,
229
+ a,
230
+ b,
231
+ };
232
+ }
233
+
234
+ console.info(`\n๐Ÿ”ง Using tool: ${toolName}`);
235
+ console.info(` Parameters: ${JSON.stringify(normalizedParams)}`);
236
+
237
+ // Find the decorated method for this tool
238
+ const toolMethodMap = this.getToolMethodMap();
239
+ const methodName = toolMethodMap.get(toolName);
240
+
241
+ if (!methodName) {
242
+ console.info(`\nโŒ Tool '${toolName}' not in agent's API`);
243
+ console.info(`Available tools: ${Array.from(toolMethodMap.keys()).join(', ')}`);
244
+ return;
245
+ }
246
+
247
+ // Dynamically call the decorated method
248
+ // The decorator intercepts the call and executes the tool
249
+ const method = (this as any)[methodName];
250
+ if (typeof method !== 'function') {
251
+ console.info(`\nโŒ Method '${methodName}' for tool '${toolName}' not found`);
252
+ return;
253
+ }
254
+
255
+ // Convert params object to positional args matching method signature
256
+ const args = this.getMethodArgsFromParams(toolName, normalizedParams);
257
+ const result = await method.apply(this, args);
258
+
259
+ // Format result
260
+ if (typeof result === 'object' && result !== null) {
261
+ const resultData = result as any;
262
+ if (resultData.stdout) {
263
+ try {
264
+ const parsed = JSON.parse(resultData.stdout);
265
+ console.info(`\nโœ… Result:`, parsed);
266
+ } catch {
267
+ console.info(`\nโœ… Result:`, resultData.stdout);
268
+ }
269
+ } else if (resultData.data) {
270
+ // HTTP response
271
+ console.info(
272
+ `\nโœ… Result (HTTP ${resultData.statusCode}):`,
273
+ typeof resultData.data === 'string'
274
+ ? resultData.data.substring(0, 200)
275
+ : JSON.stringify(resultData.data).substring(0, 200)
276
+ );
277
+ } else {
278
+ console.info(`\nโœ… Result:`, result);
279
+ }
280
+ }
281
+ } catch (error) {
282
+ console.error(`\nโŒ Tool Error: ${error instanceof Error ? error.message : String(error)}`);
283
+ }
284
+ }
285
+
286
+ /**
287
+ * Convert parameter object to positional arguments for method call
288
+ * Maps normalized params to the decorated method's parameter order
289
+ */
290
+ private getMethodArgsFromParams(toolName: string, params: Record<string, unknown>): unknown[] {
291
+ switch (toolName) {
292
+ case 'calculator':
293
+ return [params.operation, params.a, params.b];
294
+ case 'echo-tool':
295
+ return [params.message];
296
+ case 'http-client':
297
+ return [params.method, params.url];
298
+ default:
299
+ return [];
300
+ }
301
+ }
302
+ }
303
+
304
+ /**
305
+ * Run decorator pattern agent with prompts
306
+ */
307
+ async function runDecoratorPatternAgent() {
308
+ console.info('\nโ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—');
309
+ console.info('โ•‘ Matimo Decorator Pattern - True AI Agent โ•‘');
310
+ console.info('โ•‘ (AI decides which tool to use based on prompt) โ•‘');
311
+ console.info('โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•\n');
312
+
313
+ try {
314
+ const apiKey = process.env.OPENAI_API_KEY;
315
+ if (!apiKey) {
316
+ console.error('โŒ Error: OPENAI_API_KEY not set in .env');
317
+ process.exit(1);
318
+ }
319
+
320
+ // Initialize Matimo with auto-discovery
321
+ console.info('๐Ÿš€ Initializing Matimo...');
322
+ const matimo = await MatimoInstance.init({ autoDiscover: true });
323
+
324
+ // Set global Matimo instance for @tool decorators
325
+ setGlobalMatimoInstance(matimo);
326
+
327
+ const matimoTools = matimo.listTools();
328
+ console.info(`๐Ÿ“ฆ Loaded ${matimoTools.length} tools:\n`);
329
+ matimoTools.forEach((t) => {
330
+ console.info(` โ€ข ${t.name}`);
331
+ console.info(` ${t.description}\n`);
332
+ });
333
+
334
+ // Initialize OpenAI LLM
335
+ console.info('๐Ÿค– Initializing OpenAI LLM (gpt-3.5-turbo)...\n');
336
+ const llm = new ChatOpenAI({
337
+ modelName: 'gpt-4o-mini',
338
+ temperature: 0,
339
+ openAIApiKey: apiKey,
340
+ });
341
+
342
+ // Create agent
343
+ const agent = new DecoratorPatternAgent(matimo, llm);
344
+
345
+ // Test prompts - each should trigger a different tool
346
+ const prompts = [
347
+ '๐Ÿงฎ What is 42 plus 8?',
348
+ '๐Ÿ”Š Echo the message: "Decorator pattern is elegant and powerful!"',
349
+ '๐ŸŒ Fetch the GitHub user profile for octocat using HTTP GET',
350
+ ];
351
+
352
+ console.info('๐Ÿงช Testing AI Agent with 3 Different Prompts');
353
+ console.info('โ•'.repeat(60));
354
+
355
+ for (const prompt of prompts) {
356
+ await agent.process(prompt);
357
+ console.info('\n' + 'โ”€'.repeat(60));
358
+ }
359
+
360
+ console.info('\nโœ… Decorator pattern AI agent test complete!\n');
361
+ } catch (error) {
362
+ console.error('โŒ Agent failed:', error instanceof Error ? error.message : String(error));
363
+ process.exit(1);
364
+ }
365
+ }
366
+
367
+ // Run the agent
368
+ runDecoratorPatternAgent().catch(console.error);
@@ -0,0 +1,253 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Matimo Factory Pattern - True AI Agent with Tool Decision Making
4
+ *
5
+ * The agent receives a prompt, uses OpenAI to decide which tool to use,
6
+ * then executes that tool via Matimo.
7
+ *
8
+ * Run: npm run agent:factory
9
+ */
10
+
11
+ import 'dotenv/config';
12
+ import path from 'path';
13
+ import { fileURLToPath } from 'url';
14
+ import { ChatOpenAI } from '@langchain/openai';
15
+ import { MatimoInstance } from 'matimo';
16
+
17
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
18
+
19
+ /**
20
+ * Factory Pattern Agent - Uses AI to decide which tool to call
21
+ */
22
+ class FactoryPatternAgent {
23
+ private matimo: MatimoInstance;
24
+ private llm: ChatOpenAI;
25
+
26
+ constructor(matimo: MatimoInstance, llm: ChatOpenAI) {
27
+ this.matimo = matimo;
28
+ this.llm = llm;
29
+ }
30
+
31
+ /**
32
+ * Generate OpenAI function schemas from Matimo tool definitions
33
+ * Single source of truth - schemas come from tool YAML, not duplicated here
34
+ */
35
+ private getToolSchemas() {
36
+ return this.matimo.listTools().map((tool) => ({
37
+ type: 'function',
38
+ function: {
39
+ name: tool.name,
40
+ description: tool.description,
41
+ parameters: {
42
+ type: 'object',
43
+ properties: Object.entries(tool.parameters || {}).reduce(
44
+ (acc, [paramName, param]) => ({
45
+ ...acc,
46
+ [paramName]: {
47
+ type: param.type,
48
+ enum: param.enum,
49
+ description: param.description,
50
+ },
51
+ }),
52
+ {}
53
+ ),
54
+ required: Object.entries(tool.parameters || {})
55
+ .filter(([_, param]) => param.required)
56
+ .map(([paramName]) => paramName),
57
+ },
58
+ },
59
+ }));
60
+ }
61
+
62
+ /**
63
+ * Process a prompt - AI decides which tool to use
64
+ */
65
+ async process(prompt: string): Promise<void> {
66
+ console.log(`\nโ“ Prompt: "${prompt}"`);
67
+
68
+ try {
69
+ // Prepare system message for tool calling
70
+ const systemMessage = `You are an AI assistant with access to tools.
71
+ Based on the user's request, decide which tool to use and extract the required parameters.
72
+ Extract the tool name and parameters, then respond with JSON.`;
73
+
74
+ // Get tool schemas dynamically from Matimo
75
+ const toolSchemas = this.getToolSchemas();
76
+
77
+ // Create a message with proper formatting for function calling
78
+ const messages = [
79
+ {
80
+ type: 'system',
81
+ content: systemMessage,
82
+ },
83
+ {
84
+ type: 'human',
85
+ content: `User request: ${prompt}\n\nAvailable tools: ${toolSchemas.map((t) => `${t.function.name}: ${t.function.description}`).join(', ')}\n\nRespond with JSON: {"tool": "<tool_name>", "parameters": {...}}`,
86
+ },
87
+ ];
88
+
89
+ // Call LLM with tool definitions
90
+ const response = await this.llm.invoke(messages as any);
91
+
92
+ // Parse the LLM response
93
+ let toolName: string | null = null;
94
+ let toolParams: Record<string, unknown> | null = null;
95
+
96
+ // Try to extract tool call from response
97
+ const content = response.content;
98
+
99
+ if (typeof content === 'string') {
100
+ // Try to parse as JSON
101
+ try {
102
+ const parsed = JSON.parse(content);
103
+ toolName = parsed.tool || parsed.function?.name;
104
+ toolParams = parsed.parameters || parsed.function?.parameters || parsed;
105
+ } catch {
106
+ // If not JSON, try to extract from text
107
+ const jsonMatch = content.match(/\{[^{}]*"tool"[^{}]*\}/);
108
+ if (jsonMatch) {
109
+ try {
110
+ const parsed = JSON.parse(jsonMatch[0]);
111
+ toolName = parsed.tool;
112
+ toolParams = parsed.parameters;
113
+ } catch {
114
+ // Continue without params
115
+ }
116
+ }
117
+ }
118
+ }
119
+
120
+ // If we found a tool, execute it
121
+ if (toolName && toolParams) {
122
+ await this.executeTool(toolName, toolParams);
123
+ } else {
124
+ console.log(`\nโš ๏ธ No tool call detected in response`);
125
+ console.log(
126
+ `Response: ${typeof content === 'string' ? content.substring(0, 200) : content}`
127
+ );
128
+ }
129
+ } catch (error) {
130
+ console.error(`\nโŒ Error: ${error instanceof Error ? error.message : String(error)}`);
131
+ }
132
+ }
133
+
134
+ /**
135
+ * Execute a tool via Matimo
136
+ * Normalize parameters to match tool schema expectations
137
+ */
138
+ private async executeTool(toolName: string, params: Record<string, unknown>): Promise<void> {
139
+ try {
140
+ const tool = this.matimo.getTool(toolName);
141
+ if (!tool) {
142
+ console.log(`\nโŒ Tool '${toolName}' not found`);
143
+ return;
144
+ }
145
+
146
+ // Normalize parameters based on tool
147
+ let normalizedParams = params;
148
+
149
+ // Calculator: Handle "operands" array format by converting to a, b
150
+ if (toolName === 'calculator' && params.operands && Array.isArray(params.operands)) {
151
+ const [a, b] = params.operands as number[];
152
+ normalizedParams = {
153
+ operation: params.operation,
154
+ a,
155
+ b,
156
+ };
157
+ }
158
+
159
+ console.log(`\n๐Ÿ”ง Using tool: ${toolName}`);
160
+ console.log(` Parameters: ${JSON.stringify(normalizedParams)}`);
161
+
162
+ const result = await this.matimo.execute(toolName, normalizedParams);
163
+
164
+ // Format result
165
+ if (typeof result === 'object' && result !== null) {
166
+ const resultData = result as any;
167
+ if (resultData.stdout) {
168
+ try {
169
+ const parsed = JSON.parse(resultData.stdout);
170
+ console.log(`\nโœ… Result:`, parsed);
171
+ } catch {
172
+ console.log(`\nโœ… Result:`, resultData.stdout);
173
+ }
174
+ } else if (resultData.data) {
175
+ // HTTP response
176
+ console.log(
177
+ `\nโœ… Result (HTTP ${resultData.statusCode}):`,
178
+ typeof resultData.data === 'string'
179
+ ? resultData.data.substring(0, 200)
180
+ : JSON.stringify(resultData.data).substring(0, 200)
181
+ );
182
+ } else {
183
+ console.log(`\nโœ… Result:`, result);
184
+ }
185
+ }
186
+ } catch (error) {
187
+ console.error(`\nโŒ Tool Error: ${error instanceof Error ? error.message : String(error)}`);
188
+ }
189
+ }
190
+ }
191
+
192
+ /**
193
+ * Run factory pattern agent with prompts
194
+ */
195
+ async function runFactoryPatternAgent() {
196
+ console.log('\nโ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—');
197
+ console.log('โ•‘ Matimo Factory Pattern - True AI Agent โ•‘');
198
+ console.log('โ•‘ (AI decides which tool to use based on prompt) โ•‘');
199
+ console.log('โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•\n');
200
+
201
+ try {
202
+ const apiKey = process.env.OPENAI_API_KEY;
203
+ if (!apiKey) {
204
+ console.error('โŒ Error: OPENAI_API_KEY not set in .env');
205
+ process.exit(1);
206
+ }
207
+
208
+ // Initialize Matimo
209
+ console.log('๐Ÿš€ Initializing Matimo...');
210
+ const matimo = await MatimoInstance.init({ autoDiscover: true });
211
+
212
+ const matimoTools = matimo.listTools();
213
+ console.log(`๐Ÿ“ฆ Loaded ${matimoTools.length} tools:\n`);
214
+ matimoTools.forEach((t) => {
215
+ console.log(` โ€ข ${t.name}`);
216
+ console.log(` ${t.description}\n`);
217
+ });
218
+
219
+ // Initialize OpenAI LLM
220
+ console.log('๐Ÿค– Initializing OpenAI LLM (gpt-3.5-turbo)...\n');
221
+ const llm = new ChatOpenAI({
222
+ modelName: 'gpt-4o-mini',
223
+ temperature: 0,
224
+ openAIApiKey: apiKey,
225
+ });
226
+
227
+ // Create agent
228
+ const agent = new FactoryPatternAgent(matimo, llm);
229
+
230
+ // Test prompts - each should trigger a different tool
231
+ const prompts = [
232
+ '๐Ÿงฎ What is 42 plus 8?',
233
+ '๐Ÿ”Š Echo the message: "Factory pattern works perfectly!"',
234
+ '๐ŸŒ Fetch the GitHub user profile for octocat using HTTP GET',
235
+ ];
236
+
237
+ console.log('๐Ÿงช Testing AI Agent with 3 Different Prompts');
238
+ console.log('โ•'.repeat(60));
239
+
240
+ for (const prompt of prompts) {
241
+ await agent.process(prompt);
242
+ console.log('\n' + 'โ”€'.repeat(60));
243
+ }
244
+
245
+ console.log('\nโœ… Factory pattern AI agent test complete!\n');
246
+ } catch (error) {
247
+ console.error('โŒ Agent failed:', error instanceof Error ? error.message : String(error));
248
+ process.exit(1);
249
+ }
250
+ }
251
+
252
+ // Run the agent
253
+ runFactoryPatternAgent().catch(console.error);