@aeye/core 0.3.7 → 0.3.8
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/README.md +279 -225
- package/dist/common.d.ts.map +1 -1
- package/dist/common.js +17 -4
- package/dist/common.js.map +1 -1
- package/dist/prompt.d.ts +8 -0
- package/dist/prompt.d.ts.map +1 -1
- package/dist/prompt.js +43 -5
- package/dist/prompt.js.map +1 -1
- package/dist/to.js +1 -0
- package/dist/to.js.map +1 -1
- package/dist/tool.d.ts +27 -0
- package/dist/tool.d.ts.map +1 -1
- package/dist/tool.js +30 -0
- package/dist/tool.js.map +1 -1
- package/dist/types.d.ts +8 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,6 +19,8 @@ Core primitives for building AI agents, tools, and prompts with TypeScript. Prov
|
|
|
19
19
|
npm install @aeye/core zod handlebars
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
+
> **Note:** `@aeye/core` defines the primitives — you need an executor/streamer to run prompts. These are provided by `@aeye/ai` via `ai.buildCoreContext()`, or you can supply your own `execute` / `stream` functions.
|
|
23
|
+
|
|
22
24
|
## Core Concepts
|
|
23
25
|
|
|
24
26
|
### Components
|
|
@@ -46,29 +48,29 @@ const summarizer = new Prompt({
|
|
|
46
48
|
name: 'summarize',
|
|
47
49
|
description: 'Summarizes text concisely',
|
|
48
50
|
content: 'Summarize the following text:\n\n{{text}}',
|
|
49
|
-
|
|
50
|
-
// Transform input
|
|
51
|
+
|
|
52
|
+
// Transform raw input into template variables
|
|
51
53
|
input: (input: { text: string }) => ({ text: input.text }),
|
|
52
|
-
|
|
53
|
-
// Define output schema
|
|
54
|
+
|
|
55
|
+
// Define output schema (structured JSON)
|
|
54
56
|
schema: z.object({
|
|
55
57
|
summary: z.string().describe('A concise summary'),
|
|
56
|
-
keyPoints: z.array(z.string()).describe('Main points')
|
|
57
|
-
})
|
|
58
|
+
keyPoints: z.array(z.string()).describe('Main points'),
|
|
59
|
+
}),
|
|
58
60
|
});
|
|
59
61
|
|
|
60
|
-
// Execute
|
|
62
|
+
// Execute — ctx must provide execute or stream (from @aeye/ai or custom)
|
|
61
63
|
const result = await summarizer.get(
|
|
62
64
|
'result',
|
|
63
65
|
{ text: 'Long article text...' },
|
|
64
66
|
{
|
|
65
|
-
execute: yourAIExecutor,
|
|
66
|
-
messages: []
|
|
67
|
+
execute: yourAIExecutor, // (request, ctx, metadata?, signal?) => Promise<Response>
|
|
68
|
+
messages: [],
|
|
67
69
|
}
|
|
68
70
|
);
|
|
69
71
|
|
|
70
|
-
console.log(result
|
|
71
|
-
console.log(result
|
|
72
|
+
console.log(result?.summary);
|
|
73
|
+
console.log(result?.keyPoints);
|
|
72
74
|
```
|
|
73
75
|
|
|
74
76
|
### Creating Tools
|
|
@@ -77,69 +79,73 @@ console.log(result.keyPoints);
|
|
|
77
79
|
import { Tool } from '@aeye/core';
|
|
78
80
|
import z from 'zod';
|
|
79
81
|
|
|
80
|
-
const
|
|
82
|
+
const getWeather = new Tool({
|
|
81
83
|
name: 'getWeather',
|
|
82
84
|
description: 'Get current weather for a location',
|
|
83
|
-
instructions: 'Use this tool to get weather data',
|
|
84
|
-
|
|
85
|
+
instructions: 'Use this tool to get weather data for {{location}}.',
|
|
86
|
+
|
|
85
87
|
schema: z.object({
|
|
86
88
|
location: z.string().describe('City name or coordinates'),
|
|
87
|
-
units: z.enum(['celsius', 'fahrenheit']).default('celsius')
|
|
89
|
+
units: z.enum(['celsius', 'fahrenheit']).default('celsius'),
|
|
88
90
|
}),
|
|
89
|
-
|
|
90
|
-
call: async (input,
|
|
91
|
+
|
|
92
|
+
call: async (input, _refs, _ctx) => {
|
|
91
93
|
const response = await fetch(
|
|
92
|
-
`https://api.weather.com/v1/${input.location}`
|
|
94
|
+
`https://api.weather.com/v1/${encodeURIComponent(input.location)}`
|
|
93
95
|
);
|
|
94
96
|
const data = await response.json();
|
|
95
|
-
|
|
96
97
|
return {
|
|
97
98
|
temperature: data.temp,
|
|
98
99
|
condition: data.condition,
|
|
99
|
-
humidity: data.humidity
|
|
100
|
+
humidity: data.humidity,
|
|
100
101
|
};
|
|
101
|
-
}
|
|
102
|
+
},
|
|
102
103
|
});
|
|
103
104
|
```
|
|
104
105
|
|
|
105
106
|
### Prompts with Tools
|
|
106
107
|
|
|
107
108
|
```typescript
|
|
109
|
+
import { Prompt } from '@aeye/core';
|
|
110
|
+
import z from 'zod';
|
|
111
|
+
|
|
108
112
|
const travelAdvisor = new Prompt({
|
|
109
113
|
name: 'travelAdvisor',
|
|
110
114
|
description: 'Provides travel advice based on weather',
|
|
111
115
|
content: `You are a travel advisor. Help plan a trip to {{destination}}.
|
|
112
|
-
|
|
113
|
-
Use the
|
|
116
|
+
|
|
117
|
+
Use the getWeather tool to check current conditions, then provide:
|
|
114
118
|
- What to pack
|
|
115
|
-
-
|
|
119
|
+
- Recommended activities
|
|
116
120
|
- Best times to visit`,
|
|
117
|
-
|
|
118
|
-
input: (input: { destination: string }) => ({
|
|
119
|
-
destination: input.destination
|
|
121
|
+
|
|
122
|
+
input: (input: { destination: string }) => ({
|
|
123
|
+
destination: input.destination,
|
|
120
124
|
}),
|
|
121
|
-
|
|
122
|
-
tools: [
|
|
123
|
-
|
|
125
|
+
|
|
126
|
+
tools: [getWeather],
|
|
127
|
+
|
|
124
128
|
schema: z.object({
|
|
125
129
|
recommendations: z.array(z.string()),
|
|
126
130
|
packingList: z.array(z.string()),
|
|
127
|
-
weatherNotes: z.string()
|
|
128
|
-
})
|
|
131
|
+
weatherNotes: z.string(),
|
|
132
|
+
}),
|
|
129
133
|
});
|
|
130
134
|
|
|
131
|
-
// The AI will automatically call
|
|
135
|
+
// The AI will automatically call getWeather if needed
|
|
132
136
|
const advice = await travelAdvisor.get(
|
|
133
137
|
'result',
|
|
134
138
|
{ destination: 'Paris' },
|
|
135
139
|
{ execute: yourAIExecutor, messages: [] }
|
|
136
140
|
);
|
|
141
|
+
|
|
142
|
+
console.log(advice?.recommendations);
|
|
137
143
|
```
|
|
138
144
|
|
|
139
145
|
### Streaming Responses
|
|
140
146
|
|
|
141
147
|
```typescript
|
|
142
|
-
// Stream content only
|
|
148
|
+
// Stream text content only
|
|
143
149
|
for await (const chunk of summarizer.get(
|
|
144
150
|
'streamContent',
|
|
145
151
|
{ text: 'Long text...' },
|
|
@@ -155,11 +161,13 @@ for await (const event of summarizer.get(
|
|
|
155
161
|
{ stream: yourAIStreamer, messages: [] }
|
|
156
162
|
)) {
|
|
157
163
|
if (event.type === 'textPartial') {
|
|
158
|
-
|
|
164
|
+
process.stdout.write(event.content);
|
|
159
165
|
} else if (event.type === 'toolStart') {
|
|
160
166
|
console.log('Tool started:', event.tool.name);
|
|
161
167
|
} else if (event.type === 'toolOutput') {
|
|
162
168
|
console.log('Tool result:', event.result);
|
|
169
|
+
} else if (event.type === 'complete') {
|
|
170
|
+
console.log('Final output:', event.output);
|
|
163
171
|
}
|
|
164
172
|
}
|
|
165
173
|
```
|
|
@@ -168,40 +176,39 @@ for await (const event of summarizer.get(
|
|
|
168
176
|
|
|
169
177
|
```typescript
|
|
170
178
|
import { Agent } from '@aeye/core';
|
|
179
|
+
import z from 'zod';
|
|
171
180
|
|
|
181
|
+
// Assume searchTool, summarizePrompt, and analyzePrompt are defined elsewhere
|
|
172
182
|
const researchAgent = new Agent({
|
|
173
183
|
name: 'researcher',
|
|
174
|
-
description: 'Conducts research on
|
|
175
|
-
|
|
176
|
-
refs: [searchTool,
|
|
177
|
-
|
|
178
|
-
call: async (
|
|
184
|
+
description: 'Conducts research on a topic',
|
|
185
|
+
|
|
186
|
+
refs: [searchTool, summarizePrompt, analyzePrompt] as const,
|
|
187
|
+
|
|
188
|
+
call: async (
|
|
189
|
+
input: { topic: string },
|
|
190
|
+
[search, summarize, analyze],
|
|
191
|
+
ctx
|
|
192
|
+
) => {
|
|
179
193
|
// Step 1: Search for information
|
|
180
|
-
const searchResults = await search.run(
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
for (const result of searchResults) {
|
|
188
|
-
const summary = await summarize.get(
|
|
189
|
-
'result',
|
|
190
|
-
{ text: result.content },
|
|
191
|
-
ctx
|
|
192
|
-
);
|
|
193
|
-
summaries.push(summary);
|
|
194
|
+
const searchResults = await search.run({ query: input.topic, limit: 5 }, ctx);
|
|
195
|
+
|
|
196
|
+
// Step 2: Summarize each result
|
|
197
|
+
const summaries: string[] = [];
|
|
198
|
+
for (const result of searchResults.items) {
|
|
199
|
+
const summary = await summarize.get('result', { text: result.content }, ctx);
|
|
200
|
+
summaries.push(summary?.summary ?? '');
|
|
194
201
|
}
|
|
195
|
-
|
|
196
|
-
// Step 3:
|
|
202
|
+
|
|
203
|
+
// Step 3: Synthesize into a final analysis
|
|
197
204
|
const analysis = await analyze.get(
|
|
198
205
|
'result',
|
|
199
206
|
{ topic: input.topic, sources: summaries },
|
|
200
207
|
ctx
|
|
201
208
|
);
|
|
202
|
-
|
|
209
|
+
|
|
203
210
|
return analysis;
|
|
204
|
-
}
|
|
211
|
+
},
|
|
205
212
|
});
|
|
206
213
|
|
|
207
214
|
const research = await researchAgent.run(
|
|
@@ -212,15 +219,15 @@ const research = await researchAgent.run(
|
|
|
212
219
|
|
|
213
220
|
## Prompt Modes
|
|
214
221
|
|
|
215
|
-
The `get` method supports different execution modes:
|
|
222
|
+
The `get(mode, input?, ctx?)` method supports different execution modes:
|
|
216
223
|
|
|
217
224
|
| Mode | Description | Returns |
|
|
218
225
|
|------|-------------|---------|
|
|
219
|
-
| `'result'` |
|
|
220
|
-
| `'tools'` |
|
|
221
|
-
| `'stream'` | Stream all events | `AsyncGenerator<PromptEvent>` |
|
|
222
|
-
| `'streamTools'` | Stream tool
|
|
223
|
-
| `'streamContent'` | Stream text content only | `AsyncGenerator<string>` |
|
|
226
|
+
| `'result'` | Await the final structured output | `Promise<TOutput \| undefined>` |
|
|
227
|
+
| `'tools'` | Await all tool call results | `Promise<PromptToolOutput[] \| undefined>` |
|
|
228
|
+
| `'stream'` | Stream all events | `AsyncGenerator<PromptEvent<TOutput, TTools>, TOutput \| undefined>` |
|
|
229
|
+
| `'streamTools'` | Stream tool output events | `AsyncGenerator<PromptToolOutput<TTools>, TOutput \| undefined>` |
|
|
230
|
+
| `'streamContent'` | Stream text content only | `AsyncGenerator<string, TOutput \| undefined>` |
|
|
224
231
|
|
|
225
232
|
```typescript
|
|
226
233
|
// Get structured result
|
|
@@ -231,95 +238,106 @@ const tools = await prompt.get('tools', input, ctx);
|
|
|
231
238
|
|
|
232
239
|
// Stream everything
|
|
233
240
|
for await (const event of prompt.get('stream', input, ctx)) {
|
|
234
|
-
//
|
|
241
|
+
// event.type is one of: 'request', 'textPartial', 'text', 'toolStart',
|
|
242
|
+
// 'toolOutput', 'toolError', 'message', 'complete', 'usage', ...
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Stream text only
|
|
246
|
+
for await (const text of prompt.get('streamContent', input, ctx)) {
|
|
247
|
+
process.stdout.write(text);
|
|
235
248
|
}
|
|
236
249
|
```
|
|
237
250
|
|
|
238
251
|
## Tool Execution Modes
|
|
239
252
|
|
|
240
|
-
Control how tools are executed:
|
|
253
|
+
Control how tools are executed by the prompt:
|
|
241
254
|
|
|
242
255
|
```typescript
|
|
243
256
|
const prompt = new Prompt({
|
|
244
257
|
name: 'multi-tool',
|
|
245
258
|
description: 'Uses multiple tools',
|
|
246
|
-
content: 'Analyze the
|
|
259
|
+
content: 'Analyze this data using the available tools.',
|
|
247
260
|
tools: [tool1, tool2, tool3],
|
|
248
|
-
|
|
249
|
-
//
|
|
250
|
-
toolExecution: 'parallel',
|
|
251
|
-
|
|
252
|
-
//
|
|
253
|
-
toolRetries: 2,
|
|
254
|
-
|
|
255
|
-
|
|
261
|
+
|
|
262
|
+
// 'sequential' | 'parallel' | 'immediate' (default: 'immediate')
|
|
263
|
+
toolExecution: 'parallel',
|
|
264
|
+
|
|
265
|
+
// Number of times to retry a failed tool call (default: 2)
|
|
266
|
+
toolRetries: 2,
|
|
267
|
+
// Max tool-calling iterations per request (default: 3)
|
|
268
|
+
toolIterations: 3,
|
|
269
|
+
// Max total successful tool calls before stopping (no strict limit by default)
|
|
270
|
+
toolsMax: 5,
|
|
256
271
|
});
|
|
257
272
|
```
|
|
258
273
|
|
|
259
|
-
- **`sequential`** - Wait for each tool to
|
|
260
|
-
- **`parallel`** - Start all tools at once
|
|
261
|
-
- **`immediate`** - Start
|
|
274
|
+
- **`sequential`** - Wait for each tool to complete before starting the next
|
|
275
|
+
- **`parallel`** - Start all pending tools at once and wait for all to complete
|
|
276
|
+
- **`immediate`** - Start each tool as soon as its call is received (default)
|
|
262
277
|
|
|
263
278
|
## Advanced Features
|
|
264
279
|
|
|
265
280
|
### Context-Aware Tools
|
|
266
281
|
|
|
282
|
+
Tool schemas and applicability can depend on the current context:
|
|
283
|
+
|
|
267
284
|
```typescript
|
|
285
|
+
interface MyContext {
|
|
286
|
+
userRole: 'admin' | 'viewer';
|
|
287
|
+
isAuthenticated: boolean;
|
|
288
|
+
}
|
|
289
|
+
|
|
268
290
|
const contextTool = new Tool({
|
|
269
|
-
name: '
|
|
270
|
-
description: '
|
|
271
|
-
instructions: 'Use this tool
|
|
272
|
-
|
|
273
|
-
// Schema
|
|
274
|
-
schema: (ctx) => {
|
|
291
|
+
name: 'manageData',
|
|
292
|
+
description: 'Manage data records',
|
|
293
|
+
instructions: 'Use this tool to read or write data.',
|
|
294
|
+
|
|
295
|
+
// Schema varies based on the caller's role
|
|
296
|
+
schema: (ctx: Context<MyContext, {}>) => {
|
|
275
297
|
if (ctx.userRole === 'admin') {
|
|
276
|
-
return z.object({
|
|
277
|
-
action: z.enum(['read', 'write', 'delete'])
|
|
278
|
-
});
|
|
298
|
+
return z.object({ action: z.enum(['read', 'write', 'delete']) });
|
|
279
299
|
}
|
|
280
|
-
return z.object({
|
|
281
|
-
action: z.enum(['read'])
|
|
282
|
-
});
|
|
300
|
+
return z.object({ action: z.enum(['read']) });
|
|
283
301
|
},
|
|
284
|
-
|
|
285
|
-
//
|
|
286
|
-
applicable: (ctx) =>
|
|
287
|
-
|
|
302
|
+
|
|
303
|
+
// Tool is only available to authenticated users
|
|
304
|
+
applicable: (ctx: Context<MyContext, {}>) => ctx.isAuthenticated,
|
|
305
|
+
|
|
306
|
+
call: async (input, _refs, ctx) => {
|
|
307
|
+
return { action: input.action, executedBy: ctx.userRole };
|
|
288
308
|
},
|
|
289
|
-
|
|
290
|
-
call: async (input, refs, ctx) => {
|
|
291
|
-
// Implementation with context access
|
|
292
|
-
}
|
|
293
309
|
});
|
|
294
310
|
```
|
|
295
311
|
|
|
296
312
|
### Custom Validation
|
|
297
313
|
|
|
314
|
+
Add business-logic validation beyond Zod schema checks:
|
|
315
|
+
|
|
298
316
|
```typescript
|
|
299
|
-
const
|
|
317
|
+
const placeOrder = new Tool({
|
|
300
318
|
name: 'placeOrder',
|
|
301
|
-
description: 'Place
|
|
302
|
-
|
|
319
|
+
description: 'Place a product order',
|
|
320
|
+
|
|
303
321
|
schema: z.object({
|
|
304
322
|
itemId: z.string(),
|
|
305
|
-
quantity: z.number().min(1)
|
|
323
|
+
quantity: z.number().int().min(1),
|
|
306
324
|
}),
|
|
307
|
-
|
|
308
|
-
//
|
|
325
|
+
|
|
326
|
+
// Runs after schema parsing succeeds; throw to trigger re-prompting
|
|
309
327
|
validate: async (input, ctx) => {
|
|
310
328
|
const inventory = await checkInventory(input.itemId);
|
|
311
329
|
if (inventory < input.quantity) {
|
|
312
|
-
throw new Error(`Only ${inventory}
|
|
330
|
+
throw new Error(`Only ${inventory} units of ${input.itemId} are available.`);
|
|
313
331
|
}
|
|
314
332
|
},
|
|
315
|
-
|
|
316
|
-
call: async (input,
|
|
317
|
-
|
|
318
|
-
}
|
|
333
|
+
|
|
334
|
+
call: async (input, _refs, _ctx) => {
|
|
335
|
+
return { orderId: crypto.randomUUID(), ...input };
|
|
336
|
+
},
|
|
319
337
|
});
|
|
320
338
|
```
|
|
321
339
|
|
|
322
|
-
### Event Tracking
|
|
340
|
+
### Event Tracking with `withEvents`
|
|
323
341
|
|
|
324
342
|
```typescript
|
|
325
343
|
import { withEvents } from '@aeye/core';
|
|
@@ -327,164 +345,190 @@ import { withEvents } from '@aeye/core';
|
|
|
327
345
|
const runner = withEvents({
|
|
328
346
|
onStatus: (instance) => {
|
|
329
347
|
console.log(`${instance.component.name}: ${instance.status}`);
|
|
330
|
-
if (instance.status === 'completed') {
|
|
331
|
-
|
|
332
|
-
console.log(`Took ${duration}ms`);
|
|
348
|
+
if (instance.status === 'completed' && instance.completed && instance.running) {
|
|
349
|
+
console.log(` Took ${instance.completed - instance.running}ms`);
|
|
333
350
|
}
|
|
334
351
|
},
|
|
335
|
-
|
|
336
|
-
onPromptEvent: (
|
|
352
|
+
|
|
353
|
+
onPromptEvent: (_instance, event) => {
|
|
337
354
|
if (event.type === 'usage') {
|
|
338
355
|
console.log('Tokens used:', event.usage);
|
|
339
356
|
}
|
|
340
|
-
}
|
|
357
|
+
},
|
|
341
358
|
});
|
|
342
359
|
|
|
343
360
|
const result = await prompt.get(
|
|
344
361
|
'result',
|
|
345
362
|
{ text: 'Hello' },
|
|
346
|
-
{
|
|
347
|
-
execute: yourAIExecutor,
|
|
348
|
-
messages: [],
|
|
349
|
-
runner
|
|
350
|
-
}
|
|
363
|
+
{ execute: yourAIExecutor, messages: [], runner }
|
|
351
364
|
);
|
|
352
365
|
```
|
|
353
366
|
|
|
354
367
|
### Token Management
|
|
355
368
|
|
|
356
|
-
|
|
369
|
+
The prompt automatically trims old messages when the context window is full:
|
|
357
370
|
|
|
358
371
|
```typescript
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
// Custom token estimation
|
|
367
|
-
estimateTokens: (message) => {
|
|
368
|
-
return message.content.length / 4; // Rough estimate
|
|
369
|
-
}
|
|
370
|
-
};
|
|
372
|
+
const result = await prompt.get(
|
|
373
|
+
'result',
|
|
374
|
+
{ text: 'Query' },
|
|
375
|
+
{
|
|
376
|
+
execute: yourAIExecutor,
|
|
377
|
+
messages: conversationHistory, // Will be trimmed if needed
|
|
371
378
|
|
|
372
|
-
//
|
|
373
|
-
|
|
379
|
+
// Reserve this many tokens for the model's response
|
|
380
|
+
maxOutputTokens: 2048,
|
|
381
|
+
|
|
382
|
+
// Custom per-message token estimator
|
|
383
|
+
estimateUsage: (message) => ({
|
|
384
|
+
text: { input: Math.ceil((message.content as string).length / 4) }
|
|
385
|
+
}),
|
|
386
|
+
}
|
|
387
|
+
);
|
|
374
388
|
```
|
|
375
389
|
|
|
376
390
|
### Dynamic Reconfiguration
|
|
377
391
|
|
|
392
|
+
Use `reconfig` to adapt the prompt based on runtime statistics:
|
|
393
|
+
|
|
378
394
|
```typescript
|
|
379
395
|
const adaptivePrompt = new Prompt({
|
|
380
396
|
name: 'adaptive',
|
|
381
|
-
description: 'Adapts based on
|
|
397
|
+
description: 'Adapts based on runtime stats',
|
|
382
398
|
content: 'Solve: {{problem}}',
|
|
383
399
|
schema: z.object({ solution: z.string() }),
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
// If tools are failing, change strategy
|
|
400
|
+
|
|
401
|
+
reconfig: (stats, _ctx) => {
|
|
402
|
+
// If many tool errors occurred, switch to one-at-a-time mode
|
|
388
403
|
if (stats.toolCallErrors > 3) {
|
|
389
|
-
return {
|
|
390
|
-
config: { toolsOneAtATime: true },
|
|
391
|
-
maxIterations: 2
|
|
392
|
-
};
|
|
404
|
+
return { config: { toolsOneAtATime: true } };
|
|
393
405
|
}
|
|
394
406
|
return {};
|
|
395
|
-
}
|
|
407
|
+
},
|
|
396
408
|
});
|
|
397
409
|
```
|
|
398
410
|
|
|
411
|
+
## Model Capabilities
|
|
412
|
+
|
|
413
|
+
@aeye uses a capability system for automatic model selection. Models advertise their capabilities and the library selects the best match for each request:
|
|
414
|
+
|
|
415
|
+
| Capability | Description |
|
|
416
|
+
|------------|-------------|
|
|
417
|
+
| `chat` | Basic text completion |
|
|
418
|
+
| `streaming` | Real-time response streaming |
|
|
419
|
+
| `image` | Image generation |
|
|
420
|
+
| `vision` | Image understanding |
|
|
421
|
+
| `audio` | Text-to-speech |
|
|
422
|
+
| `hearing` | Speech-to-text |
|
|
423
|
+
| `embedding` | Text embeddings |
|
|
424
|
+
| `tools` | Function/tool calling |
|
|
425
|
+
| `json` | JSON output mode |
|
|
426
|
+
| `structured` | Structured outputs with schemas |
|
|
427
|
+
| `reasoning` | Extended chain-of-thought reasoning |
|
|
428
|
+
| `zdr` | Zero data retention |
|
|
429
|
+
|
|
399
430
|
## API Reference
|
|
400
431
|
|
|
401
432
|
### Prompt
|
|
402
433
|
|
|
403
434
|
```typescript
|
|
404
|
-
class Prompt<TContext, TMetadata, TName, TInput, TOutput, TTools
|
|
435
|
+
class Prompt<TContext, TMetadata, TName extends string, TInput extends object, TOutput extends object | string, TTools extends Tuple<ToolCompatible<TContext, TMetadata>>>
|
|
405
436
|
```
|
|
406
437
|
|
|
407
|
-
**Constructor Options:**
|
|
438
|
+
**Constructor Options (`PromptInput`):**
|
|
408
439
|
- `name: string` - Unique identifier
|
|
409
440
|
- `description: string` - Purpose description
|
|
410
|
-
- `content: string` - Handlebars template
|
|
411
|
-
- `input?:
|
|
412
|
-
- `schema?: ZodType |
|
|
413
|
-
- `
|
|
414
|
-
- `
|
|
415
|
-
- `
|
|
416
|
-
- `
|
|
417
|
-
- `
|
|
418
|
-
- `
|
|
419
|
-
- `
|
|
420
|
-
- `
|
|
421
|
-
- `
|
|
441
|
+
- `content: string` - Handlebars template for the system/user message
|
|
442
|
+
- `input?: (input: TInput, ctx: Context<TContext, TMetadata>) => Record<string, any>` - Maps raw input to template variables
|
|
443
|
+
- `schema?: ZodType<TOutput> | ((input: TInput | undefined, ctx: Context<TContext, TMetadata>) => ZodType<TOutput> | false)` - Output schema; `false` disables structured output
|
|
444
|
+
- `strict?: boolean` - Enforce strict Zod schema (default: `true`)
|
|
445
|
+
- `config?: Partial<Request> | ((input: TInput | undefined, ctx: Context<TContext, TMetadata>) => Partial<Request> | false)` - AI request overrides
|
|
446
|
+
- `tools?: TTools` - Tools the prompt may call
|
|
447
|
+
- `toolExecution?: 'sequential' | 'parallel' | 'immediate'` - Tool execution mode
|
|
448
|
+
- `toolRetries?: number` - Retry count for failed tools (default: 2)
|
|
449
|
+
- `toolIterations?: number` - Max tool-calling iterations (default: 3)
|
|
450
|
+
- `toolsMax?: number` - Max successful tool calls
|
|
451
|
+
- `outputRetries?: number` - Retry count for invalid structured outputs
|
|
452
|
+
- `metadata?: TMetadata` - Static metadata for this prompt
|
|
453
|
+
- `validate?: (output: TOutput, ctx: Context<TContext, TMetadata>) => void | Promise<void>` - Post-parse validation
|
|
454
|
+
- `applicable?: (ctx: Context<TContext, TMetadata>) => boolean | Promise<boolean>` - Availability check
|
|
422
455
|
|
|
423
456
|
**Methods:**
|
|
424
|
-
- `get(input,
|
|
425
|
-
- `run(input, ctx)
|
|
426
|
-
- `applicable(ctx)
|
|
457
|
+
- `get(mode: PromptGetType, input?: TInput, ctx?: Context<TContext, TMetadata>): PromptGet<...>` - Execute and return output in the specified mode
|
|
458
|
+
- `run(input?: TInput, ctx?: Context<TContext, TMetadata>): AsyncGenerator<PromptEvent<TOutput, TTools>, TOutput | undefined>` - Full streaming generator
|
|
459
|
+
- `applicable(ctx?: Context<TContext, TMetadata>): Promise<boolean>` - Check if prompt is usable
|
|
427
460
|
|
|
428
461
|
### Tool
|
|
429
462
|
|
|
430
463
|
```typescript
|
|
431
|
-
class Tool<TContext, TMetadata, TName, TParams, TOutput, TRefs
|
|
464
|
+
class Tool<TContext, TMetadata, TName extends string, TParams extends object, TOutput, TRefs extends Tuple<ComponentCompatible<TContext, TMetadata>>>
|
|
432
465
|
```
|
|
433
466
|
|
|
434
|
-
**Constructor Options:**
|
|
435
|
-
- `name: string` - Unique identifier
|
|
436
|
-
- `description: string` -
|
|
437
|
-
- `instructions?: string` - Handlebars usage instructions
|
|
438
|
-
- `schema: ZodType |
|
|
439
|
-
- `
|
|
440
|
-
- `
|
|
441
|
-
- `
|
|
442
|
-
- `
|
|
467
|
+
**Constructor Options (`ToolInput`):**
|
|
468
|
+
- `name: string` - Unique identifier (shown to the AI model)
|
|
469
|
+
- `description: string` - What the tool does (shown to the AI model)
|
|
470
|
+
- `instructions?: string` - Handlebars template for usage instructions
|
|
471
|
+
- `schema: ZodType<TParams> | ((ctx: Context<TContext, TMetadata>) => ZodType<TParams> | undefined)` - Input parameter schema
|
|
472
|
+
- `strict?: boolean` - Enforce strict Zod schema (default: `true`)
|
|
473
|
+
- `refs?: TRefs` - Referenced components
|
|
474
|
+
- `call: (input: TParams, refs: TRefs, ctx: Context<TContext, TMetadata>) => TOutput` - Implementation
|
|
475
|
+
- `validate?: (input: TParams, ctx: Context<TContext, TMetadata>) => void | Promise<void>` - Post-parse validation
|
|
476
|
+
- `applicable?: (ctx: Context<TContext, TMetadata>) => boolean | Promise<boolean>` - Availability check
|
|
443
477
|
|
|
444
478
|
**Methods:**
|
|
445
|
-
- `run(input, ctx)` - Execute the tool
|
|
446
|
-
- `compile(ctx)
|
|
447
|
-
- `
|
|
448
|
-
- `applicable(ctx)` - Check if tool can run
|
|
479
|
+
- `run(input?: TParams, ctx?: Context<TContext, TMetadata>): TOutput` - Execute the tool directly
|
|
480
|
+
- `compile(ctx: Context<TContext, TMetadata>): Promise<readonly [string, ToolDefinition] | undefined>` - Generate AI tool definition
|
|
481
|
+
- `applicable(ctx?: Context<TContext, TMetadata>): Promise<boolean>` - Check if tool is usable
|
|
449
482
|
|
|
450
483
|
### Agent
|
|
451
484
|
|
|
452
485
|
```typescript
|
|
453
|
-
class Agent<TContext, TMetadata, TName, TInput, TOutput, TRefs
|
|
486
|
+
class Agent<TContext, TMetadata, TName extends string, TInput extends object, TOutput, TRefs extends Tuple<ComponentCompatible<TContext, TMetadata>>>
|
|
454
487
|
```
|
|
455
488
|
|
|
456
|
-
**Constructor Options:**
|
|
489
|
+
**Constructor Options (`AgentInput`):**
|
|
457
490
|
- `name: string` - Unique identifier
|
|
458
491
|
- `description: string` - Purpose description
|
|
459
|
-
- `refs
|
|
460
|
-
- `call:
|
|
461
|
-
- `applicable?:
|
|
492
|
+
- `refs: TRefs` - Components this agent uses
|
|
493
|
+
- `call: (input: TInput, refs: TRefs, ctx: Context<TContext, TMetadata>) => TOutput` - Implementation
|
|
494
|
+
- `applicable?: (ctx: Context<TContext, TMetadata>) => boolean | Promise<boolean>` - Availability check
|
|
462
495
|
|
|
463
496
|
**Methods:**
|
|
464
|
-
- `run(input, ctx)` - Execute the agent
|
|
465
|
-
- `applicable(ctx)
|
|
497
|
+
- `run(input?: TInput, ctx?: Context<TContext, TMetadata>): TOutput` - Execute the agent
|
|
498
|
+
- `applicable(ctx?: Context<TContext, TMetadata>): Promise<boolean>` - Check if agent is usable
|
|
466
499
|
|
|
467
500
|
## Context Structure
|
|
468
501
|
|
|
502
|
+
The `Context<TContext, TMetadata>` type combines your custom context with the core execution context:
|
|
503
|
+
|
|
469
504
|
```typescript
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
//
|
|
505
|
+
// Core fields available in every context
|
|
506
|
+
interface CoreContextFields {
|
|
507
|
+
// AI executor for non-streaming requests
|
|
508
|
+
execute?: (request: Request, ctx: TContext, metadata?: TMetadata, signal?: AbortSignal) => Promise<Response>;
|
|
509
|
+
// AI streamer for streaming requests
|
|
510
|
+
stream?: (request: Request, ctx: TContext, metadata?: TMetadata, signal?: AbortSignal) => AsyncGenerator<Chunk, Response>;
|
|
511
|
+
|
|
512
|
+
// Conversation history passed to the model
|
|
513
|
+
messages?: Message[];
|
|
514
|
+
|
|
515
|
+
// Reserve N tokens for the model's output (used for context trimming)
|
|
516
|
+
maxOutputTokens?: number;
|
|
517
|
+
// Override context window size (used for context trimming)
|
|
518
|
+
contextWindow?: number;
|
|
519
|
+
|
|
520
|
+
// Custom per-message usage estimator
|
|
521
|
+
estimateUsage?: (message: Message) => Usage | undefined;
|
|
522
|
+
|
|
523
|
+
// Number of output-format retries (default: 2)
|
|
524
|
+
outputRetries?: number;
|
|
525
|
+
// Number of context-trimming retries (default: 1)
|
|
526
|
+
forgetRetries?: number;
|
|
527
|
+
|
|
528
|
+
// Abort signal for cancellation
|
|
483
529
|
signal?: AbortSignal;
|
|
530
|
+
// Event runner for tracking component lifecycle
|
|
484
531
|
runner?: Events;
|
|
485
|
-
|
|
486
|
-
// User context (TContext) spreads here
|
|
487
|
-
[key: string]: any;
|
|
488
532
|
}
|
|
489
533
|
```
|
|
490
534
|
|
|
@@ -493,13 +537,17 @@ interface Context<TContext, TMetadata> {
|
|
|
493
537
|
```typescript
|
|
494
538
|
interface Request {
|
|
495
539
|
messages: Message[];
|
|
496
|
-
temperature?: number;
|
|
497
|
-
topP?: number;
|
|
540
|
+
temperature?: number; // 0.0–2.0
|
|
541
|
+
topP?: number; // 0.0–1.0
|
|
498
542
|
maxTokens?: number;
|
|
499
543
|
stop?: string | string[];
|
|
500
544
|
tools?: ToolDefinition[];
|
|
501
|
-
toolChoice?: 'auto' | 'required' | 'none' | { tool: string };
|
|
502
|
-
responseFormat?: 'text' | 'json' |
|
|
545
|
+
toolChoice?: 'auto' | 'required' | 'none' | { type: 'tool'; name: string };
|
|
546
|
+
responseFormat?: 'text' | 'json' | { type: 'json_schema'; schema: object; name: string; strict?: boolean };
|
|
547
|
+
reason?: { effort?: 'low' | 'medium' | 'high'; maxTokens?: number };
|
|
548
|
+
frequencyPenalty?: number;
|
|
549
|
+
presencePenalty?: number;
|
|
550
|
+
seed?: number;
|
|
503
551
|
// ... more options
|
|
504
552
|
}
|
|
505
553
|
```
|
|
@@ -507,15 +555,13 @@ interface Request {
|
|
|
507
555
|
## Best Practices
|
|
508
556
|
|
|
509
557
|
1. **Type Safety** - Use TypeScript generics for context and metadata
|
|
510
|
-
2. **Schema Validation** - Use Zod for robust input/output validation
|
|
511
|
-
3. **
|
|
512
|
-
4. **
|
|
513
|
-
5. **
|
|
514
|
-
6. **
|
|
515
|
-
7. **
|
|
516
|
-
8. **
|
|
517
|
-
9. **Streaming** - Use streaming for better UX with long responses
|
|
518
|
-
10. **Validation** - Use `validate` hooks for business logic validation
|
|
558
|
+
2. **Schema Validation** - Use Zod for robust input/output validation
|
|
559
|
+
3. **Token Management** - Supply `estimateUsage` for accurate context trimming
|
|
560
|
+
4. **Tool Organization** - Group related tools and use agents to orchestrate them
|
|
561
|
+
5. **Testing** - Unit test tools and prompts independently
|
|
562
|
+
6. **Context Minimization** - Pass only necessary data in context
|
|
563
|
+
7. **Streaming** - Use streaming for better UX with long responses
|
|
564
|
+
8. **Validation** - Use `validate` hooks for business-logic checks after schema parsing
|
|
519
565
|
|
|
520
566
|
## Examples
|
|
521
567
|
|
|
@@ -532,24 +578,31 @@ See the `src/__tests__` directory for comprehensive examples:
|
|
|
532
578
|
Full type inference across component hierarchies:
|
|
533
579
|
|
|
534
580
|
```typescript
|
|
535
|
-
|
|
581
|
+
import { Prompt, Tool } from '@aeye/core';
|
|
582
|
+
import z from 'zod';
|
|
583
|
+
|
|
584
|
+
// Output type is inferred from the schema
|
|
536
585
|
const prompt = new Prompt({
|
|
537
586
|
name: 'example',
|
|
538
|
-
|
|
539
|
-
|
|
587
|
+
description: 'Example prompt',
|
|
588
|
+
content: 'Say hello to {{name}}.',
|
|
589
|
+
input: (input: { name: string }) => ({ name: input.name }),
|
|
590
|
+
schema: z.object({ greeting: z.string() }),
|
|
540
591
|
});
|
|
541
592
|
|
|
542
|
-
//
|
|
543
|
-
const
|
|
593
|
+
// result is typed as { greeting: string } | undefined
|
|
594
|
+
const result = await prompt.get('result', { name: 'World' }, ctx);
|
|
595
|
+
console.log(result?.greeting);
|
|
544
596
|
|
|
545
|
-
// Tool parameters are type-safe
|
|
546
|
-
const
|
|
547
|
-
name: '
|
|
597
|
+
// Tool call parameters are type-safe
|
|
598
|
+
const mathTool = new Tool({
|
|
599
|
+
name: 'add',
|
|
600
|
+
description: 'Adds two numbers',
|
|
548
601
|
schema: z.object({ a: z.number(), b: z.number() }),
|
|
549
|
-
call: (input,
|
|
550
|
-
// input is typed as { a: number
|
|
602
|
+
call: (input, _refs, _ctx) => {
|
|
603
|
+
// input is typed as { a: number; b: number }
|
|
551
604
|
return input.a + input.b;
|
|
552
|
-
}
|
|
605
|
+
},
|
|
553
606
|
});
|
|
554
607
|
```
|
|
555
608
|
|
|
@@ -563,6 +616,7 @@ GPL-3.0 © [ClickerMonkey](https://github.com/ClickerMonkey)
|
|
|
563
616
|
|
|
564
617
|
## Links
|
|
565
618
|
|
|
619
|
+
- [Root README](../../README.md)
|
|
620
|
+
- [@aeye/ai README](../ai/README.md)
|
|
566
621
|
- [GitHub Repository](https://github.com/ClickerMonkey/aeye)
|
|
567
|
-
- [Documentation](https://github.com/ClickerMonkey/aeye/tree/main/packages/core)
|
|
568
622
|
- [Issue Tracker](https://github.com/ClickerMonkey/aeye/issues)
|