@altsafe/aidirector 1.0.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +77 -297
- package/dist/index.d.mts +252 -4
- package/dist/index.d.ts +252 -4
- package/dist/index.js +4 -4
- package/dist/index.mjs +4 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,27 +1,12 @@
|
|
|
1
|
-
# @altsafe/aidirector
|
|
1
|
+
# @altsafe/aidirector - Client SDK
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
[](https://www.npmjs.com/package/@altsafe/aidirector)
|
|
6
|
-
[](https://www.typescriptlang.org/)
|
|
7
|
-
[](https://opensource.org/licenses/MIT)
|
|
8
|
-
|
|
9
|
-
## Features
|
|
10
|
-
|
|
11
|
-
- 🔐 **HMAC Authentication** — Secure request signing (Node.js & browser)
|
|
12
|
-
- ⚡ **10-minute Timeout** — Handles long-running AI requests
|
|
13
|
-
- 🔄 **Automatic Retries** — Exponential backoff with configurable limits
|
|
14
|
-
- 📦 **Structured Errors** — Type-safe error handling
|
|
15
|
-
- 🌊 **Streaming Support** — Real-time response streaming
|
|
16
|
-
- 🎯 **Full TypeScript** — Complete type definitions
|
|
3
|
+
Production-grade TypeScript SDK for the AI Director API gateway.
|
|
17
4
|
|
|
18
5
|
## Installation
|
|
19
6
|
|
|
20
7
|
```bash
|
|
21
8
|
npm install @altsafe/aidirector
|
|
22
9
|
# or
|
|
23
|
-
bun add @altsafe/aidirector
|
|
24
|
-
# or
|
|
25
10
|
pnpm add @altsafe/aidirector
|
|
26
11
|
```
|
|
27
12
|
|
|
@@ -32,330 +17,125 @@ import { AIDirector } from '@altsafe/aidirector';
|
|
|
32
17
|
|
|
33
18
|
const client = new AIDirector({
|
|
34
19
|
secretKey: process.env.AIDIRECTOR_SECRET_KEY!,
|
|
35
|
-
baseUrl: 'https://your-
|
|
20
|
+
baseUrl: 'https://your-instance.vercel.app',
|
|
36
21
|
});
|
|
37
22
|
|
|
23
|
+
// Generate content
|
|
38
24
|
const result = await client.generate({
|
|
39
|
-
chainId: '
|
|
40
|
-
prompt: 'Generate 5 user profiles
|
|
41
|
-
schema: { name: 'string', email: 'string' },
|
|
25
|
+
chainId: 'my-chain',
|
|
26
|
+
prompt: 'Generate 5 user profiles',
|
|
42
27
|
});
|
|
43
28
|
|
|
44
29
|
if (result.success) {
|
|
45
|
-
console.log(
|
|
46
|
-
console.log('Model:', result.meta.modelUsed);
|
|
47
|
-
console.log('Cached:', result.meta.cached);
|
|
30
|
+
console.log(result.data.valid);
|
|
48
31
|
}
|
|
49
32
|
```
|
|
50
33
|
|
|
51
|
-
##
|
|
52
|
-
|
|
53
|
-
### Next.js Server Actions
|
|
54
|
-
|
|
55
|
-
```typescript
|
|
56
|
-
'use server';
|
|
57
|
-
|
|
58
|
-
import { AIDirector } from '@altsafe/aidirector';
|
|
59
|
-
|
|
60
|
-
const client = new AIDirector({
|
|
61
|
-
secretKey: process.env.AIDIRECTOR_SECRET_KEY!,
|
|
62
|
-
});
|
|
34
|
+
## Features
|
|
63
35
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
```
|
|
36
|
+
- 🔐 **HMAC Authentication** - Secure request signing
|
|
37
|
+
- ⚡ **3-Step Architecture** - Token → Worker → Complete (minimizes costs)
|
|
38
|
+
- 📎 **File Attachments** - Upload and process documents
|
|
39
|
+
- 🔄 **Automatic Retries** - Exponential backoff on failures
|
|
40
|
+
- 💾 **Smart Caching** - Hybrid Redis caching with 7-day TTL
|
|
41
|
+
- 🎯 **TypeScript** - Full type safety
|
|
71
42
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
```typescript
|
|
75
|
-
// app/api/generate/route.ts
|
|
76
|
-
import { NextResponse } from 'next/server';
|
|
77
|
-
import { AIDirector } from '@altsafe/aidirector';
|
|
78
|
-
|
|
79
|
-
const client = new AIDirector({
|
|
80
|
-
secretKey: process.env.AIDIRECTOR_SECRET_KEY!,
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
export async function POST(request: Request) {
|
|
84
|
-
const { prompt, chainId } = await request.json();
|
|
85
|
-
const result = await client.generate({ chainId, prompt });
|
|
86
|
-
return NextResponse.json(result);
|
|
87
|
-
}
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
### Streaming
|
|
43
|
+
## Streaming (Recommended for Large Responses)
|
|
91
44
|
|
|
92
45
|
```typescript
|
|
93
46
|
await client.generateStream(
|
|
94
47
|
{
|
|
95
48
|
chainId: 'my-chain',
|
|
96
|
-
prompt: '
|
|
49
|
+
prompt: 'Generate 100 product descriptions',
|
|
97
50
|
},
|
|
98
51
|
{
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
52
|
+
onObject: (obj, index) => {
|
|
53
|
+
console.log(`Object ${index}:`, obj);
|
|
54
|
+
renderToUI(obj); // Render immediately!
|
|
55
|
+
},
|
|
56
|
+
onComplete: (result) => {
|
|
57
|
+
console.log(`Done! ${result.objectCount} objects`);
|
|
58
|
+
},
|
|
59
|
+
onError: (error) => {
|
|
60
|
+
console.error('Stream failed:', error);
|
|
61
|
+
},
|
|
102
62
|
}
|
|
103
63
|
);
|
|
104
64
|
```
|
|
105
65
|
|
|
106
|
-
|
|
66
|
+
## File Attachments
|
|
107
67
|
|
|
108
68
|
```typescript
|
|
109
|
-
import
|
|
110
|
-
AIDirector,
|
|
111
|
-
TimeoutError,
|
|
112
|
-
RateLimitError,
|
|
113
|
-
ValidationError,
|
|
114
|
-
isAIDirectorError,
|
|
115
|
-
isRetryableError,
|
|
116
|
-
} from '@altsafe/aidirector';
|
|
69
|
+
import fs from 'fs';
|
|
117
70
|
|
|
118
|
-
|
|
119
|
-
const result = await client.generate({ chainId, prompt });
|
|
120
|
-
} catch (error) {
|
|
121
|
-
if (error instanceof TimeoutError) {
|
|
122
|
-
console.log('Request timed out after', error.timeoutMs, 'ms');
|
|
123
|
-
} else if (error instanceof RateLimitError) {
|
|
124
|
-
console.log('Rate limited, retry after', error.retryAfterMs, 'ms');
|
|
125
|
-
} else if (error instanceof ValidationError) {
|
|
126
|
-
console.log('Validation failed:', error.validationErrors);
|
|
127
|
-
} else if (isAIDirectorError(error)) {
|
|
128
|
-
console.log('AI Director error:', error.code, error.message);
|
|
129
|
-
|
|
130
|
-
// Check if we should retry
|
|
131
|
-
if (isRetryableError(error)) {
|
|
132
|
-
console.log('This error is retryable');
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
```
|
|
71
|
+
const fileBuffer = fs.readFileSync('report.pdf');
|
|
137
72
|
|
|
138
|
-
|
|
73
|
+
const result = await client.generate({
|
|
74
|
+
chainId: 'document-analysis',
|
|
75
|
+
prompt: 'Summarize this document',
|
|
76
|
+
files: [{
|
|
77
|
+
data: fileBuffer.toString('base64'),
|
|
78
|
+
filename: 'report.pdf',
|
|
79
|
+
mimeType: 'application/pdf',
|
|
80
|
+
}],
|
|
81
|
+
});
|
|
82
|
+
```
|
|
139
83
|
|
|
140
|
-
|
|
84
|
+
## Configuration
|
|
141
85
|
|
|
142
86
|
| Option | Type | Default | Description |
|
|
143
87
|
|--------|------|---------|-------------|
|
|
144
|
-
| `secretKey` | `string` |
|
|
145
|
-
| `baseUrl` | `string` | `localhost:3000` | API base URL |
|
|
146
|
-
| `timeout` | `number` | `600000` | Request timeout (
|
|
88
|
+
| `secretKey` | `string` | **required** | Your API key (`aid_sk_...`) |
|
|
89
|
+
| `baseUrl` | `string` | `http://localhost:3000` | API base URL |
|
|
90
|
+
| `timeout` | `number` | `600000` | Request timeout (10 min) |
|
|
147
91
|
| `maxRetries` | `number` | `3` | Max retry attempts |
|
|
148
|
-
| `debug` | `boolean` | `false` | Enable
|
|
149
|
-
|
|
150
|
-
### `client.generate(options)`
|
|
92
|
+
| `debug` | `boolean` | `false` | Enable debug logging |
|
|
93
|
+
| `useOptimized` | `boolean` | `true` | Use 3-step Worker flow |
|
|
151
94
|
|
|
152
|
-
|
|
95
|
+
## API Methods
|
|
153
96
|
|
|
154
|
-
|
|
|
155
|
-
|
|
156
|
-
| `
|
|
157
|
-
| `
|
|
158
|
-
| `
|
|
159
|
-
| `
|
|
160
|
-
| `options
|
|
161
|
-
| `
|
|
162
|
-
| `options.topP` | `number` | No | Nucleus sampling (0-1) |
|
|
163
|
-
| `options.topK` | `number` | No | Top-K sampling |
|
|
164
|
-
| `options.systemPrompt` | `string` | No | System prompt to prepend |
|
|
97
|
+
| Method | Description |
|
|
98
|
+
|--------|-------------|
|
|
99
|
+
| `generate(options)` | Generate content with fallback chain |
|
|
100
|
+
| `generateStream(options, callbacks)` | Stream JSON objects in real-time |
|
|
101
|
+
| `listModels()` | List available AI models |
|
|
102
|
+
| `listChains()` | List your fallback chains |
|
|
103
|
+
| `getUsageStats(options)` | Get usage statistics |
|
|
104
|
+
| `healthCheck()` | Check API health |
|
|
165
105
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
```typescript
|
|
169
|
-
interface GenerateResult {
|
|
170
|
-
success: boolean;
|
|
171
|
-
data: {
|
|
172
|
-
valid: unknown[]; // Schema-compliant objects
|
|
173
|
-
invalid: unknown[]; // Failed validation but parsed
|
|
174
|
-
rawContent?: string; // Raw AI response
|
|
175
|
-
};
|
|
176
|
-
meta: {
|
|
177
|
-
cached: boolean;
|
|
178
|
-
modelUsed: string;
|
|
179
|
-
tokensUsed: { input: number; output: number };
|
|
180
|
-
latencyMs: number;
|
|
181
|
-
attemptedModels: string[];
|
|
182
|
-
finishReason?: string;
|
|
183
|
-
};
|
|
184
|
-
error?: {
|
|
185
|
-
code: string;
|
|
186
|
-
message: string;
|
|
187
|
-
retryable?: boolean;
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
### `client.generateStream(options, callbacks)`
|
|
193
|
-
|
|
194
|
-
Stream responses in real-time.
|
|
195
|
-
|
|
196
|
-
```typescript
|
|
197
|
-
interface StreamCallbacks {
|
|
198
|
-
onChunk?: (chunk: string) => void;
|
|
199
|
-
onComplete?: (result: GenerateResult) => void;
|
|
200
|
-
onError?: (error: Error) => void;
|
|
201
|
-
onProgress?: (progress: StreamProgress) => void;
|
|
202
|
-
}
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
### `client.listModels()`
|
|
206
|
-
|
|
207
|
-
List all available AI models.
|
|
208
|
-
|
|
209
|
-
```typescript
|
|
210
|
-
const models = await client.listModels();
|
|
211
|
-
// Returns: ModelInfo[]
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
### `client.listChains()`
|
|
215
|
-
|
|
216
|
-
Get your fallback chains (requires session auth).
|
|
217
|
-
|
|
218
|
-
```typescript
|
|
219
|
-
const chains = await client.listChains();
|
|
220
|
-
// Returns: ChainInfo[]
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
### `client.getUsage(options?)`
|
|
224
|
-
|
|
225
|
-
Get usage statistics for your account.
|
|
226
|
-
|
|
227
|
-
```typescript
|
|
228
|
-
const usage = await client.getUsage({
|
|
229
|
-
startDate: new Date('2024-01-01'),
|
|
230
|
-
endDate: new Date(),
|
|
231
|
-
});
|
|
232
|
-
// Returns: UsageStats
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
### `client.health()`
|
|
236
|
-
|
|
237
|
-
Health check to verify API connection.
|
|
238
|
-
|
|
239
|
-
```typescript
|
|
240
|
-
const { ok, latencyMs, version } = await client.health();
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
### `client.withConfig(overrides)`
|
|
244
|
-
|
|
245
|
-
Create a new client with different configuration.
|
|
246
|
-
|
|
247
|
-
```typescript
|
|
248
|
-
const debugClient = client.withConfig({ debug: true });
|
|
249
|
-
const fastClient = client.withConfig({ timeout: 30000 });
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
## Error Classes
|
|
253
|
-
|
|
254
|
-
| Error | Code | Retryable | Properties | Description |
|
|
255
|
-
|-------|------|-----------|------------|-------------|
|
|
256
|
-
| `ConfigurationError` | `CONFIGURATION_ERROR` | No | — | Invalid client setup |
|
|
257
|
-
| `AuthenticationError` | `AUTH_ERROR` | No | `statusCode` | Invalid credentials |
|
|
258
|
-
| `RateLimitError` | `RATE_LIMITED` | Yes | `retryAfterMs` | Too many requests |
|
|
259
|
-
| `TimeoutError` | `TIMEOUT` | Yes | `timeoutMs` | Request timed out |
|
|
260
|
-
| `NetworkError` | `NETWORK_ERROR` | Yes | `originalError` | Connection failed |
|
|
261
|
-
| `ChainExecutionError` | `CHAIN_FAILED` | No | `attemptedModels` | All models failed |
|
|
262
|
-
| `ValidationError` | `VALIDATION_ERROR` | No | `validationErrors` | Schema validation failed |
|
|
263
|
-
| `ServerError` | `SERVER_ERROR` | Yes | `statusCode` | Internal server error |
|
|
264
|
-
|
|
265
|
-
### Error Helpers
|
|
266
|
-
|
|
267
|
-
```typescript
|
|
268
|
-
import { isAIDirectorError, isRetryableError } from '@altsafe/aidirector';
|
|
269
|
-
|
|
270
|
-
// Check if error is from AI Director
|
|
271
|
-
if (isAIDirectorError(error)) {
|
|
272
|
-
console.log(error.code, error.message);
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// Check if error should be retried
|
|
276
|
-
if (isRetryableError(error)) {
|
|
277
|
-
// Implement retry logic
|
|
278
|
-
}
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
## Advanced: HMAC Utilities
|
|
282
|
-
|
|
283
|
-
For custom integrations, you can use the HMAC utilities directly:
|
|
106
|
+
## Error Handling
|
|
284
107
|
|
|
285
108
|
```typescript
|
|
286
109
|
import {
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
110
|
+
RateLimitError,
|
|
111
|
+
TimeoutError,
|
|
112
|
+
AuthenticationError,
|
|
290
113
|
} from '@altsafe/aidirector';
|
|
291
114
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
115
|
+
try {
|
|
116
|
+
const result = await client.generate({ ... });
|
|
117
|
+
} catch (error) {
|
|
118
|
+
if (error instanceof RateLimitError) {
|
|
119
|
+
console.log(`Retry after ${error.retryAfterMs}ms`);
|
|
120
|
+
} else if (error instanceof TimeoutError) {
|
|
121
|
+
console.log('Request timed out');
|
|
122
|
+
} else if (error instanceof AuthenticationError) {
|
|
123
|
+
console.log('Invalid API key');
|
|
124
|
+
}
|
|
295
125
|
}
|
|
296
|
-
|
|
297
|
-
// Get key prefix for headers
|
|
298
|
-
const prefix = getKeyPrefix(secretKey); // "aid_sk_xxxx"
|
|
299
|
-
|
|
300
|
-
// Generate signature for custom requests
|
|
301
|
-
const signature = await generateSignature(
|
|
302
|
-
secretKey,
|
|
303
|
-
'POST',
|
|
304
|
-
'/api/v1/generate',
|
|
305
|
-
JSON.stringify(body),
|
|
306
|
-
Date.now()
|
|
307
|
-
);
|
|
308
126
|
```
|
|
309
127
|
|
|
310
|
-
##
|
|
311
|
-
|
|
312
|
-
All types are exported for your convenience:
|
|
128
|
+
## Pricing
|
|
313
129
|
|
|
314
|
-
|
|
315
|
-
import type {
|
|
316
|
-
// Configuration
|
|
317
|
-
AIDirectorConfig,
|
|
318
|
-
|
|
319
|
-
// Generation
|
|
320
|
-
GenerateOptions,
|
|
321
|
-
GenerateResult,
|
|
322
|
-
GenerateData,
|
|
323
|
-
GenerateMeta,
|
|
324
|
-
GenerateError,
|
|
325
|
-
GenerationParameters,
|
|
326
|
-
TokenUsage,
|
|
327
|
-
|
|
328
|
-
// Streaming
|
|
329
|
-
StreamCallbacks,
|
|
330
|
-
StreamProgress,
|
|
331
|
-
|
|
332
|
-
// Chains & Models
|
|
333
|
-
ChainInfo,
|
|
334
|
-
ChainStep,
|
|
335
|
-
ModelInfo,
|
|
336
|
-
|
|
337
|
-
// Usage & Health
|
|
338
|
-
UsageStats,
|
|
339
|
-
HealthResult,
|
|
340
|
-
} from '@altsafe/aidirector';
|
|
341
|
-
```
|
|
130
|
+
BYOK (Bring Your Own Key) - You pay for AI costs directly to providers.
|
|
342
131
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
- ✅ Angular Universal (SSR)
|
|
350
|
-
- ✅ Edge Functions (Vercel, Cloudflare)
|
|
351
|
-
- ❌ Client-side JavaScript
|
|
352
|
-
|
|
353
|
-
Store your secret key in environment variables:
|
|
354
|
-
|
|
355
|
-
```bash
|
|
356
|
-
AIDIRECTOR_SECRET_KEY=aid_sk_your_secret_key_here
|
|
357
|
-
```
|
|
132
|
+
| Tier | Price | Requests | Overage |
|
|
133
|
+
|------|-------|----------|---------|
|
|
134
|
+
| Free | $0 | 1K | Blocked |
|
|
135
|
+
| Starter | $9 | 25K | $0.50/1K |
|
|
136
|
+
| Pro | $29 | 100K | $0.40/1K |
|
|
137
|
+
| Scale | $79 | 500K | $0.30/1K |
|
|
358
138
|
|
|
359
139
|
## License
|
|
360
140
|
|
|
361
|
-
MIT
|
|
141
|
+
MIT
|
package/dist/index.d.mts
CHANGED
|
@@ -58,11 +58,64 @@ interface GenerateOptions {
|
|
|
58
58
|
* Override the client timeout for this request
|
|
59
59
|
*/
|
|
60
60
|
timeout?: number;
|
|
61
|
+
/**
|
|
62
|
+
* Skip the cache for this request.
|
|
63
|
+
* Useful for prompts that should always generate fresh responses.
|
|
64
|
+
* @default false
|
|
65
|
+
*/
|
|
66
|
+
noCache?: boolean;
|
|
67
|
+
/**
|
|
68
|
+
* File attachments to include with the request.
|
|
69
|
+
* The server automatically handles:
|
|
70
|
+
* - File type detection (magic bytes)
|
|
71
|
+
* - Model compatibility checking
|
|
72
|
+
* - Automatic conversion (e.g., DOCX → PDF)
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```typescript
|
|
76
|
+
* await client.generate({
|
|
77
|
+
* chainId: 'my-chain',
|
|
78
|
+
* prompt: 'Summarize this document',
|
|
79
|
+
* files: [{
|
|
80
|
+
* data: documentBuffer.toString('base64'),
|
|
81
|
+
* filename: 'report.pdf',
|
|
82
|
+
* mimeType: 'application/pdf',
|
|
83
|
+
* }],
|
|
84
|
+
* });
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
files?: FileAttachment[];
|
|
88
|
+
/**
|
|
89
|
+
* Use the optimized 3-step generation flow.
|
|
90
|
+
* This minimizes Vercel compute costs by calling the Cloudflare Worker directly.
|
|
91
|
+
* Set to false to force legacy single-call mode.
|
|
92
|
+
* @default true
|
|
93
|
+
*/
|
|
94
|
+
useOptimized?: boolean;
|
|
61
95
|
/**
|
|
62
96
|
* Generation parameters
|
|
63
97
|
*/
|
|
64
98
|
options?: GenerationParameters;
|
|
65
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* File attachment for generate requests.
|
|
102
|
+
* Pass files and the server handles detection/conversion automatically.
|
|
103
|
+
*/
|
|
104
|
+
interface FileAttachment {
|
|
105
|
+
/**
|
|
106
|
+
* File content as base64-encoded string
|
|
107
|
+
*/
|
|
108
|
+
data: string;
|
|
109
|
+
/**
|
|
110
|
+
* Original filename (optional, helps with type detection)
|
|
111
|
+
*/
|
|
112
|
+
filename?: string;
|
|
113
|
+
/**
|
|
114
|
+
* Claimed MIME type (optional, will be verified by server)
|
|
115
|
+
* If omitted, server will auto-detect from magic bytes
|
|
116
|
+
*/
|
|
117
|
+
mimeType?: string;
|
|
118
|
+
}
|
|
66
119
|
/**
|
|
67
120
|
* AI generation parameters
|
|
68
121
|
*/
|
|
@@ -195,13 +248,22 @@ interface GenerateError {
|
|
|
195
248
|
*/
|
|
196
249
|
interface StreamCallbacks {
|
|
197
250
|
/**
|
|
198
|
-
* Called for each text chunk received
|
|
251
|
+
* Called for each text chunk received (legacy/backwards compatible)
|
|
199
252
|
*/
|
|
200
253
|
onChunk?: (chunk: string) => void;
|
|
201
254
|
/**
|
|
202
|
-
* Called
|
|
255
|
+
* Called for each VALID JSON object parsed from stream
|
|
256
|
+
* This is the preferred callback for JSON streaming - each object is complete and parseable
|
|
203
257
|
*/
|
|
204
|
-
|
|
258
|
+
onObject?: (object: unknown, index: number) => void;
|
|
259
|
+
/**
|
|
260
|
+
* Called when a JSON array starts in the stream
|
|
261
|
+
*/
|
|
262
|
+
onArrayStart?: () => void;
|
|
263
|
+
/**
|
|
264
|
+
* Called when streaming is complete with full result
|
|
265
|
+
*/
|
|
266
|
+
onComplete?: (result: StreamCompleteResult) => void;
|
|
205
267
|
/**
|
|
206
268
|
* Called if an error occurs
|
|
207
269
|
*/
|
|
@@ -211,6 +273,30 @@ interface StreamCallbacks {
|
|
|
211
273
|
*/
|
|
212
274
|
onProgress?: (progress: StreamProgress) => void;
|
|
213
275
|
}
|
|
276
|
+
/**
|
|
277
|
+
* Result passed to onComplete callback during streaming
|
|
278
|
+
*/
|
|
279
|
+
interface StreamCompleteResult {
|
|
280
|
+
/**
|
|
281
|
+
* All collected objects from the stream
|
|
282
|
+
*/
|
|
283
|
+
objects: unknown[];
|
|
284
|
+
/**
|
|
285
|
+
* Total number of objects parsed
|
|
286
|
+
*/
|
|
287
|
+
objectCount: number;
|
|
288
|
+
/**
|
|
289
|
+
* Token usage for the request
|
|
290
|
+
*/
|
|
291
|
+
tokensUsed?: {
|
|
292
|
+
input: number;
|
|
293
|
+
output: number;
|
|
294
|
+
};
|
|
295
|
+
/**
|
|
296
|
+
* Whether the response was cached at stream end
|
|
297
|
+
*/
|
|
298
|
+
cached?: boolean;
|
|
299
|
+
}
|
|
214
300
|
/**
|
|
215
301
|
* Streaming progress information
|
|
216
302
|
*/
|
|
@@ -408,6 +494,89 @@ interface HealthResult {
|
|
|
408
494
|
*/
|
|
409
495
|
timestamp?: string;
|
|
410
496
|
}
|
|
497
|
+
/**
|
|
498
|
+
* Detailed health check result with component status
|
|
499
|
+
*/
|
|
500
|
+
interface DetailedHealthResult {
|
|
501
|
+
/**
|
|
502
|
+
* Overall status
|
|
503
|
+
*/
|
|
504
|
+
status: 'healthy' | 'degraded' | 'unhealthy';
|
|
505
|
+
/**
|
|
506
|
+
* Server timestamp
|
|
507
|
+
*/
|
|
508
|
+
timestamp: string;
|
|
509
|
+
/**
|
|
510
|
+
* Server uptime in seconds
|
|
511
|
+
*/
|
|
512
|
+
uptime: number;
|
|
513
|
+
/**
|
|
514
|
+
* Component status
|
|
515
|
+
*/
|
|
516
|
+
components: {
|
|
517
|
+
database: {
|
|
518
|
+
status: string;
|
|
519
|
+
latencyMs: number;
|
|
520
|
+
message?: string;
|
|
521
|
+
};
|
|
522
|
+
redis: {
|
|
523
|
+
status: string;
|
|
524
|
+
latencyMs: number;
|
|
525
|
+
message?: string;
|
|
526
|
+
};
|
|
527
|
+
gemini: {
|
|
528
|
+
status: string;
|
|
529
|
+
errorRate: number;
|
|
530
|
+
recentErrors: number;
|
|
531
|
+
};
|
|
532
|
+
openrouter: {
|
|
533
|
+
status: string;
|
|
534
|
+
errorRate: number;
|
|
535
|
+
recentErrors: number;
|
|
536
|
+
};
|
|
537
|
+
};
|
|
538
|
+
/**
|
|
539
|
+
* 24h metrics
|
|
540
|
+
*/
|
|
541
|
+
metrics: {
|
|
542
|
+
last24h: {
|
|
543
|
+
totalRequests: number;
|
|
544
|
+
successRate: number;
|
|
545
|
+
cacheHitRate: number;
|
|
546
|
+
avgLatencyMs: number;
|
|
547
|
+
};
|
|
548
|
+
errorsByCode: Record<string, number>;
|
|
549
|
+
};
|
|
550
|
+
/**
|
|
551
|
+
* Cache status
|
|
552
|
+
*/
|
|
553
|
+
cache: {
|
|
554
|
+
totalEntries: number;
|
|
555
|
+
redisAvailable: boolean;
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* Webhook configuration for async notifications
|
|
560
|
+
*/
|
|
561
|
+
interface WebhookConfig {
|
|
562
|
+
/**
|
|
563
|
+
* Request ID to receive notifications for
|
|
564
|
+
*/
|
|
565
|
+
requestId: string;
|
|
566
|
+
/**
|
|
567
|
+
* URL to call when request completes
|
|
568
|
+
*/
|
|
569
|
+
url: string;
|
|
570
|
+
/**
|
|
571
|
+
* Optional secret for HMAC signing
|
|
572
|
+
*/
|
|
573
|
+
secret?: string;
|
|
574
|
+
/**
|
|
575
|
+
* Number of retry attempts on failure
|
|
576
|
+
* @default 3
|
|
577
|
+
*/
|
|
578
|
+
retryCount?: number;
|
|
579
|
+
}
|
|
411
580
|
|
|
412
581
|
/**
|
|
413
582
|
* AI Director Client SDK
|
|
@@ -467,6 +636,24 @@ declare class AIDirector {
|
|
|
467
636
|
* ```
|
|
468
637
|
*/
|
|
469
638
|
generate(options: GenerateOptions): Promise<GenerateResult>;
|
|
639
|
+
/**
|
|
640
|
+
* Optimized 3-step generation (minimizes Vercel compute costs)
|
|
641
|
+
*
|
|
642
|
+
* Step 1: Get token from Vercel (~50ms)
|
|
643
|
+
* Step 2: Call CF Worker directly (FREE)
|
|
644
|
+
* Step 3: Cache result in Vercel (~50ms)
|
|
645
|
+
*
|
|
646
|
+
* Total Vercel time: ~100ms vs 30-120s in legacy mode
|
|
647
|
+
*/
|
|
648
|
+
private generateOptimized;
|
|
649
|
+
/**
|
|
650
|
+
* Cache completion from worker (Step 3 - async, non-blocking)
|
|
651
|
+
*/
|
|
652
|
+
private cacheCompletionAsync;
|
|
653
|
+
/**
|
|
654
|
+
* Legacy single-call generation (higher Vercel compute cost)
|
|
655
|
+
*/
|
|
656
|
+
private generateLegacy;
|
|
470
657
|
/**
|
|
471
658
|
* Generate content with streaming (for long responses)
|
|
472
659
|
*
|
|
@@ -527,6 +714,67 @@ declare class AIDirector {
|
|
|
527
714
|
* @returns New AIDirector instance
|
|
528
715
|
*/
|
|
529
716
|
withConfig(overrides: Partial<AIDirectorConfig>): AIDirector;
|
|
717
|
+
/**
|
|
718
|
+
* Register a webhook for async notifications
|
|
719
|
+
*
|
|
720
|
+
* @param config - Webhook configuration
|
|
721
|
+
* @returns Registration result
|
|
722
|
+
*/
|
|
723
|
+
registerWebhook(config: {
|
|
724
|
+
requestId: string;
|
|
725
|
+
url: string;
|
|
726
|
+
secret?: string;
|
|
727
|
+
retryCount?: number;
|
|
728
|
+
}): Promise<{
|
|
729
|
+
registered: boolean;
|
|
730
|
+
message: string;
|
|
731
|
+
}>;
|
|
732
|
+
/**
|
|
733
|
+
* Get detailed health information including component status
|
|
734
|
+
*
|
|
735
|
+
* @returns Detailed health status
|
|
736
|
+
*/
|
|
737
|
+
healthDetailed(): Promise<{
|
|
738
|
+
status: 'healthy' | 'degraded' | 'unhealthy';
|
|
739
|
+
timestamp: string;
|
|
740
|
+
uptime: number;
|
|
741
|
+
components: Record<string, unknown>;
|
|
742
|
+
metrics: Record<string, unknown>;
|
|
743
|
+
}>;
|
|
744
|
+
/**
|
|
745
|
+
* Generate content for multiple prompts in a single request
|
|
746
|
+
*
|
|
747
|
+
* @param chainId - Chain to use for all items
|
|
748
|
+
* @param items - Array of prompts with IDs
|
|
749
|
+
* @param options - Batch options
|
|
750
|
+
* @returns Batch generation results
|
|
751
|
+
*/
|
|
752
|
+
generateBatch(chainId: string, items: Array<{
|
|
753
|
+
id: string;
|
|
754
|
+
prompt: string;
|
|
755
|
+
systemPrompt?: string;
|
|
756
|
+
temperature?: number;
|
|
757
|
+
maxTokens?: number;
|
|
758
|
+
}>, options?: {
|
|
759
|
+
continueOnError?: boolean;
|
|
760
|
+
}): Promise<{
|
|
761
|
+
results: Array<{
|
|
762
|
+
id: string;
|
|
763
|
+
success: boolean;
|
|
764
|
+
data?: unknown[];
|
|
765
|
+
error?: string;
|
|
766
|
+
}>;
|
|
767
|
+
summary: {
|
|
768
|
+
total: number;
|
|
769
|
+
succeeded: number;
|
|
770
|
+
failed: number;
|
|
771
|
+
tokensUsed: {
|
|
772
|
+
input: number;
|
|
773
|
+
output: number;
|
|
774
|
+
total: number;
|
|
775
|
+
};
|
|
776
|
+
};
|
|
777
|
+
}>;
|
|
530
778
|
/**
|
|
531
779
|
* Make authenticated API request
|
|
532
780
|
*/
|
|
@@ -656,4 +904,4 @@ declare function getKeyPrefix(secretKey: string): string;
|
|
|
656
904
|
*/
|
|
657
905
|
declare function isValidSecretKey(secretKey: string): boolean;
|
|
658
906
|
|
|
659
|
-
export { AIDirector, type AIDirectorConfig, AIDirectorError, AuthenticationError, ChainExecutionError, type ChainInfo, type ChainStep, ConfigurationError, type GenerateData, type GenerateError, type GenerateMeta, type GenerateOptions, type GenerateResult, type GenerationParameters, type HealthResult, type ModelInfo, NetworkError, RateLimitError, ServerError, type StreamCallbacks, type StreamProgress, TimeoutError, type TokenUsage, type UsageStats, ValidationError, generateSignature, getKeyPrefix, isAIDirectorError, isRetryableError, isValidSecretKey };
|
|
907
|
+
export { AIDirector, type AIDirectorConfig, AIDirectorError, AuthenticationError, ChainExecutionError, type ChainInfo, type ChainStep, ConfigurationError, type DetailedHealthResult, type FileAttachment, type GenerateData, type GenerateError, type GenerateMeta, type GenerateOptions, type GenerateResult, type GenerationParameters, type HealthResult, type ModelInfo, NetworkError, RateLimitError, ServerError, type StreamCallbacks, type StreamCompleteResult, type StreamProgress, TimeoutError, type TokenUsage, type UsageStats, ValidationError, type WebhookConfig, generateSignature, getKeyPrefix, isAIDirectorError, isRetryableError, isValidSecretKey };
|
package/dist/index.d.ts
CHANGED
|
@@ -58,11 +58,64 @@ interface GenerateOptions {
|
|
|
58
58
|
* Override the client timeout for this request
|
|
59
59
|
*/
|
|
60
60
|
timeout?: number;
|
|
61
|
+
/**
|
|
62
|
+
* Skip the cache for this request.
|
|
63
|
+
* Useful for prompts that should always generate fresh responses.
|
|
64
|
+
* @default false
|
|
65
|
+
*/
|
|
66
|
+
noCache?: boolean;
|
|
67
|
+
/**
|
|
68
|
+
* File attachments to include with the request.
|
|
69
|
+
* The server automatically handles:
|
|
70
|
+
* - File type detection (magic bytes)
|
|
71
|
+
* - Model compatibility checking
|
|
72
|
+
* - Automatic conversion (e.g., DOCX → PDF)
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```typescript
|
|
76
|
+
* await client.generate({
|
|
77
|
+
* chainId: 'my-chain',
|
|
78
|
+
* prompt: 'Summarize this document',
|
|
79
|
+
* files: [{
|
|
80
|
+
* data: documentBuffer.toString('base64'),
|
|
81
|
+
* filename: 'report.pdf',
|
|
82
|
+
* mimeType: 'application/pdf',
|
|
83
|
+
* }],
|
|
84
|
+
* });
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
files?: FileAttachment[];
|
|
88
|
+
/**
|
|
89
|
+
* Use the optimized 3-step generation flow.
|
|
90
|
+
* This minimizes Vercel compute costs by calling the Cloudflare Worker directly.
|
|
91
|
+
* Set to false to force legacy single-call mode.
|
|
92
|
+
* @default true
|
|
93
|
+
*/
|
|
94
|
+
useOptimized?: boolean;
|
|
61
95
|
/**
|
|
62
96
|
* Generation parameters
|
|
63
97
|
*/
|
|
64
98
|
options?: GenerationParameters;
|
|
65
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* File attachment for generate requests.
|
|
102
|
+
* Pass files and the server handles detection/conversion automatically.
|
|
103
|
+
*/
|
|
104
|
+
interface FileAttachment {
|
|
105
|
+
/**
|
|
106
|
+
* File content as base64-encoded string
|
|
107
|
+
*/
|
|
108
|
+
data: string;
|
|
109
|
+
/**
|
|
110
|
+
* Original filename (optional, helps with type detection)
|
|
111
|
+
*/
|
|
112
|
+
filename?: string;
|
|
113
|
+
/**
|
|
114
|
+
* Claimed MIME type (optional, will be verified by server)
|
|
115
|
+
* If omitted, server will auto-detect from magic bytes
|
|
116
|
+
*/
|
|
117
|
+
mimeType?: string;
|
|
118
|
+
}
|
|
66
119
|
/**
|
|
67
120
|
* AI generation parameters
|
|
68
121
|
*/
|
|
@@ -195,13 +248,22 @@ interface GenerateError {
|
|
|
195
248
|
*/
|
|
196
249
|
interface StreamCallbacks {
|
|
197
250
|
/**
|
|
198
|
-
* Called for each text chunk received
|
|
251
|
+
* Called for each text chunk received (legacy/backwards compatible)
|
|
199
252
|
*/
|
|
200
253
|
onChunk?: (chunk: string) => void;
|
|
201
254
|
/**
|
|
202
|
-
* Called
|
|
255
|
+
* Called for each VALID JSON object parsed from stream
|
|
256
|
+
* This is the preferred callback for JSON streaming - each object is complete and parseable
|
|
203
257
|
*/
|
|
204
|
-
|
|
258
|
+
onObject?: (object: unknown, index: number) => void;
|
|
259
|
+
/**
|
|
260
|
+
* Called when a JSON array starts in the stream
|
|
261
|
+
*/
|
|
262
|
+
onArrayStart?: () => void;
|
|
263
|
+
/**
|
|
264
|
+
* Called when streaming is complete with full result
|
|
265
|
+
*/
|
|
266
|
+
onComplete?: (result: StreamCompleteResult) => void;
|
|
205
267
|
/**
|
|
206
268
|
* Called if an error occurs
|
|
207
269
|
*/
|
|
@@ -211,6 +273,30 @@ interface StreamCallbacks {
|
|
|
211
273
|
*/
|
|
212
274
|
onProgress?: (progress: StreamProgress) => void;
|
|
213
275
|
}
|
|
276
|
+
/**
|
|
277
|
+
* Result passed to onComplete callback during streaming
|
|
278
|
+
*/
|
|
279
|
+
interface StreamCompleteResult {
|
|
280
|
+
/**
|
|
281
|
+
* All collected objects from the stream
|
|
282
|
+
*/
|
|
283
|
+
objects: unknown[];
|
|
284
|
+
/**
|
|
285
|
+
* Total number of objects parsed
|
|
286
|
+
*/
|
|
287
|
+
objectCount: number;
|
|
288
|
+
/**
|
|
289
|
+
* Token usage for the request
|
|
290
|
+
*/
|
|
291
|
+
tokensUsed?: {
|
|
292
|
+
input: number;
|
|
293
|
+
output: number;
|
|
294
|
+
};
|
|
295
|
+
/**
|
|
296
|
+
* Whether the response was cached at stream end
|
|
297
|
+
*/
|
|
298
|
+
cached?: boolean;
|
|
299
|
+
}
|
|
214
300
|
/**
|
|
215
301
|
* Streaming progress information
|
|
216
302
|
*/
|
|
@@ -408,6 +494,89 @@ interface HealthResult {
|
|
|
408
494
|
*/
|
|
409
495
|
timestamp?: string;
|
|
410
496
|
}
|
|
497
|
+
/**
|
|
498
|
+
* Detailed health check result with component status
|
|
499
|
+
*/
|
|
500
|
+
interface DetailedHealthResult {
|
|
501
|
+
/**
|
|
502
|
+
* Overall status
|
|
503
|
+
*/
|
|
504
|
+
status: 'healthy' | 'degraded' | 'unhealthy';
|
|
505
|
+
/**
|
|
506
|
+
* Server timestamp
|
|
507
|
+
*/
|
|
508
|
+
timestamp: string;
|
|
509
|
+
/**
|
|
510
|
+
* Server uptime in seconds
|
|
511
|
+
*/
|
|
512
|
+
uptime: number;
|
|
513
|
+
/**
|
|
514
|
+
* Component status
|
|
515
|
+
*/
|
|
516
|
+
components: {
|
|
517
|
+
database: {
|
|
518
|
+
status: string;
|
|
519
|
+
latencyMs: number;
|
|
520
|
+
message?: string;
|
|
521
|
+
};
|
|
522
|
+
redis: {
|
|
523
|
+
status: string;
|
|
524
|
+
latencyMs: number;
|
|
525
|
+
message?: string;
|
|
526
|
+
};
|
|
527
|
+
gemini: {
|
|
528
|
+
status: string;
|
|
529
|
+
errorRate: number;
|
|
530
|
+
recentErrors: number;
|
|
531
|
+
};
|
|
532
|
+
openrouter: {
|
|
533
|
+
status: string;
|
|
534
|
+
errorRate: number;
|
|
535
|
+
recentErrors: number;
|
|
536
|
+
};
|
|
537
|
+
};
|
|
538
|
+
/**
|
|
539
|
+
* 24h metrics
|
|
540
|
+
*/
|
|
541
|
+
metrics: {
|
|
542
|
+
last24h: {
|
|
543
|
+
totalRequests: number;
|
|
544
|
+
successRate: number;
|
|
545
|
+
cacheHitRate: number;
|
|
546
|
+
avgLatencyMs: number;
|
|
547
|
+
};
|
|
548
|
+
errorsByCode: Record<string, number>;
|
|
549
|
+
};
|
|
550
|
+
/**
|
|
551
|
+
* Cache status
|
|
552
|
+
*/
|
|
553
|
+
cache: {
|
|
554
|
+
totalEntries: number;
|
|
555
|
+
redisAvailable: boolean;
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* Webhook configuration for async notifications
|
|
560
|
+
*/
|
|
561
|
+
interface WebhookConfig {
|
|
562
|
+
/**
|
|
563
|
+
* Request ID to receive notifications for
|
|
564
|
+
*/
|
|
565
|
+
requestId: string;
|
|
566
|
+
/**
|
|
567
|
+
* URL to call when request completes
|
|
568
|
+
*/
|
|
569
|
+
url: string;
|
|
570
|
+
/**
|
|
571
|
+
* Optional secret for HMAC signing
|
|
572
|
+
*/
|
|
573
|
+
secret?: string;
|
|
574
|
+
/**
|
|
575
|
+
* Number of retry attempts on failure
|
|
576
|
+
* @default 3
|
|
577
|
+
*/
|
|
578
|
+
retryCount?: number;
|
|
579
|
+
}
|
|
411
580
|
|
|
412
581
|
/**
|
|
413
582
|
* AI Director Client SDK
|
|
@@ -467,6 +636,24 @@ declare class AIDirector {
|
|
|
467
636
|
* ```
|
|
468
637
|
*/
|
|
469
638
|
generate(options: GenerateOptions): Promise<GenerateResult>;
|
|
639
|
+
/**
|
|
640
|
+
* Optimized 3-step generation (minimizes Vercel compute costs)
|
|
641
|
+
*
|
|
642
|
+
* Step 1: Get token from Vercel (~50ms)
|
|
643
|
+
* Step 2: Call CF Worker directly (FREE)
|
|
644
|
+
* Step 3: Cache result in Vercel (~50ms)
|
|
645
|
+
*
|
|
646
|
+
* Total Vercel time: ~100ms vs 30-120s in legacy mode
|
|
647
|
+
*/
|
|
648
|
+
private generateOptimized;
|
|
649
|
+
/**
|
|
650
|
+
* Cache completion from worker (Step 3 - async, non-blocking)
|
|
651
|
+
*/
|
|
652
|
+
private cacheCompletionAsync;
|
|
653
|
+
/**
|
|
654
|
+
* Legacy single-call generation (higher Vercel compute cost)
|
|
655
|
+
*/
|
|
656
|
+
private generateLegacy;
|
|
470
657
|
/**
|
|
471
658
|
* Generate content with streaming (for long responses)
|
|
472
659
|
*
|
|
@@ -527,6 +714,67 @@ declare class AIDirector {
|
|
|
527
714
|
* @returns New AIDirector instance
|
|
528
715
|
*/
|
|
529
716
|
withConfig(overrides: Partial<AIDirectorConfig>): AIDirector;
|
|
717
|
+
/**
|
|
718
|
+
* Register a webhook for async notifications
|
|
719
|
+
*
|
|
720
|
+
* @param config - Webhook configuration
|
|
721
|
+
* @returns Registration result
|
|
722
|
+
*/
|
|
723
|
+
registerWebhook(config: {
|
|
724
|
+
requestId: string;
|
|
725
|
+
url: string;
|
|
726
|
+
secret?: string;
|
|
727
|
+
retryCount?: number;
|
|
728
|
+
}): Promise<{
|
|
729
|
+
registered: boolean;
|
|
730
|
+
message: string;
|
|
731
|
+
}>;
|
|
732
|
+
/**
|
|
733
|
+
* Get detailed health information including component status
|
|
734
|
+
*
|
|
735
|
+
* @returns Detailed health status
|
|
736
|
+
*/
|
|
737
|
+
healthDetailed(): Promise<{
|
|
738
|
+
status: 'healthy' | 'degraded' | 'unhealthy';
|
|
739
|
+
timestamp: string;
|
|
740
|
+
uptime: number;
|
|
741
|
+
components: Record<string, unknown>;
|
|
742
|
+
metrics: Record<string, unknown>;
|
|
743
|
+
}>;
|
|
744
|
+
/**
|
|
745
|
+
* Generate content for multiple prompts in a single request
|
|
746
|
+
*
|
|
747
|
+
* @param chainId - Chain to use for all items
|
|
748
|
+
* @param items - Array of prompts with IDs
|
|
749
|
+
* @param options - Batch options
|
|
750
|
+
* @returns Batch generation results
|
|
751
|
+
*/
|
|
752
|
+
generateBatch(chainId: string, items: Array<{
|
|
753
|
+
id: string;
|
|
754
|
+
prompt: string;
|
|
755
|
+
systemPrompt?: string;
|
|
756
|
+
temperature?: number;
|
|
757
|
+
maxTokens?: number;
|
|
758
|
+
}>, options?: {
|
|
759
|
+
continueOnError?: boolean;
|
|
760
|
+
}): Promise<{
|
|
761
|
+
results: Array<{
|
|
762
|
+
id: string;
|
|
763
|
+
success: boolean;
|
|
764
|
+
data?: unknown[];
|
|
765
|
+
error?: string;
|
|
766
|
+
}>;
|
|
767
|
+
summary: {
|
|
768
|
+
total: number;
|
|
769
|
+
succeeded: number;
|
|
770
|
+
failed: number;
|
|
771
|
+
tokensUsed: {
|
|
772
|
+
input: number;
|
|
773
|
+
output: number;
|
|
774
|
+
total: number;
|
|
775
|
+
};
|
|
776
|
+
};
|
|
777
|
+
}>;
|
|
530
778
|
/**
|
|
531
779
|
* Make authenticated API request
|
|
532
780
|
*/
|
|
@@ -656,4 +904,4 @@ declare function getKeyPrefix(secretKey: string): string;
|
|
|
656
904
|
*/
|
|
657
905
|
declare function isValidSecretKey(secretKey: string): boolean;
|
|
658
906
|
|
|
659
|
-
export { AIDirector, type AIDirectorConfig, AIDirectorError, AuthenticationError, ChainExecutionError, type ChainInfo, type ChainStep, ConfigurationError, type GenerateData, type GenerateError, type GenerateMeta, type GenerateOptions, type GenerateResult, type GenerationParameters, type HealthResult, type ModelInfo, NetworkError, RateLimitError, ServerError, type StreamCallbacks, type StreamProgress, TimeoutError, type TokenUsage, type UsageStats, ValidationError, generateSignature, getKeyPrefix, isAIDirectorError, isRetryableError, isValidSecretKey };
|
|
907
|
+
export { AIDirector, type AIDirectorConfig, AIDirectorError, AuthenticationError, ChainExecutionError, type ChainInfo, type ChainStep, ConfigurationError, type DetailedHealthResult, type FileAttachment, type GenerateData, type GenerateError, type GenerateMeta, type GenerateOptions, type GenerateResult, type GenerationParameters, type HealthResult, type ModelInfo, NetworkError, RateLimitError, ServerError, type StreamCallbacks, type StreamCompleteResult, type StreamProgress, TimeoutError, type TokenUsage, type UsageStats, ValidationError, type WebhookConfig, generateSignature, getKeyPrefix, isAIDirectorError, isRetryableError, isValidSecretKey };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
'use strict';var M=typeof process<"u"&&process.versions?.node;async function
|
|
2
|
-
`),u=await
|
|
3
|
-
`),u=await
|
|
4
|
-
`);
|
|
1
|
+
'use strict';var M=typeof process<"u"&&process.versions?.node;async function j(o){let{createHash:e}=await import('crypto');return e("sha256").update(o).digest("hex")}async function K(o,e,t,r,a){let{createHmac:s}=await import('crypto'),c=[e.toUpperCase(),t,a.toString(),r].join(`
|
|
2
|
+
`),u=await j(o);return s("sha256",u).update(c).digest("hex")}async function N(o){let t=new TextEncoder().encode(o),r=await crypto.subtle.digest("SHA-256",t);return Array.from(new Uint8Array(r)).map(s=>s.toString(16).padStart(2,"0")).join("")}async function G(o,e,t,r,a){let s=new TextEncoder,c=[e.toUpperCase(),t,a.toString(),r].join(`
|
|
3
|
+
`),u=await N(o),n=s.encode(u),i=await crypto.subtle.importKey("raw",n,{name:"HMAC",hash:"SHA-256"},false,["sign"]),m=await crypto.subtle.sign("HMAC",i,s.encode(c));return Array.from(new Uint8Array(m)).map(h=>h.toString(16).padStart(2,"0")).join("")}async function A(o,e,t,r,a){return M?K(o,e,t,r,a):G(o,e,t,r,a)}function T(o){return o.slice(0,12)}function x(o){return typeof o=="string"&&o.startsWith("aid_sk_")&&o.length>=20}var d=class extends Error{constructor(e,t,r){super(e),this.name="AIDirectorError",this.code=t,this.retryable=r?.retryable??false,this.statusCode=r?.statusCode,this.originalError=r?.cause;}},b=class extends d{constructor(e){super(e,"CONFIGURATION_ERROR",{retryable:false}),this.name="ConfigurationError";}},E=class extends d{constructor(e,t){super(e,"AUTH_ERROR",{retryable:false,statusCode:t}),this.name="AuthenticationError";}},w=class extends d{constructor(e,t=6e4){super(e,"RATE_LIMITED",{retryable:true,statusCode:429}),this.name="RateLimitError",this.retryAfterMs=t;}},k=class extends d{constructor(e){super(`Request timed out after ${e}ms`,"TIMEOUT",{retryable:true}),this.name="TimeoutError",this.timeoutMs=e;}},y=class extends d{constructor(e,t){super(e,"NETWORK_ERROR",{retryable:true,cause:t}),this.name="NetworkError";}},g=class extends d{constructor(e,t=[]){super(e,"CHAIN_FAILED",{retryable:false}),this.name="ChainExecutionError",this.attemptedModels=t;}},U=class extends d{constructor(e,t=[]){super(e,"VALIDATION_ERROR",{retryable:false}),this.name="ValidationError",this.validationErrors=t;}},p=class extends d{constructor(e,t=500){super(e,"SERVER_ERROR",{retryable:true,statusCode:t}),this.name="ServerError";}};function v(o){return o instanceof d}function _(o){return v(o)?o.retryable:false}var L="http://localhost:3000",W=6e5,H=3,O=[1e3,2e3,5e3],I=class o{constructor(e){if(!e.secretKey)throw new b("secretKey is required");if(!x(e.secretKey))throw new b("Invalid secret key format. Expected format: aid_sk_<key>");this.secretKey=e.secretKey,this.baseUrl=(e.baseUrl||L).replace(/\/$/,""),this.timeout=e.timeout??W,this.maxRetries=e.maxRetries??H,this.keyPrefix=T(e.secretKey),this.debug=e.debug??false,this.debug&&this.log("Initialized",{baseUrl:this.baseUrl,timeout:this.timeout});}async generate(e){if(e.useOptimized!==false)try{return await this.generateOptimized(e)}catch(r){this.debug&&this.log("3-step generation failed, falling back to legacy",{error:r instanceof Error?r.message:String(r)});}return this.generateLegacy(e)}async generateOptimized(e){let t=Date.now(),r="/api/v1/generate/token",a=JSON.stringify({chainId:e.chainId,prompt:e.prompt,systemPrompt:e.options?.systemPrompt,temperature:e.options?.temperature,maxTokens:e.options?.maxTokens,topP:e.options?.topP,topK:e.options?.topK,files:e.files}),s=await this.makeAuthenticatedRequest(r,"POST",a);if(s.cached&&s.data)return this.debug&&this.log("generate cache hit (optimized)",{latencyMs:Date.now()-t}),{success:true,data:s.data,meta:{cached:true,modelUsed:s.meta?.modelUsed||"",tokensUsed:{input:0,output:0},latencyMs:Date.now()-t,attemptedModels:[]}};if(!s.token||!s.workerUrl)throw new g("Token generation failed - no token or worker URL returned",[]);let c=JSON.stringify({token:s.token,prompt:e.prompt,systemPrompt:e.options?.systemPrompt,timeout:e.timeout??this.timeout}),n=await(await this.fetchWithTimeout(s.workerUrl,{method:"POST",headers:{"Content-Type":"application/json"},body:c},e.timeout??this.timeout)).json();if(!n.success||!n.content)return {success:false,data:{valid:[],invalid:[]},meta:{cached:false,modelUsed:n.modelId||"",tokensUsed:n.tokensUsed||{input:0,output:0},latencyMs:Date.now()-t,attemptedModels:[n.modelId||""]},error:{code:n.errorCode||"GENERATION_FAILED",message:n.errorMessage||"Generation failed",retryable:false}};let i=[],m=[];try{let h=JSON.parse(n.content);Array.isArray(h)?i=h:i=[h];}catch{i=[n.content];}n.completionToken&&this.cacheCompletionAsync({completionToken:n.completionToken,content:n.content,tokensUsed:n.tokensUsed||{input:0,output:0},finishReason:n.finishReason||"stop",chainId:e.chainId,modelUsed:n.modelId,prompt:e.prompt,systemPrompt:e.options?.systemPrompt,temperature:e.options?.temperature,maxTokens:e.options?.maxTokens,topP:e.options?.topP,topK:e.options?.topK}).catch(h=>{this.debug&&this.log("cacheCompletion failed (non-blocking)",{error:h});});let f=Date.now()-t;return this.debug&&this.log("generate success (optimized 3-step)",{latencyMs:f,modelUsed:n.modelId,tokensUsed:n.tokensUsed}),{success:true,data:{valid:i,invalid:m},meta:{cached:false,modelUsed:n.modelId,tokensUsed:n.tokensUsed||{input:0,output:0},latencyMs:f,attemptedModels:[n.modelId]}}}async cacheCompletionAsync(e){let t="/api/v1/generate/complete",r=JSON.stringify(e);await this.makeAuthenticatedRequest(t,"POST",r);}async generateLegacy(e){let t=Date.now(),r="/api/v1/generate",a=JSON.stringify({chainId:e.chainId,prompt:e.prompt,schema:e.schema,noCache:e.noCache,files:e.files,options:{...e.options,timeout:e.timeout??this.timeout}}),s=null;for(let n=0;n<=this.maxRetries;n++)try{let i=await this.makeAuthenticatedRequest(r,"POST",a,e.timeout),m=Date.now()-t;return this.debug&&this.log("generate success (legacy)",{latencyMs:m,attempt:n+1}),i.meta&&(i.meta.latencyMs=m),i}catch(i){if(s=i instanceof Error?i:new Error(String(i)),this.debug&&this.log(`generate attempt ${n+1} failed`,{error:s.message}),!this.isRetryable(i)||n>=this.maxRetries)break;let m=O[Math.min(n,O.length-1)];i instanceof w&&i.retryAfterMs>m?await this.sleep(i.retryAfterMs):await this.sleep(m);}let c=Date.now()-t,u=s instanceof k;return {success:false,data:{valid:[],invalid:[]},meta:{cached:false,modelUsed:"",tokensUsed:{input:0,output:0},latencyMs:c,attemptedModels:[]},error:{code:u?"TIMEOUT":"REQUEST_FAILED",message:s?.message||"Request failed after all retries",retryable:false}}}async generateStream(e,t){let r="/api/v1/generate/stream",a=JSON.stringify({chainId:e.chainId,prompt:e.prompt,schema:e.schema,options:e.options});try{let s=Date.now(),c=await A(this.secretKey,"POST",r,a,s),u=await this.fetchWithTimeout(`${this.baseUrl}${r}`,{method:"POST",headers:{"Content-Type":"application/json",Accept:"text/event-stream","x-aidirector-signature":c,"x-aidirector-timestamp":s.toString(),"x-aidirector-key":this.keyPrefix},body:a},e.timeout??this.timeout);if(!u.ok){let h=await u.json();throw this.parseError(u.status,h)}let n=u.body?.getReader();if(!n)throw new y("Response body is not readable");let i=new TextDecoder,m="",f=[];for(;;){let{done:h,value:D}=await n.read();if(h)break;m+=i.decode(D,{stream:!0});let P=m.split(`
|
|
4
|
+
`);m=P.pop()||"";let S="";for(let R of P){if(R.startsWith("event: ")){S=R.slice(7).trim();continue}if(R.startsWith("data: ")){let C=R.slice(6);if(C==="[DONE]")break;try{let l=JSON.parse(C);switch(S){case "start":this.debug&&this.log("Stream started",l);break;case "object":l.object!==void 0&&(f.push(l.object),t.onObject?.(l.object,l.index)),t.onChunk&&l.object&&t.onChunk(JSON.stringify(l.object));break;case "array_start":t.onArrayStart?.();break;case "complete":t.onComplete&&t.onComplete({objects:f,objectCount:l.objectCount,tokensUsed:l.tokensUsed,cached:l.cached});break;case "error":t.onError&&t.onError(new g(l.error||"Stream error",["STREAM_ERROR"]));break;default:l.chunk&&t.onChunk?.(l.chunk);}}catch{}S="";}}}}catch(s){if(t.onError)t.onError(s instanceof Error?s:new Error(String(s)));else throw s}}async listModels(){let t=await(await this.fetchWithTimeout(`${this.baseUrl}/api/v1/models`,{method:"GET",headers:{Accept:"application/json"}})).json();if(!t.success)throw new p(t.error?.message||"Failed to list models");return t.data}async listChains(){let t=await(await this.fetchWithTimeout(`${this.baseUrl}/api/v1/chains`,{method:"GET",credentials:"include",headers:{Accept:"application/json"}})).json();if(!t.success)throw new p(t.error?.message||"Failed to list chains");return t.data}async getUsage(e){let t=new URLSearchParams;e?.startDate&&t.set("startDate",e.startDate.toISOString()),e?.endDate&&t.set("endDate",e.endDate.toISOString());let r=`${this.baseUrl}/api/v1/usage${t.toString()?"?"+t:""}`,s=await(await this.fetchWithTimeout(r,{method:"GET",credentials:"include",headers:{Accept:"application/json"}})).json();if(!s.success)throw new p(s.error?.message||"Failed to get usage");return s.data}async health(){let e=Date.now();try{let t=await this.fetchWithTimeout(`${this.baseUrl}/api/health`,{method:"GET"},5e3),r=Date.now()-e,a=await t.json().catch(()=>({}));return {ok:t.ok,latencyMs:r,version:a.version}}catch{return {ok:false,latencyMs:Date.now()-e}}}withConfig(e){return new o({secretKey:this.secretKey,baseUrl:this.baseUrl,timeout:this.timeout,maxRetries:this.maxRetries,debug:this.debug,...e})}async registerWebhook(e){let t=await this.makeAuthenticatedRequest("/api/v1/webhooks","POST",JSON.stringify(e));if(!t.success||!t.data)throw new p(t.error?.message||"Failed to register webhook");return t.data}async healthDetailed(){return (await fetch(`${this.baseUrl}/api/v1/health/detailed`,{headers:{Accept:"application/json"}})).json()}async generateBatch(e,t,r={}){let a=await this.makeAuthenticatedRequest("/api/v1/generate/batch","POST",JSON.stringify({chainId:e,items:t,continueOnError:r.continueOnError??true}));if(!a.success||!a.data)throw new g(a.error?.message||"Batch generation failed",[]);return a.data}async makeAuthenticatedRequest(e,t,r,a){let s=Date.now(),c=await A(this.secretKey,t,e,r,s),u=await this.fetchWithTimeout(`${this.baseUrl}${e}`,{method:t,headers:{"Content-Type":"application/json",Accept:"application/json","x-aidirector-signature":c,"x-aidirector-timestamp":s.toString(),"x-aidirector-key":this.keyPrefix},body:r},a??this.timeout),n=await u.json();if(!u.ok)throw this.parseError(u.status,n);return n}async fetchWithTimeout(e,t,r=this.timeout){let a=new AbortController,s=setTimeout(()=>a.abort(),r);try{return await fetch(e,{...t,signal:a.signal})}catch(c){throw c instanceof Error&&c.name==="AbortError"?new k(r):c instanceof Error?new y(c.message,c):new y(String(c))}finally{clearTimeout(s);}}parseError(e,t){let r=t?.error?.message||`HTTP ${e}`,a=t?.error?.code||"UNKNOWN";switch(e){case 401:return new E(r,e);case 429:let s=t?.retryAfterMs||6e4;return new w(r,s);case 500:case 502:case 503:case 504:return new p(r,e);default:return a==="CHAIN_FAILED"?new g(r,t?.error?.attemptedModels||[]):new d(r,a,{statusCode:e,retryable:e>=500})}}isRetryable(e){if(e instanceof d)return e.retryable;if(e instanceof Error){let t=e.message.toLowerCase();return t.includes("network")||t.includes("timeout")||e.name==="AbortError"}return false}sleep(e){return new Promise(t=>setTimeout(t,e))}log(e,t){let r="[AIDirector]";t?console.log(r,e,t):console.log(r,e);}};exports.AIDirector=I;exports.AIDirectorError=d;exports.AuthenticationError=E;exports.ChainExecutionError=g;exports.ConfigurationError=b;exports.NetworkError=y;exports.RateLimitError=w;exports.ServerError=p;exports.TimeoutError=k;exports.ValidationError=U;exports.generateSignature=A;exports.getKeyPrefix=T;exports.isAIDirectorError=v;exports.isRetryableError=_;exports.isValidSecretKey=x;
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var M=typeof process<"u"&&process.versions?.node;async function
|
|
2
|
-
`),u=await
|
|
3
|
-
`),u=await
|
|
4
|
-
`);
|
|
1
|
+
var M=typeof process<"u"&&process.versions?.node;async function j(o){let{createHash:e}=await import('crypto');return e("sha256").update(o).digest("hex")}async function K(o,e,t,r,a){let{createHmac:s}=await import('crypto'),c=[e.toUpperCase(),t,a.toString(),r].join(`
|
|
2
|
+
`),u=await j(o);return s("sha256",u).update(c).digest("hex")}async function N(o){let t=new TextEncoder().encode(o),r=await crypto.subtle.digest("SHA-256",t);return Array.from(new Uint8Array(r)).map(s=>s.toString(16).padStart(2,"0")).join("")}async function G(o,e,t,r,a){let s=new TextEncoder,c=[e.toUpperCase(),t,a.toString(),r].join(`
|
|
3
|
+
`),u=await N(o),n=s.encode(u),i=await crypto.subtle.importKey("raw",n,{name:"HMAC",hash:"SHA-256"},false,["sign"]),m=await crypto.subtle.sign("HMAC",i,s.encode(c));return Array.from(new Uint8Array(m)).map(h=>h.toString(16).padStart(2,"0")).join("")}async function A(o,e,t,r,a){return M?K(o,e,t,r,a):G(o,e,t,r,a)}function T(o){return o.slice(0,12)}function x(o){return typeof o=="string"&&o.startsWith("aid_sk_")&&o.length>=20}var d=class extends Error{constructor(e,t,r){super(e),this.name="AIDirectorError",this.code=t,this.retryable=r?.retryable??false,this.statusCode=r?.statusCode,this.originalError=r?.cause;}},b=class extends d{constructor(e){super(e,"CONFIGURATION_ERROR",{retryable:false}),this.name="ConfigurationError";}},E=class extends d{constructor(e,t){super(e,"AUTH_ERROR",{retryable:false,statusCode:t}),this.name="AuthenticationError";}},w=class extends d{constructor(e,t=6e4){super(e,"RATE_LIMITED",{retryable:true,statusCode:429}),this.name="RateLimitError",this.retryAfterMs=t;}},k=class extends d{constructor(e){super(`Request timed out after ${e}ms`,"TIMEOUT",{retryable:true}),this.name="TimeoutError",this.timeoutMs=e;}},y=class extends d{constructor(e,t){super(e,"NETWORK_ERROR",{retryable:true,cause:t}),this.name="NetworkError";}},g=class extends d{constructor(e,t=[]){super(e,"CHAIN_FAILED",{retryable:false}),this.name="ChainExecutionError",this.attemptedModels=t;}},U=class extends d{constructor(e,t=[]){super(e,"VALIDATION_ERROR",{retryable:false}),this.name="ValidationError",this.validationErrors=t;}},p=class extends d{constructor(e,t=500){super(e,"SERVER_ERROR",{retryable:true,statusCode:t}),this.name="ServerError";}};function v(o){return o instanceof d}function _(o){return v(o)?o.retryable:false}var L="http://localhost:3000",W=6e5,H=3,O=[1e3,2e3,5e3],I=class o{constructor(e){if(!e.secretKey)throw new b("secretKey is required");if(!x(e.secretKey))throw new b("Invalid secret key format. Expected format: aid_sk_<key>");this.secretKey=e.secretKey,this.baseUrl=(e.baseUrl||L).replace(/\/$/,""),this.timeout=e.timeout??W,this.maxRetries=e.maxRetries??H,this.keyPrefix=T(e.secretKey),this.debug=e.debug??false,this.debug&&this.log("Initialized",{baseUrl:this.baseUrl,timeout:this.timeout});}async generate(e){if(e.useOptimized!==false)try{return await this.generateOptimized(e)}catch(r){this.debug&&this.log("3-step generation failed, falling back to legacy",{error:r instanceof Error?r.message:String(r)});}return this.generateLegacy(e)}async generateOptimized(e){let t=Date.now(),r="/api/v1/generate/token",a=JSON.stringify({chainId:e.chainId,prompt:e.prompt,systemPrompt:e.options?.systemPrompt,temperature:e.options?.temperature,maxTokens:e.options?.maxTokens,topP:e.options?.topP,topK:e.options?.topK,files:e.files}),s=await this.makeAuthenticatedRequest(r,"POST",a);if(s.cached&&s.data)return this.debug&&this.log("generate cache hit (optimized)",{latencyMs:Date.now()-t}),{success:true,data:s.data,meta:{cached:true,modelUsed:s.meta?.modelUsed||"",tokensUsed:{input:0,output:0},latencyMs:Date.now()-t,attemptedModels:[]}};if(!s.token||!s.workerUrl)throw new g("Token generation failed - no token or worker URL returned",[]);let c=JSON.stringify({token:s.token,prompt:e.prompt,systemPrompt:e.options?.systemPrompt,timeout:e.timeout??this.timeout}),n=await(await this.fetchWithTimeout(s.workerUrl,{method:"POST",headers:{"Content-Type":"application/json"},body:c},e.timeout??this.timeout)).json();if(!n.success||!n.content)return {success:false,data:{valid:[],invalid:[]},meta:{cached:false,modelUsed:n.modelId||"",tokensUsed:n.tokensUsed||{input:0,output:0},latencyMs:Date.now()-t,attemptedModels:[n.modelId||""]},error:{code:n.errorCode||"GENERATION_FAILED",message:n.errorMessage||"Generation failed",retryable:false}};let i=[],m=[];try{let h=JSON.parse(n.content);Array.isArray(h)?i=h:i=[h];}catch{i=[n.content];}n.completionToken&&this.cacheCompletionAsync({completionToken:n.completionToken,content:n.content,tokensUsed:n.tokensUsed||{input:0,output:0},finishReason:n.finishReason||"stop",chainId:e.chainId,modelUsed:n.modelId,prompt:e.prompt,systemPrompt:e.options?.systemPrompt,temperature:e.options?.temperature,maxTokens:e.options?.maxTokens,topP:e.options?.topP,topK:e.options?.topK}).catch(h=>{this.debug&&this.log("cacheCompletion failed (non-blocking)",{error:h});});let f=Date.now()-t;return this.debug&&this.log("generate success (optimized 3-step)",{latencyMs:f,modelUsed:n.modelId,tokensUsed:n.tokensUsed}),{success:true,data:{valid:i,invalid:m},meta:{cached:false,modelUsed:n.modelId,tokensUsed:n.tokensUsed||{input:0,output:0},latencyMs:f,attemptedModels:[n.modelId]}}}async cacheCompletionAsync(e){let t="/api/v1/generate/complete",r=JSON.stringify(e);await this.makeAuthenticatedRequest(t,"POST",r);}async generateLegacy(e){let t=Date.now(),r="/api/v1/generate",a=JSON.stringify({chainId:e.chainId,prompt:e.prompt,schema:e.schema,noCache:e.noCache,files:e.files,options:{...e.options,timeout:e.timeout??this.timeout}}),s=null;for(let n=0;n<=this.maxRetries;n++)try{let i=await this.makeAuthenticatedRequest(r,"POST",a,e.timeout),m=Date.now()-t;return this.debug&&this.log("generate success (legacy)",{latencyMs:m,attempt:n+1}),i.meta&&(i.meta.latencyMs=m),i}catch(i){if(s=i instanceof Error?i:new Error(String(i)),this.debug&&this.log(`generate attempt ${n+1} failed`,{error:s.message}),!this.isRetryable(i)||n>=this.maxRetries)break;let m=O[Math.min(n,O.length-1)];i instanceof w&&i.retryAfterMs>m?await this.sleep(i.retryAfterMs):await this.sleep(m);}let c=Date.now()-t,u=s instanceof k;return {success:false,data:{valid:[],invalid:[]},meta:{cached:false,modelUsed:"",tokensUsed:{input:0,output:0},latencyMs:c,attemptedModels:[]},error:{code:u?"TIMEOUT":"REQUEST_FAILED",message:s?.message||"Request failed after all retries",retryable:false}}}async generateStream(e,t){let r="/api/v1/generate/stream",a=JSON.stringify({chainId:e.chainId,prompt:e.prompt,schema:e.schema,options:e.options});try{let s=Date.now(),c=await A(this.secretKey,"POST",r,a,s),u=await this.fetchWithTimeout(`${this.baseUrl}${r}`,{method:"POST",headers:{"Content-Type":"application/json",Accept:"text/event-stream","x-aidirector-signature":c,"x-aidirector-timestamp":s.toString(),"x-aidirector-key":this.keyPrefix},body:a},e.timeout??this.timeout);if(!u.ok){let h=await u.json();throw this.parseError(u.status,h)}let n=u.body?.getReader();if(!n)throw new y("Response body is not readable");let i=new TextDecoder,m="",f=[];for(;;){let{done:h,value:D}=await n.read();if(h)break;m+=i.decode(D,{stream:!0});let P=m.split(`
|
|
4
|
+
`);m=P.pop()||"";let S="";for(let R of P){if(R.startsWith("event: ")){S=R.slice(7).trim();continue}if(R.startsWith("data: ")){let C=R.slice(6);if(C==="[DONE]")break;try{let l=JSON.parse(C);switch(S){case "start":this.debug&&this.log("Stream started",l);break;case "object":l.object!==void 0&&(f.push(l.object),t.onObject?.(l.object,l.index)),t.onChunk&&l.object&&t.onChunk(JSON.stringify(l.object));break;case "array_start":t.onArrayStart?.();break;case "complete":t.onComplete&&t.onComplete({objects:f,objectCount:l.objectCount,tokensUsed:l.tokensUsed,cached:l.cached});break;case "error":t.onError&&t.onError(new g(l.error||"Stream error",["STREAM_ERROR"]));break;default:l.chunk&&t.onChunk?.(l.chunk);}}catch{}S="";}}}}catch(s){if(t.onError)t.onError(s instanceof Error?s:new Error(String(s)));else throw s}}async listModels(){let t=await(await this.fetchWithTimeout(`${this.baseUrl}/api/v1/models`,{method:"GET",headers:{Accept:"application/json"}})).json();if(!t.success)throw new p(t.error?.message||"Failed to list models");return t.data}async listChains(){let t=await(await this.fetchWithTimeout(`${this.baseUrl}/api/v1/chains`,{method:"GET",credentials:"include",headers:{Accept:"application/json"}})).json();if(!t.success)throw new p(t.error?.message||"Failed to list chains");return t.data}async getUsage(e){let t=new URLSearchParams;e?.startDate&&t.set("startDate",e.startDate.toISOString()),e?.endDate&&t.set("endDate",e.endDate.toISOString());let r=`${this.baseUrl}/api/v1/usage${t.toString()?"?"+t:""}`,s=await(await this.fetchWithTimeout(r,{method:"GET",credentials:"include",headers:{Accept:"application/json"}})).json();if(!s.success)throw new p(s.error?.message||"Failed to get usage");return s.data}async health(){let e=Date.now();try{let t=await this.fetchWithTimeout(`${this.baseUrl}/api/health`,{method:"GET"},5e3),r=Date.now()-e,a=await t.json().catch(()=>({}));return {ok:t.ok,latencyMs:r,version:a.version}}catch{return {ok:false,latencyMs:Date.now()-e}}}withConfig(e){return new o({secretKey:this.secretKey,baseUrl:this.baseUrl,timeout:this.timeout,maxRetries:this.maxRetries,debug:this.debug,...e})}async registerWebhook(e){let t=await this.makeAuthenticatedRequest("/api/v1/webhooks","POST",JSON.stringify(e));if(!t.success||!t.data)throw new p(t.error?.message||"Failed to register webhook");return t.data}async healthDetailed(){return (await fetch(`${this.baseUrl}/api/v1/health/detailed`,{headers:{Accept:"application/json"}})).json()}async generateBatch(e,t,r={}){let a=await this.makeAuthenticatedRequest("/api/v1/generate/batch","POST",JSON.stringify({chainId:e,items:t,continueOnError:r.continueOnError??true}));if(!a.success||!a.data)throw new g(a.error?.message||"Batch generation failed",[]);return a.data}async makeAuthenticatedRequest(e,t,r,a){let s=Date.now(),c=await A(this.secretKey,t,e,r,s),u=await this.fetchWithTimeout(`${this.baseUrl}${e}`,{method:t,headers:{"Content-Type":"application/json",Accept:"application/json","x-aidirector-signature":c,"x-aidirector-timestamp":s.toString(),"x-aidirector-key":this.keyPrefix},body:r},a??this.timeout),n=await u.json();if(!u.ok)throw this.parseError(u.status,n);return n}async fetchWithTimeout(e,t,r=this.timeout){let a=new AbortController,s=setTimeout(()=>a.abort(),r);try{return await fetch(e,{...t,signal:a.signal})}catch(c){throw c instanceof Error&&c.name==="AbortError"?new k(r):c instanceof Error?new y(c.message,c):new y(String(c))}finally{clearTimeout(s);}}parseError(e,t){let r=t?.error?.message||`HTTP ${e}`,a=t?.error?.code||"UNKNOWN";switch(e){case 401:return new E(r,e);case 429:let s=t?.retryAfterMs||6e4;return new w(r,s);case 500:case 502:case 503:case 504:return new p(r,e);default:return a==="CHAIN_FAILED"?new g(r,t?.error?.attemptedModels||[]):new d(r,a,{statusCode:e,retryable:e>=500})}}isRetryable(e){if(e instanceof d)return e.retryable;if(e instanceof Error){let t=e.message.toLowerCase();return t.includes("network")||t.includes("timeout")||e.name==="AbortError"}return false}sleep(e){return new Promise(t=>setTimeout(t,e))}log(e,t){let r="[AIDirector]";t?console.log(r,e,t):console.log(r,e);}};export{I as AIDirector,d as AIDirectorError,E as AuthenticationError,g as ChainExecutionError,b as ConfigurationError,y as NetworkError,w as RateLimitError,p as ServerError,k as TimeoutError,U as ValidationError,A as generateSignature,T as getKeyPrefix,v as isAIDirectorError,_ as isRetryableError,x as isValidSecretKey};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@altsafe/aidirector",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Official TypeScript SDK for AI Director - Intelligent AI API Gateway with automatic failover, caching, and JSON extraction",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|