@ax-llm/ax 19.0.15 → 19.0.17
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.
- package/index.cjs +462 -313
- package/index.cjs.map +1 -1
- package/index.d.cts +789 -572
- package/index.d.ts +789 -572
- package/index.global.js +409 -260
- package/index.global.js.map +1 -1
- package/index.js +462 -313
- package/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/ax-agent.md +225 -56
- package/skills/ax-ai.md +245 -0
- package/skills/ax-flow.md +402 -0
- package/skills/ax-gen.md +323 -0
- package/skills/ax-gepa.md +244 -0
- package/skills/ax-learn.md +268 -0
- package/skills/ax-llm.md +150 -1675
- package/skills/ax-signature.md +192 -0
package/skills/ax-llm.md
CHANGED
|
@@ -1,1794 +1,264 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ax
|
|
3
3
|
description: This skill helps with using the @ax-llm/ax TypeScript library for building LLM applications. Use when the user asks about ax(), ai(), f(), s(), agent(), flow(), AxGen, AxAgent, AxFlow, signatures, streaming, or mentions @ax-llm/ax.
|
|
4
|
-
version: "19.0.
|
|
4
|
+
version: "19.0.17"
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
# Ax Library (@ax-llm/ax)
|
|
7
|
+
# Ax Library (@ax-llm/ax) Quick Reference
|
|
8
8
|
|
|
9
9
|
Ax is a TypeScript library for building LLM-powered applications with type-safe signatures, streaming support, and multi-provider compatibility.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
> **Detailed skills available:** ax-ai (providers), ax-signature (signatures/types), ax-gen (generators), ax-agent (agents), ax-flow (workflows), ax-gepa (Pareto optimization), ax-learn (self-improving agents).
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
import { ax, ai, f, agent, flow, AxGen, AxAgent, AxFlow } from '@ax-llm/ax';
|
|
15
|
-
|
|
16
|
-
// Create AI provider
|
|
17
|
-
const llm = ai({ name: 'openai', apiKey: process.env.OPENAI_API_KEY });
|
|
18
|
-
|
|
19
|
-
// Create typed generator using f() fluent API
|
|
20
|
-
const gen = ax(
|
|
21
|
-
f()
|
|
22
|
-
.input('question', f.string('User question'))
|
|
23
|
-
.output('answer', f.string('AI response'))
|
|
24
|
-
.build()
|
|
25
|
-
);
|
|
26
|
-
const result = await gen.forward(llm, { question: 'What is 2+2?' });
|
|
27
|
-
// result.answer is typed as string
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
## 1. AI Provider Setup
|
|
31
|
-
|
|
32
|
-
### Quick Setup (All Providers)
|
|
33
|
-
|
|
34
|
-
```typescript
|
|
35
|
-
import { ai } from '@ax-llm/ax';
|
|
36
|
-
|
|
37
|
-
// OpenAI
|
|
38
|
-
const openai = ai({ name: 'openai', apiKey: 'sk-...' });
|
|
39
|
-
|
|
40
|
-
// Anthropic Claude
|
|
41
|
-
const claude = ai({ name: 'anthropic', apiKey: 'sk-ant-...' });
|
|
42
|
-
|
|
43
|
-
// Google Gemini
|
|
44
|
-
const gemini = ai({ name: 'google-gemini', apiKey: 'AIza...' });
|
|
45
|
-
|
|
46
|
-
// Azure OpenAI
|
|
47
|
-
const azure = ai({
|
|
48
|
-
name: 'azure-openai',
|
|
49
|
-
apiKey: 'your-key',
|
|
50
|
-
resourceName: 'your-resource',
|
|
51
|
-
deploymentName: 'gpt-4'
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
// Groq
|
|
55
|
-
const groq = ai({ name: 'groq', apiKey: 'gsk_...' });
|
|
56
|
-
|
|
57
|
-
// DeepSeek
|
|
58
|
-
const deepseek = ai({ name: 'deepseek', apiKey: 'sk-...' });
|
|
59
|
-
|
|
60
|
-
// Mistral
|
|
61
|
-
const mistral = ai({ name: 'mistral', apiKey: 'your-key' });
|
|
62
|
-
|
|
63
|
-
// Cohere
|
|
64
|
-
const cohere = ai({ name: 'cohere', apiKey: 'your-key' });
|
|
65
|
-
|
|
66
|
-
// Together AI
|
|
67
|
-
const together = ai({ name: 'together', apiKey: 'your-key' });
|
|
68
|
-
|
|
69
|
-
// OpenRouter
|
|
70
|
-
const openrouter = ai({ name: 'openrouter', apiKey: 'your-key' });
|
|
71
|
-
|
|
72
|
-
// Ollama (local)
|
|
73
|
-
const ollama = ai({ name: 'ollama', url: 'http://localhost:11434' });
|
|
74
|
-
|
|
75
|
-
// HuggingFace
|
|
76
|
-
const hf = ai({ name: 'huggingface', apiKey: 'hf_...' });
|
|
77
|
-
|
|
78
|
-
// Reka
|
|
79
|
-
const reka = ai({ name: 'reka', apiKey: 'your-key' });
|
|
80
|
-
|
|
81
|
-
// xAI Grok
|
|
82
|
-
const grok = ai({ name: 'grok', apiKey: 'your-key' });
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
### Full Provider Example
|
|
86
|
-
|
|
87
|
-
```typescript
|
|
88
|
-
import { ai, ax, f } from '@ax-llm/ax';
|
|
89
|
-
|
|
90
|
-
// Create provider with options
|
|
91
|
-
const llm = ai({
|
|
92
|
-
name: 'openai',
|
|
93
|
-
apiKey: process.env.OPENAI_API_KEY!,
|
|
94
|
-
config: {
|
|
95
|
-
model: 'gpt-4o',
|
|
96
|
-
temperature: 0.7,
|
|
97
|
-
maxTokens: 1000
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
// Use with generator
|
|
102
|
-
const gen = ax(
|
|
103
|
-
f()
|
|
104
|
-
.input('topic', f.string())
|
|
105
|
-
.output('essay', f.string('A short essay'))
|
|
106
|
-
.build()
|
|
107
|
-
);
|
|
108
|
-
const result = await gen.forward(llm, { topic: 'Climate change' });
|
|
109
|
-
console.log(result.essay);
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
## 2. Signatures & Generators
|
|
113
|
-
|
|
114
|
-
Use the `f()` fluent builder to create type-safe signatures with validation, descriptions, and constraints.
|
|
115
|
-
|
|
116
|
-
### Basic Signature
|
|
117
|
-
|
|
118
|
-
```typescript
|
|
119
|
-
import { ax, f } from '@ax-llm/ax';
|
|
120
|
-
|
|
121
|
-
const gen = ax(
|
|
122
|
-
f()
|
|
123
|
-
.input('question', f.string('User question'))
|
|
124
|
-
.output('answer', f.string('AI response'))
|
|
125
|
-
.build()
|
|
126
|
-
);
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
### Field Types
|
|
130
|
-
|
|
131
|
-
```typescript
|
|
132
|
-
import { f } from '@ax-llm/ax';
|
|
133
|
-
|
|
134
|
-
// String types
|
|
135
|
-
f.string('description') // Basic string
|
|
136
|
-
f.string().min(10).max(1000) // With length constraints
|
|
137
|
-
f.string().email() // Email validation
|
|
138
|
-
f.string().url() // URL validation
|
|
139
|
-
f.string().regex('^[A-Z]', 'Start with capital') // Pattern
|
|
140
|
-
|
|
141
|
-
// Numbers
|
|
142
|
-
f.number('description')
|
|
143
|
-
f.number().min(0).max(100) // With range
|
|
144
|
-
|
|
145
|
-
// Boolean
|
|
146
|
-
f.boolean('description')
|
|
147
|
-
|
|
148
|
-
// Classification/Enum
|
|
149
|
-
f.class(['option1', 'option2', 'option3'], 'description')
|
|
150
|
-
|
|
151
|
-
// JSON (any structure)
|
|
152
|
-
f.json('description')
|
|
153
|
-
|
|
154
|
-
// Dates and times
|
|
155
|
-
f.date('description')
|
|
156
|
-
f.datetime('description')
|
|
157
|
-
|
|
158
|
-
// Media (input only)
|
|
159
|
-
f.image('description')
|
|
160
|
-
f.audio('description')
|
|
161
|
-
f.file('description')
|
|
162
|
-
|
|
163
|
-
// Code
|
|
164
|
-
f.code('python', 'description')
|
|
165
|
-
|
|
166
|
-
// URL
|
|
167
|
-
f.url('description')
|
|
168
|
-
|
|
169
|
-
// Nested objects
|
|
170
|
-
f.object({
|
|
171
|
-
name: f.string('Person name'),
|
|
172
|
-
age: f.number('Age in years'),
|
|
173
|
-
email: f.string().email()
|
|
174
|
-
}, 'Person details')
|
|
175
|
-
|
|
176
|
-
// Arrays
|
|
177
|
-
f.string('Item description').array('List of items')
|
|
178
|
-
f.object({ id: f.number(), name: f.string() }).array('List of objects')
|
|
179
|
-
|
|
180
|
-
// Modifiers
|
|
181
|
-
f.string().optional() // Optional field
|
|
182
|
-
f.string().internal() // Internal (not shown to LLM)
|
|
183
|
-
f.string().cache() // Enable caching
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
### Signature Examples
|
|
187
|
-
|
|
188
|
-
```typescript
|
|
189
|
-
import { ax, f } from '@ax-llm/ax';
|
|
190
|
-
|
|
191
|
-
// Basic Q&A
|
|
192
|
-
const gen1 = ax(
|
|
193
|
-
f()
|
|
194
|
-
.input('question', f.string())
|
|
195
|
-
.output('answer', f.string())
|
|
196
|
-
.build()
|
|
197
|
-
);
|
|
198
|
-
|
|
199
|
-
// With descriptions
|
|
200
|
-
const gen2 = ax(
|
|
201
|
-
f()
|
|
202
|
-
.input('question', f.string('User question'))
|
|
203
|
-
.output('answer', f.string('AI response'))
|
|
204
|
-
.build()
|
|
205
|
-
);
|
|
206
|
-
|
|
207
|
-
// Optional fields
|
|
208
|
-
const gen3 = ax(
|
|
209
|
-
f()
|
|
210
|
-
.input('query', f.string())
|
|
211
|
-
.input('context', f.string('Background context').optional())
|
|
212
|
-
.output('response', f.string())
|
|
213
|
-
.build()
|
|
214
|
-
);
|
|
215
|
-
|
|
216
|
-
// Arrays
|
|
217
|
-
const gen4 = ax(
|
|
218
|
-
f()
|
|
219
|
-
.input('text', f.string())
|
|
220
|
-
.output('keywords', f.string().array())
|
|
221
|
-
.build()
|
|
222
|
-
);
|
|
223
|
-
|
|
224
|
-
// Classification (enum)
|
|
225
|
-
const gen5 = ax(
|
|
226
|
-
f()
|
|
227
|
-
.input('review', f.string())
|
|
228
|
-
.output('sentiment', f.class(['positive', 'negative', 'neutral']))
|
|
229
|
-
.build()
|
|
230
|
-
);
|
|
231
|
-
|
|
232
|
-
// Multiple outputs
|
|
233
|
-
const gen6 = ax(
|
|
234
|
-
f()
|
|
235
|
-
.input('article', f.string())
|
|
236
|
-
.output('title', f.string())
|
|
237
|
-
.output('summary', f.string())
|
|
238
|
-
.output('tags', f.string().array())
|
|
239
|
-
.build()
|
|
240
|
-
);
|
|
241
|
-
|
|
242
|
-
// Numbers and booleans
|
|
243
|
-
const gen7 = ax(
|
|
244
|
-
f()
|
|
245
|
-
.input('text', f.string())
|
|
246
|
-
.output('wordCount', f.number())
|
|
247
|
-
.output('isQuestion', f.boolean())
|
|
248
|
-
.build()
|
|
249
|
-
);
|
|
250
|
-
|
|
251
|
-
// JSON output
|
|
252
|
-
const gen8 = ax(
|
|
253
|
-
f()
|
|
254
|
-
.input('data', f.string())
|
|
255
|
-
.output('parsed', f.json())
|
|
256
|
-
.build()
|
|
257
|
-
);
|
|
258
|
-
|
|
259
|
-
// Dates
|
|
260
|
-
const gen9 = ax(
|
|
261
|
-
f()
|
|
262
|
-
.input('text', f.string())
|
|
263
|
-
.output('extractedDate', f.date())
|
|
264
|
-
.build()
|
|
265
|
-
);
|
|
266
|
-
|
|
267
|
-
// Code blocks
|
|
268
|
-
const gen10 = ax(
|
|
269
|
-
f()
|
|
270
|
-
.input('task', f.string())
|
|
271
|
-
.output('code', f.code('python'))
|
|
272
|
-
.build()
|
|
273
|
-
);
|
|
274
|
-
|
|
275
|
-
// Signature description
|
|
276
|
-
const gen11 = ax(
|
|
277
|
-
f()
|
|
278
|
-
.input('text', f.string())
|
|
279
|
-
.output('translation', f.string())
|
|
280
|
-
.description('Translate text to French')
|
|
281
|
-
.build()
|
|
282
|
-
);
|
|
283
|
-
|
|
284
|
-
// Nested objects
|
|
285
|
-
const gen12 = ax(
|
|
286
|
-
f()
|
|
287
|
-
.input('document', f.string('Document to analyze'))
|
|
288
|
-
.input('analysisType', f.class(['sentiment', 'entities', 'summary']))
|
|
289
|
-
.output('result', f.object({
|
|
290
|
-
score: f.number().min(0).max(1),
|
|
291
|
-
label: f.string(),
|
|
292
|
-
details: f.string().optional()
|
|
293
|
-
}))
|
|
294
|
-
.output('entities', f.object({
|
|
295
|
-
name: f.string(),
|
|
296
|
-
type: f.class(['person', 'org', 'location'])
|
|
297
|
-
}).array().optional())
|
|
298
|
-
.description('Analyze documents')
|
|
299
|
-
.build()
|
|
300
|
-
);
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
### Complete Generator Example
|
|
304
|
-
|
|
305
|
-
```typescript
|
|
306
|
-
import { ai, ax, f } from '@ax-llm/ax';
|
|
307
|
-
|
|
308
|
-
const llm = ai({ name: 'openai', apiKey: process.env.OPENAI_API_KEY! });
|
|
309
|
-
|
|
310
|
-
// Create generator with options
|
|
311
|
-
const summarizer = ax(
|
|
312
|
-
f()
|
|
313
|
-
.input('article', f.string('Article to summarize'))
|
|
314
|
-
.output('summary', f.string('Concise summary'))
|
|
315
|
-
.output('keyPoints', f.string('Key point').array())
|
|
316
|
-
.build(),
|
|
317
|
-
{
|
|
318
|
-
description: 'Summarize articles and extract key points',
|
|
319
|
-
maxRetries: 3,
|
|
320
|
-
maxSteps: 5
|
|
321
|
-
}
|
|
322
|
-
);
|
|
323
|
-
|
|
324
|
-
// Forward (non-streaming)
|
|
325
|
-
const result = await summarizer.forward(llm, {
|
|
326
|
-
article: 'Long article text here...'
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
console.log(result.summary); // string
|
|
330
|
-
console.log(result.keyPoints); // string[]
|
|
331
|
-
|
|
332
|
-
// With model override
|
|
333
|
-
const result2 = await summarizer.forward(llm, { article: 'text' }, {
|
|
334
|
-
model: 'gpt-4o-mini'
|
|
335
|
-
});
|
|
336
|
-
```
|
|
337
|
-
|
|
338
|
-
### String Shorthand
|
|
339
|
-
|
|
340
|
-
For simple cases, you can also use string syntax:
|
|
341
|
-
|
|
342
|
-
```typescript
|
|
343
|
-
import { ax } from '@ax-llm/ax';
|
|
344
|
-
|
|
345
|
-
// String shorthand: 'inputField:type -> outputField:type'
|
|
346
|
-
const gen = ax('question:string -> answer:string');
|
|
347
|
-
|
|
348
|
-
// Types: string, number, boolean, json, class, date, datetime, image, audio, file, code, url
|
|
349
|
-
// Modifiers: field?:type (optional), field:type[] (array), field:class "opt1, opt2" (enum)
|
|
350
|
-
```
|
|
351
|
-
|
|
352
|
-
## 3. Streaming
|
|
353
|
-
|
|
354
|
-
### Basic Streaming
|
|
355
|
-
|
|
356
|
-
```typescript
|
|
357
|
-
import { ai, ax, f } from '@ax-llm/ax';
|
|
358
|
-
|
|
359
|
-
const llm = ai({ name: 'openai', apiKey: process.env.OPENAI_API_KEY! });
|
|
360
|
-
const gen = ax(
|
|
361
|
-
f()
|
|
362
|
-
.input('topic', f.string())
|
|
363
|
-
.output('content', f.string())
|
|
364
|
-
.build()
|
|
365
|
-
);
|
|
366
|
-
|
|
367
|
-
// Stream responses
|
|
368
|
-
for await (const chunk of gen.streamingForward(llm, { topic: 'AI' })) {
|
|
369
|
-
// chunk.delta contains partial values
|
|
370
|
-
if (chunk.delta.content) {
|
|
371
|
-
process.stdout.write(chunk.delta.content);
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
```
|
|
375
|
-
|
|
376
|
-
### Complete Streaming Example
|
|
377
|
-
|
|
378
|
-
```typescript
|
|
379
|
-
import { ai, ax, f } from '@ax-llm/ax';
|
|
380
|
-
|
|
381
|
-
const llm = ai({ name: 'openai', apiKey: process.env.OPENAI_API_KEY! });
|
|
382
|
-
|
|
383
|
-
const writer = ax(
|
|
384
|
-
f()
|
|
385
|
-
.input('prompt', f.string())
|
|
386
|
-
.output('story', f.string())
|
|
387
|
-
.output('title', f.string())
|
|
388
|
-
.build()
|
|
389
|
-
);
|
|
390
|
-
|
|
391
|
-
async function streamStory() {
|
|
392
|
-
let fullStory = '';
|
|
393
|
-
let title = '';
|
|
394
|
-
|
|
395
|
-
for await (const chunk of writer.streamingForward(
|
|
396
|
-
llm,
|
|
397
|
-
{ prompt: 'Write a short story about a robot' },
|
|
398
|
-
{ stream: true }
|
|
399
|
-
)) {
|
|
400
|
-
// Handle story chunks
|
|
401
|
-
if (chunk.delta.story) {
|
|
402
|
-
process.stdout.write(chunk.delta.story);
|
|
403
|
-
fullStory += chunk.delta.story;
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
// Handle title (usually comes early)
|
|
407
|
-
if (chunk.delta.title) {
|
|
408
|
-
title = chunk.delta.title;
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
console.log('\n\nTitle:', title);
|
|
413
|
-
return { story: fullStory, title };
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
await streamStory();
|
|
417
|
-
```
|
|
418
|
-
|
|
419
|
-
### Streaming with Field Processors
|
|
420
|
-
|
|
421
|
-
```typescript
|
|
422
|
-
import { ai, ax, f } from '@ax-llm/ax';
|
|
423
|
-
|
|
424
|
-
const llm = ai({ name: 'openai', apiKey: process.env.OPENAI_API_KEY! });
|
|
425
|
-
|
|
426
|
-
const gen = ax(
|
|
427
|
-
f()
|
|
428
|
-
.input('query', f.string())
|
|
429
|
-
.output('response', f.string())
|
|
430
|
-
.build()
|
|
431
|
-
);
|
|
432
|
-
|
|
433
|
-
// Add streaming field processor
|
|
434
|
-
gen.addStreamingFieldProcessor('response', (chunk, context) => {
|
|
435
|
-
console.log('Chunk received:', chunk);
|
|
436
|
-
console.log('Full value so far:', context?.values?.response);
|
|
437
|
-
console.log('Done:', context?.done);
|
|
438
|
-
});
|
|
439
|
-
|
|
440
|
-
await gen.forward(llm, { query: 'Hello' }, { stream: true });
|
|
441
|
-
```
|
|
442
|
-
|
|
443
|
-
## 4. Agents with Tools
|
|
444
|
-
|
|
445
|
-
Agents can use functions (tools) to perform actions.
|
|
446
|
-
|
|
447
|
-
### Defining Functions
|
|
448
|
-
|
|
449
|
-
```typescript
|
|
450
|
-
import { ai, agent } from '@ax-llm/ax';
|
|
451
|
-
|
|
452
|
-
// Function definition
|
|
453
|
-
const getCurrentWeather = {
|
|
454
|
-
name: 'getCurrentWeather',
|
|
455
|
-
description: 'Get the current weather for a location',
|
|
456
|
-
parameters: {
|
|
457
|
-
type: 'object',
|
|
458
|
-
properties: {
|
|
459
|
-
location: { type: 'string', description: 'City name' },
|
|
460
|
-
unit: { type: 'string', enum: ['celsius', 'fahrenheit'] }
|
|
461
|
-
},
|
|
462
|
-
required: ['location']
|
|
463
|
-
},
|
|
464
|
-
func: async ({ location, unit = 'celsius' }) => {
|
|
465
|
-
// Implementation
|
|
466
|
-
return JSON.stringify({ temp: 22, unit, location });
|
|
467
|
-
}
|
|
468
|
-
};
|
|
469
|
-
```
|
|
470
|
-
|
|
471
|
-
### Creating Agents
|
|
472
|
-
|
|
473
|
-
```typescript
|
|
474
|
-
import { ai, agent, f } from '@ax-llm/ax';
|
|
475
|
-
|
|
476
|
-
const llm = ai({ name: 'openai', apiKey: process.env.OPENAI_API_KEY! });
|
|
477
|
-
|
|
478
|
-
// Create agent with functions
|
|
479
|
-
const weatherAgent = agent(
|
|
480
|
-
f()
|
|
481
|
-
.input('query', f.string())
|
|
482
|
-
.output('response', f.string())
|
|
483
|
-
.build(),
|
|
484
|
-
{
|
|
485
|
-
name: 'weatherAssistant',
|
|
486
|
-
description: 'An assistant that helps with weather queries',
|
|
487
|
-
definition: 'You are a helpful weather assistant. Use the getCurrentWeather function to get weather data and provide friendly responses.',
|
|
488
|
-
functions: [getCurrentWeather]
|
|
489
|
-
}
|
|
490
|
-
);
|
|
491
|
-
|
|
492
|
-
const result = await weatherAgent.forward(llm, {
|
|
493
|
-
query: 'What is the weather in Tokyo?'
|
|
494
|
-
});
|
|
495
|
-
|
|
496
|
-
console.log(result.response);
|
|
497
|
-
```
|
|
498
|
-
|
|
499
|
-
### Complete Agent Example
|
|
500
|
-
|
|
501
|
-
```typescript
|
|
502
|
-
import { ai, agent, f } from '@ax-llm/ax';
|
|
503
|
-
|
|
504
|
-
// Define tools
|
|
505
|
-
const searchDatabase = {
|
|
506
|
-
name: 'searchDatabase',
|
|
507
|
-
description: 'Search the product database',
|
|
508
|
-
parameters: {
|
|
509
|
-
type: 'object',
|
|
510
|
-
properties: {
|
|
511
|
-
query: { type: 'string', description: 'Search query' },
|
|
512
|
-
limit: { type: 'number', description: 'Max results' }
|
|
513
|
-
},
|
|
514
|
-
required: ['query']
|
|
515
|
-
},
|
|
516
|
-
func: async ({ query, limit = 5 }) => {
|
|
517
|
-
// Simulate database search
|
|
518
|
-
return JSON.stringify([
|
|
519
|
-
{ id: 1, name: 'Product A', price: 99 },
|
|
520
|
-
{ id: 2, name: 'Product B', price: 149 }
|
|
521
|
-
].slice(0, limit));
|
|
522
|
-
}
|
|
523
|
-
};
|
|
524
|
-
|
|
525
|
-
const getProductDetails = {
|
|
526
|
-
name: 'getProductDetails',
|
|
527
|
-
description: 'Get details of a specific product',
|
|
528
|
-
parameters: {
|
|
529
|
-
type: 'object',
|
|
530
|
-
properties: {
|
|
531
|
-
productId: { type: 'number', description: 'Product ID' }
|
|
532
|
-
},
|
|
533
|
-
required: ['productId']
|
|
534
|
-
},
|
|
535
|
-
func: async ({ productId }) => {
|
|
536
|
-
return JSON.stringify({
|
|
537
|
-
id: productId,
|
|
538
|
-
name: 'Product A',
|
|
539
|
-
price: 99,
|
|
540
|
-
description: 'A great product',
|
|
541
|
-
stock: 50
|
|
542
|
-
});
|
|
543
|
-
}
|
|
544
|
-
};
|
|
545
|
-
|
|
546
|
-
// Create agent
|
|
547
|
-
const shopAssistant = agent(
|
|
548
|
-
f()
|
|
549
|
-
.input('userQuery', f.string())
|
|
550
|
-
.output('response', f.string())
|
|
551
|
-
.output('recommendations', f.string().array())
|
|
552
|
-
.build(),
|
|
553
|
-
{
|
|
554
|
-
name: 'shoppingAssistant',
|
|
555
|
-
description: 'An AI assistant that helps users find and learn about products',
|
|
556
|
-
definition: `You are a helpful shopping assistant. Use the available tools to:
|
|
557
|
-
1. Search for products when users ask about items
|
|
558
|
-
2. Get product details when they want more information
|
|
559
|
-
3. Provide helpful recommendations based on their needs
|
|
560
|
-
|
|
561
|
-
Always be friendly and provide clear, helpful responses.`,
|
|
562
|
-
functions: [searchDatabase, getProductDetails]
|
|
563
|
-
}
|
|
564
|
-
);
|
|
565
|
-
|
|
566
|
-
const llm = ai({ name: 'openai', apiKey: process.env.OPENAI_API_KEY! });
|
|
567
|
-
|
|
568
|
-
const result = await shopAssistant.forward(llm, {
|
|
569
|
-
userQuery: 'Can you find me some products and tell me about the first one?'
|
|
570
|
-
});
|
|
571
|
-
|
|
572
|
-
console.log('Response:', result.response);
|
|
573
|
-
console.log('Recommendations:', result.recommendations);
|
|
574
|
-
```
|
|
575
|
-
|
|
576
|
-
### Nested Agents
|
|
577
|
-
|
|
578
|
-
```typescript
|
|
579
|
-
import { ai, agent, f } from '@ax-llm/ax';
|
|
580
|
-
|
|
581
|
-
// Child agent
|
|
582
|
-
const researcher = agent(
|
|
583
|
-
f()
|
|
584
|
-
.input('topic', f.string())
|
|
585
|
-
.output('findings', f.string())
|
|
586
|
-
.build(),
|
|
587
|
-
{
|
|
588
|
-
name: 'researchAgent',
|
|
589
|
-
description: 'Researches topics and provides detailed findings'
|
|
590
|
-
}
|
|
591
|
-
);
|
|
592
|
-
|
|
593
|
-
// Parent agent that can use child agent
|
|
594
|
-
const writer = agent(
|
|
595
|
-
f()
|
|
596
|
-
.input('topic', f.string())
|
|
597
|
-
.output('article', f.string())
|
|
598
|
-
.build(),
|
|
599
|
-
{
|
|
600
|
-
name: 'writerAgent',
|
|
601
|
-
description: 'Writes articles using research from the research agent',
|
|
602
|
-
agents: [researcher]
|
|
603
|
-
}
|
|
604
|
-
);
|
|
605
|
-
|
|
606
|
-
const llm = ai({ name: 'openai', apiKey: process.env.OPENAI_API_KEY! });
|
|
607
|
-
|
|
608
|
-
const result = await writer.forward(llm, {
|
|
609
|
-
topic: 'Benefits of meditation'
|
|
610
|
-
});
|
|
611
|
-
```
|
|
612
|
-
|
|
613
|
-
## 5. Workflows (AxFlow)
|
|
614
|
-
|
|
615
|
-
AxFlow enables building complex, multi-step AI workflows with type safety.
|
|
616
|
-
|
|
617
|
-
### Basic Flow
|
|
618
|
-
|
|
619
|
-
```typescript
|
|
620
|
-
import { ai, flow, f } from '@ax-llm/ax';
|
|
621
|
-
|
|
622
|
-
const llm = ai({ name: 'openai', apiKey: process.env.OPENAI_API_KEY! });
|
|
623
|
-
|
|
624
|
-
const summarizerSig = f()
|
|
625
|
-
.input('text', f.string())
|
|
626
|
-
.output('summary', f.string())
|
|
627
|
-
.build();
|
|
628
|
-
|
|
629
|
-
const translatorSig = f()
|
|
630
|
-
.input('text', f.string())
|
|
631
|
-
.output('translation', f.string())
|
|
632
|
-
.build();
|
|
633
|
-
|
|
634
|
-
const pipeline = flow<{ text: string }, { result: string }>()
|
|
635
|
-
.node('summarizer', summarizerSig)
|
|
636
|
-
.node('translator', translatorSig)
|
|
637
|
-
.execute('summarizer', (state) => ({ text: state.text }))
|
|
638
|
-
.execute('translator', (state) => ({ text: state.summarizerResult.summary }))
|
|
639
|
-
.map((state) => ({ result: state.translatorResult.translation }));
|
|
640
|
-
|
|
641
|
-
const result = await pipeline.forward(llm, { text: 'Long article...' });
|
|
642
|
-
console.log(result.result);
|
|
643
|
-
```
|
|
644
|
-
|
|
645
|
-
### Flow with Branching
|
|
646
|
-
|
|
647
|
-
```typescript
|
|
648
|
-
import { ai, flow, f } from '@ax-llm/ax';
|
|
649
|
-
|
|
650
|
-
const llm = ai({ name: 'openai', apiKey: process.env.OPENAI_API_KEY! });
|
|
651
|
-
|
|
652
|
-
const technicalSig = f()
|
|
653
|
-
.input('query', f.string())
|
|
654
|
-
.output('answer', f.string())
|
|
655
|
-
.build();
|
|
656
|
-
|
|
657
|
-
const creativeSig = f()
|
|
658
|
-
.input('query', f.string())
|
|
659
|
-
.output('answer', f.string())
|
|
660
|
-
.build();
|
|
661
|
-
|
|
662
|
-
const workflow = flow<{ query: string; type: string }, { output: string }>()
|
|
663
|
-
.node('technical', technicalSig)
|
|
664
|
-
.node('creative', creativeSig)
|
|
665
|
-
.branch(
|
|
666
|
-
(state) => state.type === 'technical',
|
|
667
|
-
(branch) => branch.execute('technical', (s) => ({ query: s.query })),
|
|
668
|
-
(branch) => branch.execute('creative', (s) => ({ query: s.query }))
|
|
669
|
-
)
|
|
670
|
-
.map((state) => ({
|
|
671
|
-
output: state.technicalResult?.answer || state.creativeResult?.answer || ''
|
|
672
|
-
}));
|
|
673
|
-
|
|
674
|
-
const result = await workflow.forward(llm, {
|
|
675
|
-
query: 'Explain quantum computing',
|
|
676
|
-
type: 'technical'
|
|
677
|
-
});
|
|
678
|
-
```
|
|
679
|
-
|
|
680
|
-
### Flow with Parallel Execution
|
|
681
|
-
|
|
682
|
-
```typescript
|
|
683
|
-
import { ai, flow, f } from '@ax-llm/ax';
|
|
684
|
-
|
|
685
|
-
const llm = ai({ name: 'openai', apiKey: process.env.OPENAI_API_KEY! });
|
|
686
|
-
|
|
687
|
-
const prosSig = f()
|
|
688
|
-
.input('topic', f.string())
|
|
689
|
-
.output('arguments', f.string())
|
|
690
|
-
.build();
|
|
691
|
-
|
|
692
|
-
const consSig = f()
|
|
693
|
-
.input('topic', f.string())
|
|
694
|
-
.output('arguments', f.string())
|
|
695
|
-
.build();
|
|
696
|
-
|
|
697
|
-
const summarySig = f()
|
|
698
|
-
.input('prosArgs', f.string())
|
|
699
|
-
.input('consArgs', f.string())
|
|
700
|
-
.output('summary', f.string())
|
|
701
|
-
.build();
|
|
702
|
-
|
|
703
|
-
const parallelFlow = flow<{ topic: string }, { combined: string }>()
|
|
704
|
-
.node('pros', prosSig)
|
|
705
|
-
.node('cons', consSig)
|
|
706
|
-
.node('summary', summarySig)
|
|
707
|
-
.parallel([
|
|
708
|
-
{ branch: (b) => b.execute('pros', (s) => ({ topic: s.topic })) },
|
|
709
|
-
{ branch: (b) => b.execute('cons', (s) => ({ topic: s.topic })) }
|
|
710
|
-
])
|
|
711
|
-
.execute('summary', (state) => ({
|
|
712
|
-
prosArgs: state.prosResult.arguments,
|
|
713
|
-
consArgs: state.consResult.arguments
|
|
714
|
-
}))
|
|
715
|
-
.map((state) => ({ combined: state.summaryResult.summary }));
|
|
716
|
-
|
|
717
|
-
const result = await parallelFlow.forward(llm, { topic: 'Remote work' });
|
|
718
|
-
```
|
|
719
|
-
|
|
720
|
-
### Complete Flow Example
|
|
721
|
-
|
|
722
|
-
```typescript
|
|
723
|
-
import { ai, flow, f } from '@ax-llm/ax';
|
|
724
|
-
|
|
725
|
-
// Define nodes with proper signatures
|
|
726
|
-
const researchNode = f()
|
|
727
|
-
.input('topic', f.string())
|
|
728
|
-
.output('research', f.string())
|
|
729
|
-
.output('sources', f.string().array())
|
|
730
|
-
.build();
|
|
731
|
-
|
|
732
|
-
const outlineNode = f()
|
|
733
|
-
.input('research', f.string())
|
|
734
|
-
.input('sources', f.string().array())
|
|
735
|
-
.output('outline', f.string().array())
|
|
736
|
-
.build();
|
|
737
|
-
|
|
738
|
-
const writeNode = f()
|
|
739
|
-
.input('outline', f.string().array())
|
|
740
|
-
.input('research', f.string())
|
|
741
|
-
.output('draft', f.string())
|
|
742
|
-
.build();
|
|
743
|
-
|
|
744
|
-
const editNode = f()
|
|
745
|
-
.input('draft', f.string())
|
|
746
|
-
.output('final', f.string())
|
|
747
|
-
.output('wordCount', f.number())
|
|
748
|
-
.build();
|
|
749
|
-
|
|
750
|
-
// Build the flow
|
|
751
|
-
const articlePipeline = flow<
|
|
752
|
-
{ topic: string },
|
|
753
|
-
{ article: string; wordCount: number }
|
|
754
|
-
>()
|
|
755
|
-
.node('research', researchNode)
|
|
756
|
-
.node('outline', outlineNode)
|
|
757
|
-
.node('write', writeNode)
|
|
758
|
-
.node('edit', editNode)
|
|
759
|
-
.execute('research', (s) => ({ topic: s.topic }))
|
|
760
|
-
.execute('outline', (s) => ({
|
|
761
|
-
research: s.researchResult.research,
|
|
762
|
-
sources: s.researchResult.sources
|
|
763
|
-
}))
|
|
764
|
-
.execute('write', (s) => ({
|
|
765
|
-
outline: s.outlineResult.outline,
|
|
766
|
-
research: s.researchResult.research
|
|
767
|
-
}))
|
|
768
|
-
.execute('edit', (s) => ({
|
|
769
|
-
draft: s.writeResult.draft
|
|
770
|
-
}))
|
|
771
|
-
.map((s) => ({
|
|
772
|
-
article: s.editResult.final,
|
|
773
|
-
wordCount: s.editResult.wordCount
|
|
774
|
-
}));
|
|
775
|
-
|
|
776
|
-
const llm = ai({ name: 'openai', apiKey: process.env.OPENAI_API_KEY! });
|
|
777
|
-
|
|
778
|
-
const result = await articlePipeline.forward(llm, {
|
|
779
|
-
topic: 'The future of renewable energy'
|
|
780
|
-
});
|
|
781
|
-
|
|
782
|
-
console.log('Article:', result.article);
|
|
783
|
-
console.log('Word count:', result.wordCount);
|
|
784
|
-
```
|
|
785
|
-
|
|
786
|
-
## 6. Common Patterns
|
|
787
|
-
|
|
788
|
-
### Classification
|
|
789
|
-
|
|
790
|
-
```typescript
|
|
791
|
-
import { ai, ax, f } from '@ax-llm/ax';
|
|
792
|
-
|
|
793
|
-
const classifier = ax(
|
|
794
|
-
f()
|
|
795
|
-
.input('text', f.string())
|
|
796
|
-
.output('category', f.class(['spam', 'ham', 'uncertain']))
|
|
797
|
-
.output('confidence', f.number().min(0).max(1))
|
|
798
|
-
.build()
|
|
799
|
-
);
|
|
800
|
-
|
|
801
|
-
const llm = ai({ name: 'openai', apiKey: process.env.OPENAI_API_KEY! });
|
|
802
|
-
|
|
803
|
-
const result = await classifier.forward(llm, {
|
|
804
|
-
text: 'Congratulations! You won $1,000,000!'
|
|
805
|
-
});
|
|
806
|
-
|
|
807
|
-
console.log(result.category); // 'spam'
|
|
808
|
-
console.log(result.confidence); // 0.95
|
|
809
|
-
```
|
|
810
|
-
|
|
811
|
-
### Extraction
|
|
812
|
-
|
|
813
|
-
```typescript
|
|
814
|
-
import { ai, ax, f } from '@ax-llm/ax';
|
|
815
|
-
|
|
816
|
-
const extractor = ax(
|
|
817
|
-
f()
|
|
818
|
-
.input('text', f.string())
|
|
819
|
-
.output('entities', f.object({
|
|
820
|
-
people: f.string().array(),
|
|
821
|
-
organizations: f.string().array(),
|
|
822
|
-
locations: f.string().array()
|
|
823
|
-
}))
|
|
824
|
-
.build()
|
|
825
|
-
);
|
|
826
|
-
|
|
827
|
-
const llm = ai({ name: 'openai', apiKey: process.env.OPENAI_API_KEY! });
|
|
828
|
-
|
|
829
|
-
const result = await extractor.forward(llm, {
|
|
830
|
-
text: 'Tim Cook announced that Apple will open a new store in Paris on January 15th.'
|
|
831
|
-
});
|
|
832
|
-
|
|
833
|
-
console.log(result.entities.people); // ['Tim Cook']
|
|
834
|
-
console.log(result.entities.organizations); // ['Apple']
|
|
835
|
-
console.log(result.entities.locations); // ['Paris']
|
|
836
|
-
```
|
|
837
|
-
|
|
838
|
-
### Multi-modal (Images)
|
|
839
|
-
|
|
840
|
-
```typescript
|
|
841
|
-
import { ai, ax, f } from '@ax-llm/ax';
|
|
842
|
-
import { readFileSync } from 'fs';
|
|
843
|
-
|
|
844
|
-
const imageAnalyzer = ax(
|
|
845
|
-
f()
|
|
846
|
-
.input('image', f.image('Image to analyze'))
|
|
847
|
-
.input('question', f.string('Question about the image').optional())
|
|
848
|
-
.output('description', f.string('Image description'))
|
|
849
|
-
.output('objects', f.string().array())
|
|
850
|
-
.build()
|
|
851
|
-
);
|
|
852
|
-
|
|
853
|
-
const llm = ai({ name: 'openai', apiKey: process.env.OPENAI_API_KEY! });
|
|
854
|
-
|
|
855
|
-
// From file
|
|
856
|
-
const imageData = readFileSync('./photo.jpg').toString('base64');
|
|
857
|
-
const result = await imageAnalyzer.forward(llm, {
|
|
858
|
-
image: { mimeType: 'image/jpeg', data: imageData },
|
|
859
|
-
question: 'What objects are in this image?'
|
|
860
|
-
});
|
|
861
|
-
|
|
862
|
-
// From URL (for providers that support it)
|
|
863
|
-
const result2 = await imageAnalyzer.forward(llm, {
|
|
864
|
-
image: { mimeType: 'image/jpeg', url: 'https://example.com/image.jpg' }
|
|
865
|
-
});
|
|
866
|
-
```
|
|
867
|
-
|
|
868
|
-
### Chaining Generators
|
|
869
|
-
|
|
870
|
-
```typescript
|
|
871
|
-
import { ai, ax, f } from '@ax-llm/ax';
|
|
872
|
-
|
|
873
|
-
const llm = ai({ name: 'openai', apiKey: process.env.OPENAI_API_KEY! });
|
|
874
|
-
|
|
875
|
-
// Define generators
|
|
876
|
-
const researcher = ax(
|
|
877
|
-
f()
|
|
878
|
-
.input('topic', f.string())
|
|
879
|
-
.output('research', f.string())
|
|
880
|
-
.output('keyFacts', f.string().array())
|
|
881
|
-
.build()
|
|
882
|
-
);
|
|
883
|
-
|
|
884
|
-
const writer = ax(
|
|
885
|
-
f()
|
|
886
|
-
.input('research', f.string())
|
|
887
|
-
.input('keyFacts', f.string().array())
|
|
888
|
-
.output('article', f.string())
|
|
889
|
-
.build()
|
|
890
|
-
);
|
|
891
|
-
|
|
892
|
-
const editor = ax(
|
|
893
|
-
f()
|
|
894
|
-
.input('article', f.string())
|
|
895
|
-
.output('editedArticle', f.string())
|
|
896
|
-
.output('suggestions', f.string().array())
|
|
897
|
-
.build()
|
|
898
|
-
);
|
|
899
|
-
|
|
900
|
-
// Chain them
|
|
901
|
-
async function createArticle(topic: string) {
|
|
902
|
-
const research = await researcher.forward(llm, { topic });
|
|
903
|
-
|
|
904
|
-
const draft = await writer.forward(llm, {
|
|
905
|
-
research: research.research,
|
|
906
|
-
keyFacts: research.keyFacts
|
|
907
|
-
});
|
|
908
|
-
|
|
909
|
-
const final = await editor.forward(llm, {
|
|
910
|
-
article: draft.article
|
|
911
|
-
});
|
|
912
|
-
|
|
913
|
-
return final;
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
const result = await createArticle('Artificial General Intelligence');
|
|
917
|
-
console.log(result.editedArticle);
|
|
918
|
-
```
|
|
919
|
-
|
|
920
|
-
### Error Handling
|
|
921
|
-
|
|
922
|
-
```typescript
|
|
923
|
-
import { ai, ax, f, AxGenerateError, AxAIServiceError } from '@ax-llm/ax';
|
|
924
|
-
|
|
925
|
-
const gen = ax(
|
|
926
|
-
f()
|
|
927
|
-
.input('input', f.string())
|
|
928
|
-
.output('output', f.string())
|
|
929
|
-
.build()
|
|
930
|
-
);
|
|
931
|
-
const llm = ai({ name: 'openai', apiKey: process.env.OPENAI_API_KEY! });
|
|
932
|
-
|
|
933
|
-
try {
|
|
934
|
-
const result = await gen.forward(llm, { input: 'test' });
|
|
935
|
-
console.log(result.output);
|
|
936
|
-
} catch (error) {
|
|
937
|
-
if (error instanceof AxGenerateError) {
|
|
938
|
-
console.error('Generation failed:', error.message);
|
|
939
|
-
console.error('Details:', error.details);
|
|
940
|
-
} else if (error instanceof AxAIServiceError) {
|
|
941
|
-
console.error('AI service error:', error.message);
|
|
942
|
-
} else {
|
|
943
|
-
throw error;
|
|
944
|
-
}
|
|
945
|
-
}
|
|
946
|
-
```
|
|
947
|
-
|
|
948
|
-
### Examples and Few-Shot Learning
|
|
949
|
-
|
|
950
|
-
```typescript
|
|
951
|
-
import { ai, ax, f } from '@ax-llm/ax';
|
|
952
|
-
|
|
953
|
-
const classifier = ax(
|
|
954
|
-
f()
|
|
955
|
-
.input('text', f.string())
|
|
956
|
-
.output('sentiment', f.class(['positive', 'negative', 'neutral']))
|
|
957
|
-
.build()
|
|
958
|
-
);
|
|
959
|
-
|
|
960
|
-
// Set examples for few-shot learning
|
|
961
|
-
classifier.setExamples([
|
|
962
|
-
{ text: 'I love this product!', sentiment: 'positive' },
|
|
963
|
-
{ text: 'This is terrible.', sentiment: 'negative' },
|
|
964
|
-
{ text: 'It works as expected.', sentiment: 'neutral' }
|
|
965
|
-
]);
|
|
966
|
-
|
|
967
|
-
const llm = ai({ name: 'openai', apiKey: process.env.OPENAI_API_KEY! });
|
|
968
|
-
|
|
969
|
-
const result = await classifier.forward(llm, {
|
|
970
|
-
text: 'The quality exceeded my expectations!'
|
|
971
|
-
});
|
|
972
|
-
```
|
|
973
|
-
|
|
974
|
-
### Assertions and Validation
|
|
975
|
-
|
|
976
|
-
```typescript
|
|
977
|
-
import { ai, ax, f } from '@ax-llm/ax';
|
|
978
|
-
|
|
979
|
-
const gen = ax(
|
|
980
|
-
f()
|
|
981
|
-
.input('number', f.number())
|
|
982
|
-
.output('doubled', f.number())
|
|
983
|
-
.build()
|
|
984
|
-
);
|
|
985
|
-
|
|
986
|
-
// Add assertion
|
|
987
|
-
gen.addAssert(
|
|
988
|
-
(output) => output.doubled === output.number * 2,
|
|
989
|
-
'Output must be double the input'
|
|
990
|
-
);
|
|
991
|
-
|
|
992
|
-
const llm = ai({ name: 'openai', apiKey: process.env.OPENAI_API_KEY! });
|
|
993
|
-
|
|
994
|
-
// This will retry if assertion fails
|
|
995
|
-
const result = await gen.forward(llm, { number: 5 }, { maxRetries: 3 });
|
|
996
|
-
```
|
|
997
|
-
|
|
998
|
-
### Memory and Context
|
|
999
|
-
|
|
1000
|
-
```typescript
|
|
1001
|
-
import { ai, ax, f, AxMemory } from '@ax-llm/ax';
|
|
1002
|
-
|
|
1003
|
-
const chatbot = ax(
|
|
1004
|
-
f()
|
|
1005
|
-
.input('userMessage', f.string())
|
|
1006
|
-
.output('response', f.string())
|
|
1007
|
-
.build()
|
|
1008
|
-
);
|
|
1009
|
-
const llm = ai({ name: 'openai', apiKey: process.env.OPENAI_API_KEY! });
|
|
1010
|
-
|
|
1011
|
-
// Create shared memory
|
|
1012
|
-
const memory = new AxMemory();
|
|
1013
|
-
|
|
1014
|
-
// Conversation with memory
|
|
1015
|
-
await chatbot.forward(llm, { userMessage: 'My name is Alice' }, { mem: memory });
|
|
1016
|
-
const response = await chatbot.forward(llm, { userMessage: 'What is my name?' }, { mem: memory });
|
|
1017
|
-
// response.response will reference "Alice"
|
|
1018
|
-
```
|
|
1019
|
-
|
|
1020
|
-
## 7. Advanced Configuration
|
|
1021
|
-
|
|
1022
|
-
### Model Configuration
|
|
1023
|
-
|
|
1024
|
-
```typescript
|
|
1025
|
-
import { ai, ax, f } from '@ax-llm/ax';
|
|
1026
|
-
|
|
1027
|
-
const llm = ai({ name: 'openai', apiKey: process.env.OPENAI_API_KEY! });
|
|
1028
|
-
const gen = ax(
|
|
1029
|
-
f()
|
|
1030
|
-
.input('input', f.string())
|
|
1031
|
-
.output('output', f.string())
|
|
1032
|
-
.build()
|
|
1033
|
-
);
|
|
1034
|
-
|
|
1035
|
-
const result = await gen.forward(llm, { input: 'test' }, {
|
|
1036
|
-
model: 'gpt-4o',
|
|
1037
|
-
modelConfig: {
|
|
1038
|
-
temperature: 0.7,
|
|
1039
|
-
maxTokens: 2000,
|
|
1040
|
-
topP: 0.9
|
|
1041
|
-
}
|
|
1042
|
-
});
|
|
1043
|
-
```
|
|
1044
|
-
|
|
1045
|
-
### Debugging
|
|
1046
|
-
|
|
1047
|
-
```typescript
|
|
1048
|
-
import { ai, ax, f, axCreateDefaultColorLogger } from '@ax-llm/ax';
|
|
1049
|
-
|
|
1050
|
-
const llm = ai({ name: 'openai', apiKey: process.env.OPENAI_API_KEY! });
|
|
1051
|
-
const gen = ax(
|
|
1052
|
-
f()
|
|
1053
|
-
.input('input', f.string())
|
|
1054
|
-
.output('output', f.string())
|
|
1055
|
-
.build()
|
|
1056
|
-
);
|
|
1057
|
-
|
|
1058
|
-
// Enable debug logging
|
|
1059
|
-
const result = await gen.forward(llm, { input: 'test' }, {
|
|
1060
|
-
debug: true,
|
|
1061
|
-
logger: axCreateDefaultColorLogger()
|
|
1062
|
-
});
|
|
1063
|
-
```
|
|
1064
|
-
|
|
1065
|
-
### Context Caching
|
|
1066
|
-
|
|
1067
|
-
```typescript
|
|
1068
|
-
import { ai, ax, f } from '@ax-llm/ax';
|
|
1069
|
-
|
|
1070
|
-
const gen = ax(
|
|
1071
|
-
f()
|
|
1072
|
-
.input('document', f.string())
|
|
1073
|
-
.input('question', f.string())
|
|
1074
|
-
.output('answer', f.string())
|
|
1075
|
-
.build()
|
|
1076
|
-
);
|
|
1077
|
-
const llm = ai({ name: 'anthropic', apiKey: process.env.ANTHROPIC_API_KEY! });
|
|
1078
|
-
|
|
1079
|
-
// Enable context caching for long documents
|
|
1080
|
-
const result = await gen.forward(llm, {
|
|
1081
|
-
document: longDocument,
|
|
1082
|
-
question: 'What is the main topic?'
|
|
1083
|
-
}, {
|
|
1084
|
-
contextCache: {
|
|
1085
|
-
cacheBreakpoint: 'after-examples'
|
|
1086
|
-
}
|
|
1087
|
-
});
|
|
1088
|
-
```
|
|
1089
|
-
|
|
1090
|
-
## 8. Forward & AI Options
|
|
1091
|
-
|
|
1092
|
-
### Quick Reference Table
|
|
1093
|
-
|
|
1094
|
-
| Goal | Option | Example |
|
|
1095
|
-
|------|--------|---------|
|
|
1096
|
-
| Adjust creativity | `modelConfig.temperature` | `{ modelConfig: { temperature: 0.8 } }` |
|
|
1097
|
-
| Limit response length | `modelConfig.maxTokens` | `{ modelConfig: { maxTokens: 500 } }` |
|
|
1098
|
-
| Use different model | `model` | `{ model: 'gpt-4o-mini' }` |
|
|
1099
|
-
| Enable caching | `contextCache` | `{ contextCache: { cacheBreakpoint: 'after-examples' } }` |
|
|
1100
|
-
| Debug output | `debug` | `{ debug: true }` |
|
|
1101
|
-
| Retry on failure | `maxRetries` | `{ maxRetries: 3 }` |
|
|
1102
|
-
| Multi-sampling | `sampleCount` | `{ sampleCount: 5, resultPicker: bestResultPicker }` |
|
|
1103
|
-
| Thinking models | `thinkingTokenBudget` | `{ thinkingTokenBudget: 10000 }` |
|
|
1104
|
-
| Abort request | `abortSignal` | `{ abortSignal: controller.signal }` |
|
|
1105
|
-
| Custom timeout | `timeout` | `{ timeout: 60000 }` |
|
|
1106
|
-
|
|
1107
|
-
### Execution Control Options
|
|
13
|
+
## Imports & Factories
|
|
1108
14
|
|
|
1109
15
|
```typescript
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
const llm = ai({ name: 'openai', apiKey: process.env.OPENAI_API_KEY! });
|
|
1113
|
-
const gen = ax(
|
|
1114
|
-
f()
|
|
1115
|
-
.input('input', f.string())
|
|
1116
|
-
.output('output', f.string())
|
|
1117
|
-
.build()
|
|
1118
|
-
);
|
|
1119
|
-
|
|
1120
|
-
const result = await gen.forward(llm, { input: 'test' }, {
|
|
1121
|
-
// Retry failed generations (validation failures, API errors)
|
|
1122
|
-
maxRetries: 3,
|
|
16
|
+
// Prefer factory functions: ax(), ai(), agent(), flow() — not new AxGen(), new AxAI(), etc.
|
|
17
|
+
import { ax, ai, f, s, fn, agent, flow, AxMemory, AxMCPClient, AxLearn } from '@ax-llm/ax';
|
|
1123
18
|
|
|
1124
|
-
|
|
1125
|
-
|
|
19
|
+
// AI provider
|
|
20
|
+
const llm = ai({ name: 'openai', apiKey: process.env.OPENAI_APIKEY });
|
|
1126
21
|
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
});
|
|
1130
|
-
```
|
|
1131
|
-
|
|
1132
|
-
### Model Configuration
|
|
1133
|
-
|
|
1134
|
-
```typescript
|
|
1135
|
-
import { ai, ax, f } from '@ax-llm/ax';
|
|
22
|
+
// Generator (from string signature)
|
|
23
|
+
const gen = ax('question:string -> answer:string');
|
|
1136
24
|
|
|
1137
|
-
|
|
25
|
+
// Generator (from fluent signature)
|
|
1138
26
|
const gen = ax(
|
|
1139
27
|
f()
|
|
1140
|
-
.input('
|
|
1141
|
-
.output('
|
|
28
|
+
.input('question', f.string('User question'))
|
|
29
|
+
.output('answer', f.string('AI response'))
|
|
1142
30
|
.build()
|
|
1143
31
|
);
|
|
1144
32
|
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
model: 'gpt-4o-mini',
|
|
1148
|
-
|
|
1149
|
-
// Model-specific configuration
|
|
1150
|
-
modelConfig: {
|
|
1151
|
-
// Sampling temperature (0.0 = deterministic, 2.0 = creative)
|
|
1152
|
-
temperature: 0.7,
|
|
1153
|
-
|
|
1154
|
-
// Maximum tokens in response
|
|
1155
|
-
maxTokens: 2000,
|
|
1156
|
-
|
|
1157
|
-
// Nucleus sampling threshold
|
|
1158
|
-
topP: 0.9,
|
|
1159
|
-
|
|
1160
|
-
// Top-K sampling (not all providers support this)
|
|
1161
|
-
topK: 40,
|
|
1162
|
-
|
|
1163
|
-
// Frequency penalty (-2.0 to 2.0)
|
|
1164
|
-
frequencyPenalty: 0.5,
|
|
1165
|
-
|
|
1166
|
-
// Presence penalty (-2.0 to 2.0)
|
|
1167
|
-
presencePenalty: 0.5,
|
|
1168
|
-
|
|
1169
|
-
// Stop sequences
|
|
1170
|
-
stopSequences: ['\n\n', 'END'],
|
|
33
|
+
// Reusable signature
|
|
34
|
+
const sig = s('question:string, context:string[] -> answer:string');
|
|
1171
35
|
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
responseFormat: 'json_object'
|
|
1177
|
-
}
|
|
36
|
+
// Agent
|
|
37
|
+
const myAgent = agent('userInput:string -> response:string', {
|
|
38
|
+
name: 'helper',
|
|
39
|
+
description: 'A helpful assistant',
|
|
1178
40
|
});
|
|
1179
|
-
```
|
|
1180
41
|
|
|
1181
|
-
|
|
42
|
+
// Flow
|
|
43
|
+
const wf = flow<{ input: string }, { output: string }>()
|
|
44
|
+
.node('step1', 'input:string -> output:string')
|
|
45
|
+
.execute('step1', (state) => ({ input: state.input }))
|
|
46
|
+
.returns((state) => ({ output: state.step1Result.output }));
|
|
47
|
+
|
|
48
|
+
// Function tool
|
|
49
|
+
const tool = fn('search')
|
|
50
|
+
.description('Search the web')
|
|
51
|
+
.arg('query', f.string('Search query'))
|
|
52
|
+
.returns(f.string('Search results'))
|
|
53
|
+
.handler(({ query }) => searchWeb(query))
|
|
54
|
+
.build();
|
|
55
|
+
```
|
|
1182
56
|
|
|
1183
|
-
|
|
57
|
+
## Running
|
|
1184
58
|
|
|
1185
59
|
```typescript
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
const llm = ai({ name: 'anthropic', apiKey: process.env.ANTHROPIC_API_KEY! });
|
|
1189
|
-
const gen = ax(
|
|
1190
|
-
f()
|
|
1191
|
-
.input('document', f.string())
|
|
1192
|
-
.input('question', f.string())
|
|
1193
|
-
.output('answer', f.string())
|
|
1194
|
-
.build()
|
|
1195
|
-
);
|
|
1196
|
-
|
|
1197
|
-
const longDocument = '... very long document ...';
|
|
1198
|
-
|
|
1199
|
-
// Multiple questions about the same document - caching saves cost
|
|
1200
|
-
for (const question of questions) {
|
|
1201
|
-
const result = await gen.forward(llm, { document: longDocument, question }, {
|
|
1202
|
-
contextCache: {
|
|
1203
|
-
// Cache name (required for identifying the cache)
|
|
1204
|
-
name: 'doc-analysis-cache',
|
|
1205
|
-
|
|
1206
|
-
// Where to split the prompt for caching:
|
|
1207
|
-
// - 'system': Cache system prompt only
|
|
1208
|
-
// - 'after-functions': Cache system + function definitions
|
|
1209
|
-
// - 'after-examples': Cache system + functions + examples
|
|
1210
|
-
cacheBreakpoint: 'after-examples',
|
|
1211
|
-
|
|
1212
|
-
// Cache time-to-live in seconds (default: provider-specific)
|
|
1213
|
-
ttlSeconds: 3600,
|
|
1214
|
-
|
|
1215
|
-
// Minimum tokens to trigger caching (avoid caching small prompts)
|
|
1216
|
-
minTokens: 1000,
|
|
1217
|
-
|
|
1218
|
-
// Refresh cache when within this window of expiry
|
|
1219
|
-
refreshWindowSeconds: 300,
|
|
60
|
+
// Forward (blocking)
|
|
61
|
+
const result = await gen.forward(llm, { question: 'What is 2+2?' });
|
|
1220
62
|
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
});
|
|
63
|
+
// Streaming
|
|
64
|
+
for await (const chunk of gen.streamingForward(llm, { question: 'Tell a story' })) {
|
|
65
|
+
if (chunk.delta.answer) process.stdout.write(chunk.delta.answer);
|
|
1225
66
|
}
|
|
1226
67
|
```
|
|
1227
68
|
|
|
1228
|
-
|
|
69
|
+
## Forward Options Quick Reference
|
|
70
|
+
|
|
71
|
+
| Goal | Option | Example |
|
|
72
|
+
|------|--------|---------|
|
|
73
|
+
| Model override | `model` | `{ model: 'gpt-4o-mini' }` |
|
|
74
|
+
| Temperature | `modelConfig.temperature` | `{ modelConfig: { temperature: 0.8 } }` |
|
|
75
|
+
| Max tokens | `modelConfig.maxTokens` | `{ modelConfig: { maxTokens: 500 } }` |
|
|
76
|
+
| Retry on failure | `maxRetries` | `{ maxRetries: 3 }` |
|
|
77
|
+
| Max agent steps | `maxSteps` | `{ maxSteps: 10 }` |
|
|
78
|
+
| Fail fast | `fastFail` | `{ fastFail: true }` |
|
|
79
|
+
| Thinking budget | `thinkingTokenBudget` | `{ thinkingTokenBudget: 'medium' }` |
|
|
80
|
+
| Show thoughts | `showThoughts` | `{ showThoughts: true }` |
|
|
81
|
+
| Context caching | `contextCache` | `{ contextCache: { cacheBreakpoint: 'after-examples' } }` |
|
|
82
|
+
| Multi-sampling | `sampleCount` | `{ sampleCount: 5 }` |
|
|
83
|
+
| Debug logging | `debug` | `{ debug: true }` |
|
|
84
|
+
| Abort signal | `abortSignal` | `{ abortSignal: controller.signal }` |
|
|
85
|
+
| Memory | `mem` | `{ mem: new AxMemory() }` |
|
|
86
|
+
| Stop function | `stopFunction` | `{ stopFunction: 'finalAnswer' }` |
|
|
87
|
+
| Function mode | `functionCallMode` | `{ functionCallMode: 'auto' }` |
|
|
1229
88
|
|
|
1230
|
-
|
|
89
|
+
## Memory and Context
|
|
1231
90
|
|
|
1232
91
|
```typescript
|
|
1233
|
-
import {
|
|
92
|
+
import { AxMemory } from '@ax-llm/ax';
|
|
1234
93
|
|
|
1235
|
-
const
|
|
1236
|
-
const gen = ax(
|
|
1237
|
-
f()
|
|
1238
|
-
.input('problem', f.string())
|
|
1239
|
-
.output('solution', f.string())
|
|
1240
|
-
.build()
|
|
1241
|
-
);
|
|
94
|
+
const memory = new AxMemory();
|
|
1242
95
|
|
|
1243
|
-
|
|
1244
|
-
|
|
96
|
+
// Multi-turn conversation
|
|
97
|
+
await gen.forward(llm, { userMessage: 'My name is Alice' }, { mem: memory });
|
|
98
|
+
const r = await gen.forward(llm, { userMessage: 'What is my name?' }, { mem: memory });
|
|
99
|
+
```
|
|
1245
100
|
|
|
1246
|
-
|
|
1247
|
-
thinkingTokenBudget: 10000,
|
|
101
|
+
## Few-Shot Examples
|
|
1248
102
|
|
|
1249
|
-
|
|
1250
|
-
|
|
103
|
+
```typescript
|
|
104
|
+
const classifier = ax('reviewText:string -> sentiment:class "positive, negative, neutral"');
|
|
1251
105
|
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
}
|
|
106
|
+
classifier.setExamples([
|
|
107
|
+
{ reviewText: 'I love this!', sentiment: 'positive' },
|
|
108
|
+
{ reviewText: 'Terrible.', sentiment: 'negative' },
|
|
109
|
+
{ reviewText: 'It works.', sentiment: 'neutral' },
|
|
110
|
+
]);
|
|
1255
111
|
```
|
|
1256
112
|
|
|
1257
|
-
|
|
113
|
+
## Common Patterns
|
|
1258
114
|
|
|
1259
|
-
|
|
115
|
+
### Classification
|
|
1260
116
|
|
|
1261
117
|
```typescript
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
const llm = ai({ name: 'openai', apiKey: process.env.OPENAI_API_KEY! });
|
|
1265
|
-
const gen = ax(
|
|
118
|
+
const classifier = ax(
|
|
1266
119
|
f()
|
|
1267
|
-
.input('
|
|
1268
|
-
.output('
|
|
1269
|
-
.output('confidence', f.number())
|
|
120
|
+
.input('text', f.string())
|
|
121
|
+
.output('category', f.class(['spam', 'ham', 'uncertain']))
|
|
122
|
+
.output('confidence', f.number().min(0).max(1))
|
|
1270
123
|
.build()
|
|
1271
124
|
);
|
|
1272
|
-
|
|
1273
|
-
const result = await gen.forward(llm, { input: 'test' }, {
|
|
1274
|
-
// Generate multiple samples
|
|
1275
|
-
sampleCount: 5,
|
|
1276
|
-
|
|
1277
|
-
// Pick the best result (custom function)
|
|
1278
|
-
resultPicker: (results) => {
|
|
1279
|
-
// Return the result with highest confidence
|
|
1280
|
-
return results.reduce((best, current) =>
|
|
1281
|
-
current.confidence > best.confidence ? current : best
|
|
1282
|
-
);
|
|
1283
|
-
}
|
|
1284
|
-
});
|
|
1285
125
|
```
|
|
1286
126
|
|
|
1287
|
-
###
|
|
1288
|
-
|
|
1289
|
-
Control how agents use tools/functions:
|
|
127
|
+
### Extraction
|
|
1290
128
|
|
|
1291
129
|
```typescript
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
const llm = ai({ name: 'openai', apiKey: process.env.OPENAI_API_KEY! });
|
|
1295
|
-
|
|
1296
|
-
const myAgent = agent(
|
|
130
|
+
const extractor = ax(
|
|
1297
131
|
f()
|
|
1298
|
-
.input('
|
|
1299
|
-
.output('
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
}
|
|
132
|
+
.input('text', f.string())
|
|
133
|
+
.output('entities', f.object({
|
|
134
|
+
people: f.string().array(),
|
|
135
|
+
organizations: f.string().array(),
|
|
136
|
+
locations: f.string().array()
|
|
137
|
+
}))
|
|
138
|
+
.build()
|
|
1306
139
|
);
|
|
1307
|
-
|
|
1308
|
-
const result = await myAgent.forward(llm, { query: 'test' }, {
|
|
1309
|
-
// Function calling mode:
|
|
1310
|
-
// - 'auto': Model decides when to call functions
|
|
1311
|
-
// - 'none': Disable function calling
|
|
1312
|
-
// - 'required': Force at least one function call
|
|
1313
|
-
functionCallMode: 'auto',
|
|
1314
|
-
|
|
1315
|
-
// Force a specific function to be called
|
|
1316
|
-
functionCall: 'searchTool',
|
|
1317
|
-
|
|
1318
|
-
// Stop after this function is called
|
|
1319
|
-
stopFunction: 'finalAnswer',
|
|
1320
|
-
|
|
1321
|
-
// Override available functions for this request
|
|
1322
|
-
functions: [searchTool],
|
|
1323
|
-
|
|
1324
|
-
// Custom caching for function results
|
|
1325
|
-
cachingFunction: async (funcName, args) => {
|
|
1326
|
-
const cacheKey = `${funcName}:${JSON.stringify(args)}`;
|
|
1327
|
-
return await cache.get(cacheKey);
|
|
1328
|
-
}
|
|
1329
|
-
});
|
|
1330
140
|
```
|
|
1331
141
|
|
|
1332
|
-
###
|
|
142
|
+
### Multi-modal (Images)
|
|
1333
143
|
|
|
1334
144
|
```typescript
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
const llm = ai({ name: 'openai', apiKey: process.env.OPENAI_API_KEY! });
|
|
1338
|
-
const gen = ax(
|
|
145
|
+
const analyzer = ax(
|
|
1339
146
|
f()
|
|
1340
|
-
.input('
|
|
1341
|
-
.
|
|
147
|
+
.input('image', f.image('Image to analyze'))
|
|
148
|
+
.input('question', f.string('Question').optional())
|
|
149
|
+
.output('description', f.string())
|
|
150
|
+
.output('objects', f.string().array())
|
|
1342
151
|
.build()
|
|
1343
152
|
);
|
|
1344
153
|
|
|
1345
|
-
const result = await
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
// Show verbose output (more details)
|
|
1350
|
-
verbose: true,
|
|
1351
|
-
|
|
1352
|
-
// Hide system prompt in debug output (security)
|
|
1353
|
-
debugHideSystemPrompt: true,
|
|
1354
|
-
|
|
1355
|
-
// Custom logger
|
|
1356
|
-
logger: axCreateDefaultColorLogger(),
|
|
1357
|
-
|
|
1358
|
-
// OpenTelemetry tracer for distributed tracing
|
|
1359
|
-
tracer: openTelemetryTracer,
|
|
1360
|
-
|
|
1361
|
-
// OpenTelemetry meter for metrics
|
|
1362
|
-
meter: openTelemetryMeter,
|
|
1363
|
-
|
|
1364
|
-
// Parent trace context
|
|
1365
|
-
traceContext: parentSpan,
|
|
1366
|
-
|
|
1367
|
-
// Custom labels for traces/metrics
|
|
1368
|
-
customLabels: { environment: 'production', version: '1.0' },
|
|
1369
|
-
|
|
1370
|
-
// Exclude content from traces (privacy)
|
|
1371
|
-
excludeContentFromTrace: true
|
|
154
|
+
const result = await analyzer.forward(llm, {
|
|
155
|
+
image: { mimeType: 'image/jpeg', data: base64Data },
|
|
156
|
+
question: 'What objects are in this image?'
|
|
1372
157
|
});
|
|
1373
158
|
```
|
|
1374
159
|
|
|
1375
|
-
###
|
|
160
|
+
### Chaining Generators
|
|
1376
161
|
|
|
1377
162
|
```typescript
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
const llm = ai({
|
|
1381
|
-
name: 'openai',
|
|
1382
|
-
apiKey: process.env.OPENAI_API_KEY!,
|
|
1383
|
-
options: {
|
|
1384
|
-
// Retry configuration for API calls
|
|
1385
|
-
retry: {
|
|
1386
|
-
// Maximum retry attempts
|
|
1387
|
-
maxRetries: 3,
|
|
1388
|
-
|
|
1389
|
-
// Initial delay between retries (ms)
|
|
1390
|
-
initialDelayMs: 1000,
|
|
1391
|
-
|
|
1392
|
-
// Maximum delay between retries (ms)
|
|
1393
|
-
maxDelayMs: 30000,
|
|
163
|
+
const researcher = ax('topic:string -> research:string, keyFacts:string[]');
|
|
164
|
+
const writer = ax('research:string, keyFacts:string[] -> article:string');
|
|
1394
165
|
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
// Jitter factor (0-1) to randomize delays
|
|
1399
|
-
jitterFactor: 0.1,
|
|
1400
|
-
|
|
1401
|
-
// HTTP status codes to retry on
|
|
1402
|
-
retryOnStatusCodes: [429, 500, 502, 503, 504]
|
|
1403
|
-
},
|
|
1404
|
-
|
|
1405
|
-
// Rate limiter for API calls
|
|
1406
|
-
rateLimiter: customRateLimiter,
|
|
1407
|
-
|
|
1408
|
-
// Request timeout (ms)
|
|
1409
|
-
timeout: 60000
|
|
1410
|
-
}
|
|
1411
|
-
});
|
|
166
|
+
const research = await researcher.forward(llm, { topic: 'AGI' });
|
|
167
|
+
const draft = await writer.forward(llm, { research: research.research, keyFacts: research.keyFacts });
|
|
1412
168
|
```
|
|
1413
169
|
|
|
1414
|
-
|
|
170
|
+
## Error Handling
|
|
1415
171
|
|
|
1416
172
|
```typescript
|
|
1417
|
-
import {
|
|
1418
|
-
|
|
1419
|
-
const llm = ai({ name: 'openai', apiKey: process.env.OPENAI_API_KEY! });
|
|
1420
|
-
const gen = ax(
|
|
1421
|
-
f()
|
|
1422
|
-
.input('input', f.string())
|
|
1423
|
-
.output('output', f.string())
|
|
1424
|
-
.build()
|
|
1425
|
-
);
|
|
1426
|
-
|
|
1427
|
-
// Create abort controller
|
|
1428
|
-
const controller = new AbortController();
|
|
1429
|
-
|
|
1430
|
-
// Cancel after 5 seconds
|
|
1431
|
-
setTimeout(() => controller.abort(), 5000);
|
|
173
|
+
import { AxGenerateError, AxAIServiceError, AxAIServiceAbortedError } from '@ax-llm/ax';
|
|
1432
174
|
|
|
1433
175
|
try {
|
|
1434
|
-
const result = await gen.forward(llm, { input: 'test' }
|
|
1435
|
-
// Abort signal for cancellation
|
|
1436
|
-
abortSignal: controller.signal,
|
|
1437
|
-
|
|
1438
|
-
// Request timeout (ms)
|
|
1439
|
-
timeout: 30000,
|
|
1440
|
-
|
|
1441
|
-
// Custom fetch function (for proxies, etc.)
|
|
1442
|
-
fetch: customFetch,
|
|
1443
|
-
|
|
1444
|
-
// CORS proxy URL (for browser environments)
|
|
1445
|
-
corsProxy: 'https://cors-proxy.example.com'
|
|
1446
|
-
});
|
|
176
|
+
const result = await gen.forward(llm, { input: 'test' });
|
|
1447
177
|
} catch (error) {
|
|
1448
|
-
if (error
|
|
1449
|
-
console.
|
|
178
|
+
if (error instanceof AxGenerateError) {
|
|
179
|
+
console.error('Generation failed:', error.details.model, error.details.signature);
|
|
180
|
+
} else if (error instanceof AxAIServiceAbortedError) {
|
|
181
|
+
console.log('Request was aborted');
|
|
182
|
+
} else if (error instanceof AxAIServiceError) {
|
|
183
|
+
console.error('AI service error:', error.message);
|
|
1450
184
|
}
|
|
1451
185
|
}
|
|
1452
186
|
```
|
|
1453
187
|
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
```typescript
|
|
1457
|
-
import { ai, ax, f, AxMemory } from '@ax-llm/ax';
|
|
1458
|
-
|
|
1459
|
-
const llm = ai({ name: 'openai', apiKey: process.env.OPENAI_API_KEY! });
|
|
1460
|
-
const gen = ax(
|
|
1461
|
-
f()
|
|
1462
|
-
.input('message', f.string())
|
|
1463
|
-
.output('response', f.string())
|
|
1464
|
-
.build()
|
|
1465
|
-
);
|
|
1466
|
-
|
|
1467
|
-
const memory = new AxMemory();
|
|
1468
|
-
|
|
1469
|
-
const result = await gen.forward(llm, { message: 'Hello' }, {
|
|
1470
|
-
// Use shared memory for conversation context
|
|
1471
|
-
mem: memory,
|
|
1472
|
-
|
|
1473
|
-
// Disable automatic memory cleanup (keep all messages)
|
|
1474
|
-
disableMemoryCleanup: true
|
|
1475
|
-
});
|
|
1476
|
-
```
|
|
1477
|
-
|
|
1478
|
-
### Validation Options
|
|
188
|
+
## Debugging
|
|
1479
189
|
|
|
1480
190
|
```typescript
|
|
1481
|
-
import {
|
|
1482
|
-
|
|
1483
|
-
const llm = ai({ name: 'openai', apiKey: process.env.OPENAI_API_KEY! });
|
|
1484
|
-
const gen = ax(
|
|
1485
|
-
f()
|
|
1486
|
-
.input('input', f.string())
|
|
1487
|
-
.output('output', f.string())
|
|
1488
|
-
.build()
|
|
1489
|
-
);
|
|
191
|
+
import { axCreateDefaultColorLogger } from '@ax-llm/ax';
|
|
1490
192
|
|
|
1491
193
|
const result = await gen.forward(llm, { input: 'test' }, {
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
{
|
|
1498
|
-
fn: (output) => output.output.length > 10,
|
|
1499
|
-
message: 'Output must be longer than 10 characters'
|
|
1500
|
-
}
|
|
1501
|
-
],
|
|
1502
|
-
|
|
1503
|
-
// Streaming assertions (run during streaming)
|
|
1504
|
-
streamingAsserts: [
|
|
1505
|
-
{
|
|
1506
|
-
fn: (partial) => !partial.output?.includes('forbidden'),
|
|
1507
|
-
message: 'Output must not contain forbidden content'
|
|
1508
|
-
}
|
|
1509
|
-
]
|
|
194
|
+
debug: true,
|
|
195
|
+
logger: axCreateDefaultColorLogger(),
|
|
196
|
+
// OpenTelemetry
|
|
197
|
+
tracer: openTelemetryTracer,
|
|
198
|
+
meter: openTelemetryMeter,
|
|
1510
199
|
});
|
|
1511
200
|
```
|
|
1512
201
|
|
|
1513
|
-
##
|
|
1514
|
-
|
|
1515
|
-
MCP (Model Context Protocol) enables AxAgent to use external tools from MCP-compliant servers. This allows your agents to interact with databases, file systems, APIs, and other services through a standardized protocol.
|
|
1516
|
-
|
|
1517
|
-
### Quick Start
|
|
202
|
+
## MCP Integration
|
|
1518
203
|
|
|
1519
204
|
```typescript
|
|
1520
|
-
import {
|
|
205
|
+
import { AxMCPClient } from '@ax-llm/ax';
|
|
1521
206
|
import { AxMCPStdioTransport } from '@ax-llm/ax-tools';
|
|
1522
207
|
|
|
1523
|
-
//
|
|
208
|
+
// Stdio transport (local MCP server)
|
|
1524
209
|
const transport = new AxMCPStdioTransport({
|
|
1525
210
|
command: 'npx',
|
|
1526
211
|
args: ['-y', '@modelcontextprotocol/server-memory'],
|
|
1527
212
|
});
|
|
1528
213
|
|
|
1529
|
-
// Initialize MCP client
|
|
1530
214
|
const mcpClient = new AxMCPClient(transport, { debug: false });
|
|
1531
215
|
await mcpClient.init();
|
|
1532
216
|
|
|
1533
|
-
//
|
|
1534
|
-
const
|
|
1535
|
-
name: '
|
|
1536
|
-
description: 'An assistant with MCP
|
|
1537
|
-
|
|
1538
|
-
.input('userMessage', f.string())
|
|
1539
|
-
.output('response', f.string())
|
|
1540
|
-
.build(),
|
|
1541
|
-
functions: [mcpClient], // Pass client directly
|
|
1542
|
-
});
|
|
1543
|
-
|
|
1544
|
-
const ai = new AxAI({ name: 'openai', apiKey: process.env.OPENAI_API_KEY! });
|
|
1545
|
-
const result = await agent.forward(ai, { userMessage: 'Hello' });
|
|
1546
|
-
```
|
|
1547
|
-
|
|
1548
|
-
### MCP Transports
|
|
1549
|
-
|
|
1550
|
-
#### AxMCPStdioTransport (Local Servers)
|
|
1551
|
-
|
|
1552
|
-
For MCP servers that run as local processes via stdin/stdout:
|
|
1553
|
-
|
|
1554
|
-
```typescript
|
|
1555
|
-
import { AxMCPStdioTransport } from '@ax-llm/ax-tools';
|
|
1556
|
-
|
|
1557
|
-
const transport = new AxMCPStdioTransport({
|
|
1558
|
-
command: 'npx',
|
|
1559
|
-
args: ['-y', '@modelcontextprotocol/server-memory'],
|
|
217
|
+
// Use with agent
|
|
218
|
+
const myAgent = agent('userMessage:string -> response:string', {
|
|
219
|
+
name: 'assistant',
|
|
220
|
+
description: 'An assistant with MCP tools',
|
|
221
|
+
functions: [mcpClient],
|
|
1560
222
|
});
|
|
1561
|
-
|
|
1562
|
-
// Clean up when done
|
|
1563
|
-
await transport.terminate();
|
|
1564
223
|
```
|
|
1565
224
|
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
For MCP servers accessible via HTTP (e.g., Pipedream, hosted services):
|
|
225
|
+
### HTTP Transport (Remote MCP)
|
|
1569
226
|
|
|
1570
227
|
```typescript
|
|
1571
228
|
import { AxMCPStreambleHTTPTransport } from '@ax-llm/ax/mcp/transports/httpStreamTransport.js';
|
|
1572
229
|
|
|
1573
|
-
const transport = new AxMCPStreambleHTTPTransport(
|
|
1574
|
-
'
|
|
1575
|
-
{
|
|
1576
|
-
headers: {
|
|
1577
|
-
'x-pd-project-id': projectId,
|
|
1578
|
-
'x-pd-environment': 'development',
|
|
1579
|
-
'x-pd-external-user-id': 'user123',
|
|
1580
|
-
'x-pd-app-slug': 'notion',
|
|
1581
|
-
},
|
|
1582
|
-
authorization: `Bearer ${accessToken}`,
|
|
1583
|
-
}
|
|
1584
|
-
);
|
|
1585
|
-
```
|
|
1586
|
-
|
|
1587
|
-
### Using MCP with Agents
|
|
1588
|
-
|
|
1589
|
-
Pass the MCP client directly to the agent's `functions` array:
|
|
1590
|
-
|
|
1591
|
-
```typescript
|
|
1592
|
-
import { AxAgent, AxAI, AxMCPClient, f } from '@ax-llm/ax';
|
|
1593
|
-
import { AxMCPStdioTransport } from '@ax-llm/ax-tools';
|
|
1594
|
-
|
|
1595
|
-
const transport = new AxMCPStdioTransport({
|
|
1596
|
-
command: 'npx',
|
|
1597
|
-
args: ['-y', '@modelcontextprotocol/server-memory'],
|
|
1598
|
-
});
|
|
1599
|
-
|
|
1600
|
-
const mcpClient = new AxMCPClient(transport);
|
|
1601
|
-
await mcpClient.init();
|
|
1602
|
-
|
|
1603
|
-
const memoryAgent = new AxAgent<
|
|
1604
|
-
{ userMessage: string; userId: string },
|
|
1605
|
-
{ assistantResponse: string }
|
|
1606
|
-
>({
|
|
1607
|
-
name: 'MemoryAssistant',
|
|
1608
|
-
description: 'An assistant that remembers past conversations. Use the database functions to manage, search, and add memories.',
|
|
1609
|
-
signature: f()
|
|
1610
|
-
.input('userMessage', f.string())
|
|
1611
|
-
.input('userId', f.string())
|
|
1612
|
-
.output('assistantResponse', f.string())
|
|
1613
|
-
.build(),
|
|
1614
|
-
functions: [mcpClient],
|
|
1615
|
-
});
|
|
1616
|
-
|
|
1617
|
-
const ai = new AxAI({
|
|
1618
|
-
name: 'openai',
|
|
1619
|
-
apiKey: process.env.OPENAI_API_KEY!,
|
|
1620
|
-
config: { model: 'gpt-4o-mini' }
|
|
1621
|
-
});
|
|
1622
|
-
|
|
1623
|
-
// First interaction - stores memory
|
|
1624
|
-
const first = await memoryAgent.forward(ai, {
|
|
1625
|
-
userMessage: 'My name is Alice and my favorite color is blue.',
|
|
1626
|
-
userId: 'user123',
|
|
1627
|
-
});
|
|
1628
|
-
|
|
1629
|
-
// Later interaction - retrieves memory
|
|
1630
|
-
const second = await memoryAgent.forward(ai, {
|
|
1631
|
-
userMessage: "What's my favorite color?",
|
|
1632
|
-
userId: 'user123',
|
|
230
|
+
const transport = new AxMCPStreambleHTTPTransport('https://remote.mcp.pipedream.net', {
|
|
231
|
+
headers: { 'x-pd-project-id': projectId },
|
|
232
|
+
authorization: `Bearer ${accessToken}`,
|
|
1633
233
|
});
|
|
1634
234
|
```
|
|
1635
235
|
|
|
1636
236
|
### MCP Capabilities
|
|
1637
237
|
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
|
1641
|
-
|
|
1642
|
-
|
|
|
1643
|
-
| **Prompts** | `prompt_` | Prompt templates (e.g., `prompt_summarize`) |
|
|
1644
|
-
| **Resources** | `resource_` | File/data access (e.g., `resource_config_json`) |
|
|
1645
|
-
|
|
1646
|
-
Check available capabilities:
|
|
238
|
+
| Capability | Prefix | Description |
|
|
239
|
+
|---|---|---|
|
|
240
|
+
| Tools | *(none)* | Function calls |
|
|
241
|
+
| Prompts | `prompt_` | Prompt templates |
|
|
242
|
+
| Resources | `resource_` | File/data access |
|
|
1647
243
|
|
|
1648
244
|
```typescript
|
|
1649
|
-
const mcpClient = new AxMCPClient(transport);
|
|
1650
|
-
await mcpClient.init();
|
|
1651
|
-
|
|
1652
245
|
const caps = mcpClient.getCapabilities();
|
|
1653
|
-
|
|
1654
|
-
console.log('Prompts:', caps.prompts); // true/false
|
|
1655
|
-
console.log('Resources:', caps.resources); // true/false
|
|
1656
|
-
|
|
1657
|
-
// Or check individually
|
|
1658
|
-
if (mcpClient.hasToolsCapability()) {
|
|
1659
|
-
console.log('Server supports tools');
|
|
1660
|
-
}
|
|
246
|
+
const functions = mcpClient.toFunction();
|
|
1661
247
|
```
|
|
1662
248
|
|
|
1663
249
|
### Function Overrides
|
|
1664
250
|
|
|
1665
|
-
Customize function names and descriptions while preserving functionality:
|
|
1666
|
-
|
|
1667
251
|
```typescript
|
|
1668
252
|
const mcpClient = new AxMCPClient(transport, {
|
|
1669
253
|
functionOverrides: [
|
|
1670
|
-
{
|
|
1671
|
-
name: 'search_documents',
|
|
1672
|
-
updates: {
|
|
1673
|
-
name: 'findDocs',
|
|
1674
|
-
description: 'Search through all available documents'
|
|
1675
|
-
}
|
|
1676
|
-
},
|
|
1677
|
-
{
|
|
1678
|
-
name: 'prompt_summarize',
|
|
1679
|
-
updates: {
|
|
1680
|
-
name: 'getSummaryPrompt',
|
|
1681
|
-
description: 'Get a prompt template for summarization'
|
|
1682
|
-
}
|
|
1683
|
-
}
|
|
254
|
+
{ name: 'search_documents', updates: { name: 'findDocs', description: 'Search docs' } }
|
|
1684
255
|
]
|
|
1685
256
|
});
|
|
1686
257
|
```
|
|
1687
258
|
|
|
1688
|
-
### Getting Functions Directly
|
|
1689
|
-
|
|
1690
|
-
If you need the function array instead of passing the client:
|
|
1691
|
-
|
|
1692
|
-
```typescript
|
|
1693
|
-
const mcpClient = new AxMCPClient(transport);
|
|
1694
|
-
await mcpClient.init();
|
|
1695
|
-
|
|
1696
|
-
// Get all functions (tools + prompts + resources)
|
|
1697
|
-
const functions = mcpClient.toFunction();
|
|
1698
|
-
|
|
1699
|
-
// Use with agent
|
|
1700
|
-
const agent = new AxAgent({
|
|
1701
|
-
name: 'MyAgent',
|
|
1702
|
-
signature: f()
|
|
1703
|
-
.input('query', f.string())
|
|
1704
|
-
.output('answer', f.string())
|
|
1705
|
-
.build(),
|
|
1706
|
-
functions: functions, // Or spread: [...functions, otherFunction]
|
|
1707
|
-
});
|
|
1708
|
-
```
|
|
1709
|
-
|
|
1710
|
-
### Complete Example: Remote HTTP MCP Server
|
|
1711
|
-
|
|
1712
|
-
```typescript
|
|
1713
|
-
import { AxAgent, AxAI, AxMCPClient, f } from '@ax-llm/ax';
|
|
1714
|
-
import { AxMCPStreambleHTTPTransport } from '@ax-llm/ax/mcp/transports/httpStreamTransport.js';
|
|
1715
|
-
import { createBackendClient } from '@pipedream/sdk/server';
|
|
1716
|
-
|
|
1717
|
-
// Initialize Pipedream SDK
|
|
1718
|
-
const pd = createBackendClient({
|
|
1719
|
-
environment: 'development',
|
|
1720
|
-
credentials: {
|
|
1721
|
-
clientId: process.env.PIPEDREAM_CLIENT_ID!,
|
|
1722
|
-
clientSecret: process.env.PIPEDREAM_CLIENT_SECRET!,
|
|
1723
|
-
},
|
|
1724
|
-
projectId: process.env.PIPEDREAM_PROJECT_ID!,
|
|
1725
|
-
});
|
|
1726
|
-
|
|
1727
|
-
// Get access token and app info
|
|
1728
|
-
const accessToken = await pd.rawAccessToken();
|
|
1729
|
-
const apps = await pd.getApps({ q: 'notion' });
|
|
1730
|
-
const appSlug = apps.data[0]?.name_slug;
|
|
1731
|
-
|
|
1732
|
-
// Create HTTP transport for Pipedream MCP
|
|
1733
|
-
const httpTransport = new AxMCPStreambleHTTPTransport(
|
|
1734
|
-
'https://remote.mcp.pipedream.net',
|
|
1735
|
-
{
|
|
1736
|
-
headers: {
|
|
1737
|
-
'x-pd-project-id': process.env.PIPEDREAM_PROJECT_ID!,
|
|
1738
|
-
'x-pd-environment': 'development',
|
|
1739
|
-
'x-pd-external-user-id': 'user123',
|
|
1740
|
-
'x-pd-app-slug': appSlug!,
|
|
1741
|
-
},
|
|
1742
|
-
authorization: `Bearer ${accessToken}`,
|
|
1743
|
-
}
|
|
1744
|
-
);
|
|
1745
|
-
|
|
1746
|
-
// Initialize MCP client
|
|
1747
|
-
const mcpClient = new AxMCPClient(httpTransport, { debug: false });
|
|
1748
|
-
await mcpClient.init();
|
|
1749
|
-
|
|
1750
|
-
// Create Notion agent
|
|
1751
|
-
const notionAgent = new AxAgent<
|
|
1752
|
-
{ userRequest: string },
|
|
1753
|
-
{ assistantResponse: string }
|
|
1754
|
-
>({
|
|
1755
|
-
name: 'NotionAssistant',
|
|
1756
|
-
description: 'An assistant that can interact with Notion documents. Use the provided functions to read, search, and analyze Notion content.',
|
|
1757
|
-
signature: f()
|
|
1758
|
-
.input('userRequest', f.string())
|
|
1759
|
-
.output('assistantResponse', f.string())
|
|
1760
|
-
.build(),
|
|
1761
|
-
functions: [mcpClient],
|
|
1762
|
-
});
|
|
1763
|
-
|
|
1764
|
-
const ai = new AxAI({
|
|
1765
|
-
name: 'openai',
|
|
1766
|
-
apiKey: process.env.OPENAI_API_KEY!,
|
|
1767
|
-
config: { model: 'gpt-4o-mini' }
|
|
1768
|
-
});
|
|
1769
|
-
|
|
1770
|
-
// Use the agent
|
|
1771
|
-
const response = await notionAgent.forward(ai, {
|
|
1772
|
-
userRequest: 'Summarize my most recently created Notion doc'
|
|
1773
|
-
});
|
|
1774
|
-
console.log(response.assistantResponse);
|
|
1775
|
-
```
|
|
1776
|
-
|
|
1777
|
-
### Example Files
|
|
1778
|
-
|
|
1779
|
-
Full working examples on GitHub:
|
|
1780
|
-
|
|
1781
|
-
- [Local Memory Server (stdio)](https://raw.githubusercontent.com/ax-llm/ax/refs/heads/main/src/examples/mcp-client-memory.ts) - Memory-augmented agent using local MCP server
|
|
1782
|
-
- [Remote HTTP Server (Pipedream/Notion)](https://raw.githubusercontent.com/ax-llm/ax/refs/heads/main/src/examples/mcp-client-pipedream.ts) - Notion integration via Pipedream MCP
|
|
1783
|
-
|
|
1784
259
|
## Type Reference
|
|
1785
260
|
|
|
1786
261
|
```typescript
|
|
1787
|
-
// Core types
|
|
1788
|
-
type AxGenIn = Record<string, any>;
|
|
1789
|
-
type AxGenOut = Record<string, any>;
|
|
1790
|
-
|
|
1791
|
-
// Generator
|
|
1792
262
|
class AxGen<IN, OUT> {
|
|
1793
263
|
forward(ai: AxAIService, values: IN, options?: AxProgramForwardOptions): Promise<OUT>;
|
|
1794
264
|
streamingForward(ai: AxAIService, values: IN, options?: AxProgramStreamingForwardOptions): AsyncGenerator<{ delta: Partial<OUT> }>;
|
|
@@ -1796,22 +266,27 @@ class AxGen<IN, OUT> {
|
|
|
1796
266
|
addAssert(fn: (output: OUT) => boolean, message?: string): void;
|
|
1797
267
|
addFieldProcessor(field: keyof OUT, fn: (value: any) => any): void;
|
|
1798
268
|
addStreamingFieldProcessor(field: keyof OUT, fn: (chunk: string, ctx: any) => void): void;
|
|
269
|
+
stop(): void;
|
|
1799
270
|
}
|
|
1800
271
|
|
|
1801
|
-
// Agent
|
|
1802
272
|
class AxAgent<IN, OUT> {
|
|
1803
273
|
forward(ai: AxAIService, values: IN, options?: AxAgentOptions): Promise<OUT>;
|
|
1804
274
|
streamingForward(ai: AxAIService, values: IN, options?: AxAgentOptions): AsyncGenerator<{ delta: Partial<OUT> }>;
|
|
1805
275
|
getFunction(): AxFunction;
|
|
1806
276
|
}
|
|
1807
277
|
|
|
1808
|
-
|
|
1809
|
-
class AxFlow<IN, OUT, TNodes, TState> {
|
|
278
|
+
class AxFlow<IN, OUT> {
|
|
1810
279
|
node(name: string, signature: string | AxSignature): AxFlow;
|
|
1811
|
-
execute(
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
parallel(branches: ParallelBranch[]): AxFlow;
|
|
1815
|
-
forward(ai: AxAIService, input: IN): Promise<OUT>;
|
|
280
|
+
execute(name: string, mapper: (state) => any): AxFlow;
|
|
281
|
+
returns(mapper: (state) => OUT): AxFlow;
|
|
282
|
+
forward(ai: AxAIService, values: IN): Promise<OUT>;
|
|
1816
283
|
}
|
|
1817
284
|
```
|
|
285
|
+
|
|
286
|
+
## Examples
|
|
287
|
+
|
|
288
|
+
Fetch these for full working code:
|
|
289
|
+
|
|
290
|
+
- [Chat](https://raw.githubusercontent.com/ax-llm/ax/refs/heads/main/src/examples/chat.ts) — multi-turn conversation
|
|
291
|
+
- [Marketing](https://raw.githubusercontent.com/ax-llm/ax/refs/heads/main/src/examples/marketing.ts) — product use case
|
|
292
|
+
- [MCP Integration](https://raw.githubusercontent.com/ax-llm/ax/refs/heads/main/src/examples/mcp-client-memory.ts) — MCP integration
|