@altsafe/aidirector 1.1.0 → 1.2.1
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 +145 -6
- package/dist/index.d.mts +113 -1
- package/dist/index.d.ts +113 -1
- package/dist/index.js +4 -4
- package/dist/index.mjs +4 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -37,8 +37,10 @@ if (result.success) {
|
|
|
37
37
|
- ⚡ **3-Step Architecture** - Token → Worker → Complete (minimizes costs)
|
|
38
38
|
- 📎 **File Attachments** - Upload and process documents
|
|
39
39
|
- 🔄 **Automatic Retries** - Exponential backoff on failures
|
|
40
|
-
- 💾 **Smart Caching** -
|
|
41
|
-
- 🎯 **TypeScript** - Full type safety
|
|
40
|
+
- 💾 **Smart Caching** - Two-tier (user/global) cache with AI-directed scoping
|
|
41
|
+
- 🎯 **TypeScript** - Full type safety with comprehensive types
|
|
42
|
+
- 🛑 **Request Cancellation** - Support for AbortSignal
|
|
43
|
+
- 🪝 **Webhooks** - Async notification callbacks
|
|
42
44
|
|
|
43
45
|
## Streaming (Recommended for Large Responses)
|
|
44
46
|
|
|
@@ -63,6 +65,64 @@ await client.generateStream(
|
|
|
63
65
|
);
|
|
64
66
|
```
|
|
65
67
|
|
|
68
|
+
## Batch Generation
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
const result = await client.generateBatch('my-chain', [
|
|
72
|
+
{ id: 'item1', prompt: 'Describe product A' },
|
|
73
|
+
{ id: 'item2', prompt: 'Describe product B' },
|
|
74
|
+
{ id: 'item3', prompt: 'Describe product C' },
|
|
75
|
+
]);
|
|
76
|
+
|
|
77
|
+
console.log(`Processed ${result.summary.succeeded}/${result.summary.total}`);
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Request Cancellation
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
const controller = new AbortController();
|
|
84
|
+
|
|
85
|
+
// Cancel after 5 seconds
|
|
86
|
+
setTimeout(() => controller.abort(), 5000);
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
const result = await client.generate({
|
|
90
|
+
chainId: 'my-chain',
|
|
91
|
+
prompt: 'Long running prompt',
|
|
92
|
+
signal: controller.signal,
|
|
93
|
+
});
|
|
94
|
+
} catch (error) {
|
|
95
|
+
if (error instanceof TimeoutError) {
|
|
96
|
+
console.log('Request was cancelled');
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Cache Control
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
// Global cache (shared across users - default)
|
|
105
|
+
const result = await client.generate({
|
|
106
|
+
chainId: 'my-chain',
|
|
107
|
+
prompt: 'Static content',
|
|
108
|
+
cacheScope: 'global',
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// User-scoped cache (private to user)
|
|
112
|
+
const userResult = await client.generate({
|
|
113
|
+
chainId: 'my-chain',
|
|
114
|
+
prompt: 'Personalized content',
|
|
115
|
+
cacheScope: 'user',
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// Skip cache entirely
|
|
119
|
+
const freshResult = await client.generate({
|
|
120
|
+
chainId: 'my-chain',
|
|
121
|
+
prompt: 'Always fresh',
|
|
122
|
+
cacheScope: 'skip',
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
66
126
|
## File Attachments
|
|
67
127
|
|
|
68
128
|
```typescript
|
|
@@ -81,6 +141,39 @@ const result = await client.generate({
|
|
|
81
141
|
});
|
|
82
142
|
```
|
|
83
143
|
|
|
144
|
+
## Webhooks
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
// Register a webhook
|
|
148
|
+
await client.registerWebhook({
|
|
149
|
+
requestId: 'req_123',
|
|
150
|
+
url: 'https://your-domain.com/webhooks/ai-director',
|
|
151
|
+
secret: 'your-webhook-secret',
|
|
152
|
+
retryCount: 3,
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// List webhooks
|
|
156
|
+
const webhooks = await client.listWebhooks();
|
|
157
|
+
|
|
158
|
+
// Update webhook
|
|
159
|
+
await client.updateWebhook('webhook_id', { retryCount: 5 });
|
|
160
|
+
|
|
161
|
+
// Unregister webhook
|
|
162
|
+
await client.unregisterWebhook('webhook_id');
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Thinking Mode (Reasoning Models)
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
const result = await client.generate({
|
|
169
|
+
chainId: 'reasoning-chain',
|
|
170
|
+
prompt: 'Solve this complex problem step by step',
|
|
171
|
+
options: {
|
|
172
|
+
thinkingMode: true, // Shows model reasoning process
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
```
|
|
176
|
+
|
|
84
177
|
## Configuration
|
|
85
178
|
|
|
86
179
|
| Option | Type | Default | Description |
|
|
@@ -90,7 +183,20 @@ const result = await client.generate({
|
|
|
90
183
|
| `timeout` | `number` | `600000` | Request timeout (10 min) |
|
|
91
184
|
| `maxRetries` | `number` | `3` | Max retry attempts |
|
|
92
185
|
| `debug` | `boolean` | `false` | Enable debug logging |
|
|
93
|
-
|
|
186
|
+
|
|
187
|
+
## Generate Options
|
|
188
|
+
|
|
189
|
+
| Option | Type | Default | Description |
|
|
190
|
+
|--------|------|---------|-------------|
|
|
191
|
+
| `chainId` | `string` | **required** | Fallback chain ID |
|
|
192
|
+
| `prompt` | `string` | **required** | The prompt to send |
|
|
193
|
+
| `schema` | `object` | - | JSON schema for validation |
|
|
194
|
+
| `cacheScope` | `'global' \| 'user' \| 'skip'` | `'global'` | Cache behavior |
|
|
195
|
+
| `signal` | `AbortSignal` | - | Cancellation signal |
|
|
196
|
+
| `maxRetries` | `number` | Client setting | Override retries |
|
|
197
|
+
| `requestId` | `string` | Auto-generated | Custom request ID |
|
|
198
|
+
| `files` | `FileAttachment[]` | - | File attachments |
|
|
199
|
+
| `useOptimized` | `boolean` | `true` | Use 3-step flow |
|
|
94
200
|
|
|
95
201
|
## API Methods
|
|
96
202
|
|
|
@@ -98,10 +204,16 @@ const result = await client.generate({
|
|
|
98
204
|
|--------|-------------|
|
|
99
205
|
| `generate(options)` | Generate content with fallback chain |
|
|
100
206
|
| `generateStream(options, callbacks)` | Stream JSON objects in real-time |
|
|
207
|
+
| `generateBatch(chainId, items)` | Process multiple prompts |
|
|
101
208
|
| `listModels()` | List available AI models |
|
|
102
209
|
| `listChains()` | List your fallback chains |
|
|
103
|
-
| `
|
|
104
|
-
| `
|
|
210
|
+
| `getUsage(options)` | Get usage statistics |
|
|
211
|
+
| `health()` | Check API health |
|
|
212
|
+
| `healthDetailed()` | Get detailed component health |
|
|
213
|
+
| `registerWebhook(config)` | Register async webhook |
|
|
214
|
+
| `unregisterWebhook(id)` | Remove a webhook |
|
|
215
|
+
| `listWebhooks()` | List all webhooks |
|
|
216
|
+
| `updateWebhook(id, updates)` | Modify webhook config |
|
|
105
217
|
|
|
106
218
|
## Error Handling
|
|
107
219
|
|
|
@@ -110,6 +222,9 @@ import {
|
|
|
110
222
|
RateLimitError,
|
|
111
223
|
TimeoutError,
|
|
112
224
|
AuthenticationError,
|
|
225
|
+
QuotaExceededError,
|
|
226
|
+
WorkerError,
|
|
227
|
+
FileProcessingError,
|
|
113
228
|
} from '@altsafe/aidirector';
|
|
114
229
|
|
|
115
230
|
try {
|
|
@@ -117,14 +232,38 @@ try {
|
|
|
117
232
|
} catch (error) {
|
|
118
233
|
if (error instanceof RateLimitError) {
|
|
119
234
|
console.log(`Retry after ${error.retryAfterMs}ms`);
|
|
235
|
+
} else if (error instanceof QuotaExceededError) {
|
|
236
|
+
console.log(`Quota exceeded: ${error.used}/${error.limit} (${error.tier})`);
|
|
120
237
|
} else if (error instanceof TimeoutError) {
|
|
121
|
-
console.log(
|
|
238
|
+
console.log(`Request timed out after ${error.timeoutMs}ms`);
|
|
122
239
|
} else if (error instanceof AuthenticationError) {
|
|
123
240
|
console.log('Invalid API key');
|
|
241
|
+
} else if (error instanceof WorkerError) {
|
|
242
|
+
console.log('Worker processing failed - will retry');
|
|
243
|
+
} else if (error instanceof FileProcessingError) {
|
|
244
|
+
console.log(`File error: ${error.reason} - ${error.filename}`);
|
|
124
245
|
}
|
|
125
246
|
}
|
|
126
247
|
```
|
|
127
248
|
|
|
249
|
+
## AI-Directed Caching
|
|
250
|
+
|
|
251
|
+
The AI can control caching by including a `_cache` directive in its JSON output:
|
|
252
|
+
|
|
253
|
+
```json
|
|
254
|
+
{
|
|
255
|
+
"data": { ... },
|
|
256
|
+
"_cache": { "scope": "user" }
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Scopes:
|
|
261
|
+
- `global` - Share response across all users (default)
|
|
262
|
+
- `user` - Cache per-user only
|
|
263
|
+
- `skip` - Do not cache this response
|
|
264
|
+
|
|
265
|
+
The directive is automatically stripped from the final response.
|
|
266
|
+
|
|
128
267
|
## Pricing
|
|
129
268
|
|
|
130
269
|
BYOK (Bring Your Own Key) - You pay for AI costs directly to providers.
|
package/dist/index.d.mts
CHANGED
|
@@ -92,6 +92,35 @@ interface GenerateOptions {
|
|
|
92
92
|
* @default true
|
|
93
93
|
*/
|
|
94
94
|
useOptimized?: boolean;
|
|
95
|
+
/**
|
|
96
|
+
* Cache scope for this request.
|
|
97
|
+
* - 'global': Cache is shared across all users (default)
|
|
98
|
+
* - 'user': Cache is scoped to the authenticated user
|
|
99
|
+
* - 'skip': Do not cache this response
|
|
100
|
+
* @default 'global'
|
|
101
|
+
*/
|
|
102
|
+
cacheScope?: 'global' | 'user' | 'skip';
|
|
103
|
+
/**
|
|
104
|
+
* AbortSignal for request cancellation.
|
|
105
|
+
* Pass a signal from an AbortController to cancel the request.
|
|
106
|
+
* @example
|
|
107
|
+
* ```typescript
|
|
108
|
+
* const controller = new AbortController();
|
|
109
|
+
* setTimeout(() => controller.abort(), 5000);
|
|
110
|
+
* await client.generate({ chainId: 'x', prompt: 'y', signal: controller.signal });
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
signal?: AbortSignal;
|
|
114
|
+
/**
|
|
115
|
+
* Override max retries for this specific request.
|
|
116
|
+
* Set to 0 to disable retries for idempotent-sensitive operations.
|
|
117
|
+
*/
|
|
118
|
+
maxRetries?: number;
|
|
119
|
+
/**
|
|
120
|
+
* Client-generated request ID for tracing and debugging.
|
|
121
|
+
* If not provided, one will be generated automatically.
|
|
122
|
+
*/
|
|
123
|
+
requestId?: string;
|
|
95
124
|
/**
|
|
96
125
|
* Generation parameters
|
|
97
126
|
*/
|
|
@@ -142,6 +171,12 @@ interface GenerationParameters {
|
|
|
142
171
|
* System prompt to prepend
|
|
143
172
|
*/
|
|
144
173
|
systemPrompt?: string;
|
|
174
|
+
/**
|
|
175
|
+
* Enable thinking/reasoning mode for supported models.
|
|
176
|
+
* When enabled, the model will show its reasoning process.
|
|
177
|
+
* Only works with models that support thinking (e.g., Gemini 2.0 Flash Thinking).
|
|
178
|
+
*/
|
|
179
|
+
thinkingMode?: boolean;
|
|
145
180
|
}
|
|
146
181
|
/**
|
|
147
182
|
* Result from the generate method
|
|
@@ -729,6 +764,43 @@ declare class AIDirector {
|
|
|
729
764
|
registered: boolean;
|
|
730
765
|
message: string;
|
|
731
766
|
}>;
|
|
767
|
+
/**
|
|
768
|
+
* Unregister a webhook
|
|
769
|
+
*
|
|
770
|
+
* @param webhookId - ID of the webhook to unregister
|
|
771
|
+
* @returns Unregistration result
|
|
772
|
+
*/
|
|
773
|
+
unregisterWebhook(webhookId: string): Promise<{
|
|
774
|
+
unregistered: boolean;
|
|
775
|
+
message: string;
|
|
776
|
+
}>;
|
|
777
|
+
/**
|
|
778
|
+
* List all registered webhooks
|
|
779
|
+
*
|
|
780
|
+
* @returns Array of webhook configurations
|
|
781
|
+
*/
|
|
782
|
+
listWebhooks(): Promise<Array<{
|
|
783
|
+
id: string;
|
|
784
|
+
requestId: string;
|
|
785
|
+
url: string;
|
|
786
|
+
status: 'pending' | 'delivered' | 'failed';
|
|
787
|
+
retryCount: number;
|
|
788
|
+
createdAt: string;
|
|
789
|
+
}>>;
|
|
790
|
+
/**
|
|
791
|
+
* Update a webhook configuration
|
|
792
|
+
*
|
|
793
|
+
* @param webhookId - ID of the webhook to update
|
|
794
|
+
* @param updates - Fields to update
|
|
795
|
+
* @returns Updated webhook info
|
|
796
|
+
*/
|
|
797
|
+
updateWebhook(webhookId: string, updates: {
|
|
798
|
+
url?: string;
|
|
799
|
+
retryCount?: number;
|
|
800
|
+
}): Promise<{
|
|
801
|
+
updated: boolean;
|
|
802
|
+
message: string;
|
|
803
|
+
}>;
|
|
732
804
|
/**
|
|
733
805
|
* Get detailed health information including component status
|
|
734
806
|
*
|
|
@@ -871,6 +943,46 @@ declare class ValidationError extends AIDirectorError {
|
|
|
871
943
|
declare class ServerError extends AIDirectorError {
|
|
872
944
|
constructor(message: string, statusCode?: number);
|
|
873
945
|
}
|
|
946
|
+
/**
|
|
947
|
+
* Quota exceeded error - monthly request limit reached
|
|
948
|
+
*/
|
|
949
|
+
declare class QuotaExceededError extends AIDirectorError {
|
|
950
|
+
readonly tier: string;
|
|
951
|
+
readonly limit: number;
|
|
952
|
+
readonly used: number;
|
|
953
|
+
constructor(message: string, options: {
|
|
954
|
+
tier: string;
|
|
955
|
+
limit: number;
|
|
956
|
+
used: number;
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
/**
|
|
960
|
+
* Worker error - Cloudflare Worker execution failed
|
|
961
|
+
*/
|
|
962
|
+
declare class WorkerError extends AIDirectorError {
|
|
963
|
+
readonly workerDurationMs?: number;
|
|
964
|
+
constructor(message: string, workerDurationMs?: number);
|
|
965
|
+
}
|
|
966
|
+
/**
|
|
967
|
+
* File processing error - attachment handling failed
|
|
968
|
+
*/
|
|
969
|
+
declare class FileProcessingError extends AIDirectorError {
|
|
970
|
+
readonly filename?: string;
|
|
971
|
+
readonly mimeType?: string;
|
|
972
|
+
readonly reason: 'unsupported_type' | 'conversion_failed' | 'too_large' | 'corrupted';
|
|
973
|
+
constructor(message: string, options: {
|
|
974
|
+
filename?: string;
|
|
975
|
+
mimeType?: string;
|
|
976
|
+
reason: 'unsupported_type' | 'conversion_failed' | 'too_large' | 'corrupted';
|
|
977
|
+
});
|
|
978
|
+
}
|
|
979
|
+
/**
|
|
980
|
+
* Invalid schema error - provided schema is malformed
|
|
981
|
+
*/
|
|
982
|
+
declare class InvalidSchemaError extends AIDirectorError {
|
|
983
|
+
readonly schemaPath?: string;
|
|
984
|
+
constructor(message: string, schemaPath?: string);
|
|
985
|
+
}
|
|
874
986
|
/**
|
|
875
987
|
* Check if error is an AI Director error
|
|
876
988
|
*/
|
|
@@ -904,4 +1016,4 @@ declare function getKeyPrefix(secretKey: string): string;
|
|
|
904
1016
|
*/
|
|
905
1017
|
declare function isValidSecretKey(secretKey: string): boolean;
|
|
906
1018
|
|
|
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 };
|
|
1019
|
+
export { AIDirector, type AIDirectorConfig, AIDirectorError, AuthenticationError, ChainExecutionError, type ChainInfo, type ChainStep, ConfigurationError, type DetailedHealthResult, type FileAttachment, FileProcessingError, type GenerateData, type GenerateError, type GenerateMeta, type GenerateOptions, type GenerateResult, type GenerationParameters, type HealthResult, InvalidSchemaError, type ModelInfo, NetworkError, QuotaExceededError, RateLimitError, ServerError, type StreamCallbacks, type StreamCompleteResult, type StreamProgress, TimeoutError, type TokenUsage, type UsageStats, ValidationError, type WebhookConfig, WorkerError, generateSignature, getKeyPrefix, isAIDirectorError, isRetryableError, isValidSecretKey };
|
package/dist/index.d.ts
CHANGED
|
@@ -92,6 +92,35 @@ interface GenerateOptions {
|
|
|
92
92
|
* @default true
|
|
93
93
|
*/
|
|
94
94
|
useOptimized?: boolean;
|
|
95
|
+
/**
|
|
96
|
+
* Cache scope for this request.
|
|
97
|
+
* - 'global': Cache is shared across all users (default)
|
|
98
|
+
* - 'user': Cache is scoped to the authenticated user
|
|
99
|
+
* - 'skip': Do not cache this response
|
|
100
|
+
* @default 'global'
|
|
101
|
+
*/
|
|
102
|
+
cacheScope?: 'global' | 'user' | 'skip';
|
|
103
|
+
/**
|
|
104
|
+
* AbortSignal for request cancellation.
|
|
105
|
+
* Pass a signal from an AbortController to cancel the request.
|
|
106
|
+
* @example
|
|
107
|
+
* ```typescript
|
|
108
|
+
* const controller = new AbortController();
|
|
109
|
+
* setTimeout(() => controller.abort(), 5000);
|
|
110
|
+
* await client.generate({ chainId: 'x', prompt: 'y', signal: controller.signal });
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
signal?: AbortSignal;
|
|
114
|
+
/**
|
|
115
|
+
* Override max retries for this specific request.
|
|
116
|
+
* Set to 0 to disable retries for idempotent-sensitive operations.
|
|
117
|
+
*/
|
|
118
|
+
maxRetries?: number;
|
|
119
|
+
/**
|
|
120
|
+
* Client-generated request ID for tracing and debugging.
|
|
121
|
+
* If not provided, one will be generated automatically.
|
|
122
|
+
*/
|
|
123
|
+
requestId?: string;
|
|
95
124
|
/**
|
|
96
125
|
* Generation parameters
|
|
97
126
|
*/
|
|
@@ -142,6 +171,12 @@ interface GenerationParameters {
|
|
|
142
171
|
* System prompt to prepend
|
|
143
172
|
*/
|
|
144
173
|
systemPrompt?: string;
|
|
174
|
+
/**
|
|
175
|
+
* Enable thinking/reasoning mode for supported models.
|
|
176
|
+
* When enabled, the model will show its reasoning process.
|
|
177
|
+
* Only works with models that support thinking (e.g., Gemini 2.0 Flash Thinking).
|
|
178
|
+
*/
|
|
179
|
+
thinkingMode?: boolean;
|
|
145
180
|
}
|
|
146
181
|
/**
|
|
147
182
|
* Result from the generate method
|
|
@@ -729,6 +764,43 @@ declare class AIDirector {
|
|
|
729
764
|
registered: boolean;
|
|
730
765
|
message: string;
|
|
731
766
|
}>;
|
|
767
|
+
/**
|
|
768
|
+
* Unregister a webhook
|
|
769
|
+
*
|
|
770
|
+
* @param webhookId - ID of the webhook to unregister
|
|
771
|
+
* @returns Unregistration result
|
|
772
|
+
*/
|
|
773
|
+
unregisterWebhook(webhookId: string): Promise<{
|
|
774
|
+
unregistered: boolean;
|
|
775
|
+
message: string;
|
|
776
|
+
}>;
|
|
777
|
+
/**
|
|
778
|
+
* List all registered webhooks
|
|
779
|
+
*
|
|
780
|
+
* @returns Array of webhook configurations
|
|
781
|
+
*/
|
|
782
|
+
listWebhooks(): Promise<Array<{
|
|
783
|
+
id: string;
|
|
784
|
+
requestId: string;
|
|
785
|
+
url: string;
|
|
786
|
+
status: 'pending' | 'delivered' | 'failed';
|
|
787
|
+
retryCount: number;
|
|
788
|
+
createdAt: string;
|
|
789
|
+
}>>;
|
|
790
|
+
/**
|
|
791
|
+
* Update a webhook configuration
|
|
792
|
+
*
|
|
793
|
+
* @param webhookId - ID of the webhook to update
|
|
794
|
+
* @param updates - Fields to update
|
|
795
|
+
* @returns Updated webhook info
|
|
796
|
+
*/
|
|
797
|
+
updateWebhook(webhookId: string, updates: {
|
|
798
|
+
url?: string;
|
|
799
|
+
retryCount?: number;
|
|
800
|
+
}): Promise<{
|
|
801
|
+
updated: boolean;
|
|
802
|
+
message: string;
|
|
803
|
+
}>;
|
|
732
804
|
/**
|
|
733
805
|
* Get detailed health information including component status
|
|
734
806
|
*
|
|
@@ -871,6 +943,46 @@ declare class ValidationError extends AIDirectorError {
|
|
|
871
943
|
declare class ServerError extends AIDirectorError {
|
|
872
944
|
constructor(message: string, statusCode?: number);
|
|
873
945
|
}
|
|
946
|
+
/**
|
|
947
|
+
* Quota exceeded error - monthly request limit reached
|
|
948
|
+
*/
|
|
949
|
+
declare class QuotaExceededError extends AIDirectorError {
|
|
950
|
+
readonly tier: string;
|
|
951
|
+
readonly limit: number;
|
|
952
|
+
readonly used: number;
|
|
953
|
+
constructor(message: string, options: {
|
|
954
|
+
tier: string;
|
|
955
|
+
limit: number;
|
|
956
|
+
used: number;
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
/**
|
|
960
|
+
* Worker error - Cloudflare Worker execution failed
|
|
961
|
+
*/
|
|
962
|
+
declare class WorkerError extends AIDirectorError {
|
|
963
|
+
readonly workerDurationMs?: number;
|
|
964
|
+
constructor(message: string, workerDurationMs?: number);
|
|
965
|
+
}
|
|
966
|
+
/**
|
|
967
|
+
* File processing error - attachment handling failed
|
|
968
|
+
*/
|
|
969
|
+
declare class FileProcessingError extends AIDirectorError {
|
|
970
|
+
readonly filename?: string;
|
|
971
|
+
readonly mimeType?: string;
|
|
972
|
+
readonly reason: 'unsupported_type' | 'conversion_failed' | 'too_large' | 'corrupted';
|
|
973
|
+
constructor(message: string, options: {
|
|
974
|
+
filename?: string;
|
|
975
|
+
mimeType?: string;
|
|
976
|
+
reason: 'unsupported_type' | 'conversion_failed' | 'too_large' | 'corrupted';
|
|
977
|
+
});
|
|
978
|
+
}
|
|
979
|
+
/**
|
|
980
|
+
* Invalid schema error - provided schema is malformed
|
|
981
|
+
*/
|
|
982
|
+
declare class InvalidSchemaError extends AIDirectorError {
|
|
983
|
+
readonly schemaPath?: string;
|
|
984
|
+
constructor(message: string, schemaPath?: string);
|
|
985
|
+
}
|
|
874
986
|
/**
|
|
875
987
|
* Check if error is an AI Director error
|
|
876
988
|
*/
|
|
@@ -904,4 +1016,4 @@ declare function getKeyPrefix(secretKey: string): string;
|
|
|
904
1016
|
*/
|
|
905
1017
|
declare function isValidSecretKey(secretKey: string): boolean;
|
|
906
1018
|
|
|
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 };
|
|
1019
|
+
export { AIDirector, type AIDirectorConfig, AIDirectorError, AuthenticationError, ChainExecutionError, type ChainInfo, type ChainStep, ConfigurationError, type DetailedHealthResult, type FileAttachment, FileProcessingError, type GenerateData, type GenerateError, type GenerateMeta, type GenerateOptions, type GenerateResult, type GenerationParameters, type HealthResult, InvalidSchemaError, type ModelInfo, NetworkError, QuotaExceededError, RateLimitError, ServerError, type StreamCallbacks, type StreamCompleteResult, type StreamProgress, TimeoutError, type TokenUsage, type UsageStats, ValidationError, type WebhookConfig, WorkerError, generateSignature, getKeyPrefix, isAIDirectorError, isRetryableError, isValidSecretKey };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
'use strict';var
|
|
2
|
-
`),
|
|
3
|
-
`),
|
|
4
|
-
`);m=
|
|
1
|
+
'use strict';var K=typeof process<"u"&&process.versions?.node;async function G(n){let{createHash:e}=await import('crypto');return e("sha256").update(n).digest("hex")}async function L(n,e,t,r,a){let{createHmac:s}=await import('crypto'),c=[e.toUpperCase(),t,a.toString(),r].join(`
|
|
2
|
+
`),d=await G(n);return s("sha256",d).update(c).digest("hex")}async function W(n){let t=new TextEncoder().encode(n),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 q(n,e,t,r,a){let s=new TextEncoder,c=[e.toUpperCase(),t,a.toString(),r].join(`
|
|
3
|
+
`),d=await W(n),o=s.encode(d),i=await crypto.subtle.importKey("raw",o,{name:"HMAC",hash:"SHA-256"},false,["sign"]),m=await crypto.subtle.sign("HMAC",i,s.encode(c));return Array.from(new Uint8Array(m)).map(p=>p.toString(16).padStart(2,"0")).join("")}async function S(n,e,t,r,a){return K?L(n,e,t,r,a):q(n,e,t,r,a)}function I(n){return n.slice(0,12)}function U(n){return typeof n=="string"&&n.startsWith("aid_sk_")&&n.length>=20}var u=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;}},k=class extends u{constructor(e){super(e,"CONFIGURATION_ERROR",{retryable:false}),this.name="ConfigurationError";}},A=class extends u{constructor(e,t){super(e,"AUTH_ERROR",{retryable:false,statusCode:t}),this.name="AuthenticationError";}},E=class extends u{constructor(e,t=6e4){super(e,"RATE_LIMITED",{retryable:true,statusCode:429}),this.name="RateLimitError",this.retryAfterMs=t;}},R=class extends u{constructor(e){super(`Request timed out after ${e}ms`,"TIMEOUT",{retryable:true}),this.name="TimeoutError",this.timeoutMs=e;}},w=class extends u{constructor(e,t){super(e,"NETWORK_ERROR",{retryable:true,cause:t}),this.name="NetworkError";}},y=class extends u{constructor(e,t=[]){super(e,"CHAIN_FAILED",{retryable:false}),this.name="ChainExecutionError",this.attemptedModels=t;}},C=class extends u{constructor(e,t=[]){super(e,"VALIDATION_ERROR",{retryable:false}),this.name="ValidationError",this.validationErrors=t;}},g=class extends u{constructor(e,t=500){super(e,"SERVER_ERROR",{retryable:true,statusCode:t}),this.name="ServerError";}},P=class extends u{constructor(e,t){super(e,"QUOTA_EXCEEDED",{retryable:false,statusCode:402}),this.name="QuotaExceededError",this.tier=t.tier,this.limit=t.limit,this.used=t.used;}},v=class extends u{constructor(e,t){super(e,"WORKER_ERROR",{retryable:true}),this.name="WorkerError",this.workerDurationMs=t;}},O=class extends u{constructor(e,t){super(e,"FILE_PROCESSING_ERROR",{retryable:false}),this.name="FileProcessingError",this.filename=t.filename,this.mimeType=t.mimeType,this.reason=t.reason;}},D=class extends u{constructor(e,t){super(e,"INVALID_SCHEMA",{retryable:false}),this.name="InvalidSchemaError",this.schemaPath=t;}};function _(n){return n instanceof u}function F(n){return _(n)?n.retryable:false}var H="http://localhost:3000",$=6e5,B=3,N=[1e3,2e3,5e3];function J(n){let e=n.trim(),t=e.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/);t&&(e=t[1].trim());let r=e.indexOf("{"),a=e.indexOf("["),s;if(r===-1&&a===-1)return e;r===-1?s=a:a===-1?s=r:s=Math.min(r,a);let c=e[s],d=c==="{"?"}":"]",o=0,i=false,m=false,f=-1;for(let h=s;h<e.length;h++){let b=e[h];if(m){m=false;continue}if(b==="\\"&&i){m=true;continue}if(b==='"'&&!m){i=!i;continue}if(!i){if(b===c)o++;else if(b===d&&(o--,o===0)){f=h;break}}}if(f!==-1)return e.slice(s,f+1);let p=e.lastIndexOf(d);return p>s?e.slice(s,p+1):e}var M=class n{constructor(e){if(!e.secretKey)throw new k("secretKey is required");if(!U(e.secretKey))throw new k("Invalid secret key format. Expected format: aid_sk_<key>");this.secretKey=e.secretKey,this.baseUrl=(e.baseUrl||H).replace(/\/$/,""),this.timeout=e.timeout??$,this.maxRetries=e.maxRetries??B,this.keyPrefix=I(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 y("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}),o=await(await this.fetchWithTimeout(s.workerUrl,{method:"POST",headers:{"Content-Type":"application/json"},body:c},e.timeout??this.timeout)).json();if(!o.success||!o.content)return {success:false,data:{valid:[],invalid:[]},meta:{cached:false,modelUsed:o.modelId||"",tokensUsed:o.tokensUsed||{input:0,output:0},latencyMs:Date.now()-t,attemptedModels:[o.modelId||""]},error:{code:o.errorCode||"GENERATION_FAILED",message:o.errorMessage||"Generation failed",retryable:false}};let i=[],m=[],f=J(o.content);try{let h=JSON.parse(f);Array.isArray(h)?i=h:i=[h];}catch{i=[f];}o.completionToken&&this.cacheCompletionAsync({completionToken:o.completionToken,content:o.content,tokensUsed:o.tokensUsed||{input:0,output:0},finishReason:o.finishReason||"stop",chainId:e.chainId,modelUsed:o.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 p=Date.now()-t;return this.debug&&this.log("generate success (optimized 3-step)",{latencyMs:p,modelUsed:o.modelId,tokensUsed:o.tokensUsed}),{success:true,data:{valid:i,invalid:m},meta:{cached:false,modelUsed:o.modelId,tokensUsed:o.tokensUsed||{input:0,output:0},latencyMs:p,attemptedModels:[o.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 o=0;o<=this.maxRetries;o++)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:o+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 ${o+1} failed`,{error:s.message}),!this.isRetryable(i)||o>=this.maxRetries)break;let m=N[Math.min(o,N.length-1)];i instanceof E&&i.retryAfterMs>m?await this.sleep(i.retryAfterMs):await this.sleep(m);}let c=Date.now()-t,d=s instanceof R;return {success:false,data:{valid:[],invalid:[]},meta:{cached:false,modelUsed:"",tokensUsed:{input:0,output:0},latencyMs:c,attemptedModels:[]},error:{code:d?"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 S(this.secretKey,"POST",r,a,s),d=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(!d.ok){let p=await d.json();throw this.parseError(d.status,p)}let o=d.body?.getReader();if(!o)throw new w("Response body is not readable");let i=new TextDecoder,m="",f=[];for(;;){let{done:p,value:h}=await o.read();if(p)break;m+=i.decode(h,{stream:!0});let b=m.split(`
|
|
4
|
+
`);m=b.pop()||"";let T="";for(let x of b){if(x.startsWith("event: ")){T=x.slice(7).trim();continue}if(x.startsWith("data: ")){let j=x.slice(6);if(j==="[DONE]")break;try{let l=JSON.parse(j);switch(T){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 y(l.error||"Stream error",["STREAM_ERROR"]));break;default:l.chunk&&t.onChunk?.(l.chunk);}}catch{}T="";}}}}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 g(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 g(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 g(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 n({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 g(t.error?.message||"Failed to register webhook");return t.data}async unregisterWebhook(e){let t=await this.makeAuthenticatedRequest(`/api/v1/webhooks/${e}`,"DELETE","");if(!t.success||!t.data)throw new g(t.error?.message||"Failed to unregister webhook");return t.data}async listWebhooks(){let e=await this.makeAuthenticatedRequest("/api/v1/webhooks","GET","");if(!e.success)throw new g(e.error?.message||"Failed to list webhooks");return e.data||[]}async updateWebhook(e,t){let r=await this.makeAuthenticatedRequest(`/api/v1/webhooks/${e}`,"PATCH",JSON.stringify(t));if(!r.success||!r.data)throw new g(r.error?.message||"Failed to update webhook");return r.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 y(a.error?.message||"Batch generation failed",[]);return a.data}async makeAuthenticatedRequest(e,t,r,a){let s=Date.now(),c=await S(this.secretKey,t,e,r,s),d=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),o=await d.json();if(!d.ok)throw this.parseError(d.status,o);return o}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 R(r):c instanceof Error?new w(c.message,c):new w(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 A(r,e);case 429:let s=t?.retryAfterMs||6e4;return new E(r,s);case 500:case 502:case 503:case 504:return new g(r,e);default:return a==="CHAIN_FAILED"?new y(r,t?.error?.attemptedModels||[]):new u(r,a,{statusCode:e,retryable:e>=500})}}isRetryable(e){if(e instanceof u)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=M;exports.AIDirectorError=u;exports.AuthenticationError=A;exports.ChainExecutionError=y;exports.ConfigurationError=k;exports.FileProcessingError=O;exports.InvalidSchemaError=D;exports.NetworkError=w;exports.QuotaExceededError=P;exports.RateLimitError=E;exports.ServerError=g;exports.TimeoutError=R;exports.ValidationError=C;exports.WorkerError=v;exports.generateSignature=S;exports.getKeyPrefix=I;exports.isAIDirectorError=_;exports.isRetryableError=F;exports.isValidSecretKey=U;
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var
|
|
2
|
-
`),
|
|
3
|
-
`),
|
|
4
|
-
`);m=
|
|
1
|
+
var K=typeof process<"u"&&process.versions?.node;async function G(n){let{createHash:e}=await import('crypto');return e("sha256").update(n).digest("hex")}async function L(n,e,t,r,a){let{createHmac:s}=await import('crypto'),c=[e.toUpperCase(),t,a.toString(),r].join(`
|
|
2
|
+
`),d=await G(n);return s("sha256",d).update(c).digest("hex")}async function W(n){let t=new TextEncoder().encode(n),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 q(n,e,t,r,a){let s=new TextEncoder,c=[e.toUpperCase(),t,a.toString(),r].join(`
|
|
3
|
+
`),d=await W(n),o=s.encode(d),i=await crypto.subtle.importKey("raw",o,{name:"HMAC",hash:"SHA-256"},false,["sign"]),m=await crypto.subtle.sign("HMAC",i,s.encode(c));return Array.from(new Uint8Array(m)).map(p=>p.toString(16).padStart(2,"0")).join("")}async function S(n,e,t,r,a){return K?L(n,e,t,r,a):q(n,e,t,r,a)}function I(n){return n.slice(0,12)}function U(n){return typeof n=="string"&&n.startsWith("aid_sk_")&&n.length>=20}var u=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;}},k=class extends u{constructor(e){super(e,"CONFIGURATION_ERROR",{retryable:false}),this.name="ConfigurationError";}},A=class extends u{constructor(e,t){super(e,"AUTH_ERROR",{retryable:false,statusCode:t}),this.name="AuthenticationError";}},E=class extends u{constructor(e,t=6e4){super(e,"RATE_LIMITED",{retryable:true,statusCode:429}),this.name="RateLimitError",this.retryAfterMs=t;}},R=class extends u{constructor(e){super(`Request timed out after ${e}ms`,"TIMEOUT",{retryable:true}),this.name="TimeoutError",this.timeoutMs=e;}},w=class extends u{constructor(e,t){super(e,"NETWORK_ERROR",{retryable:true,cause:t}),this.name="NetworkError";}},y=class extends u{constructor(e,t=[]){super(e,"CHAIN_FAILED",{retryable:false}),this.name="ChainExecutionError",this.attemptedModels=t;}},C=class extends u{constructor(e,t=[]){super(e,"VALIDATION_ERROR",{retryable:false}),this.name="ValidationError",this.validationErrors=t;}},g=class extends u{constructor(e,t=500){super(e,"SERVER_ERROR",{retryable:true,statusCode:t}),this.name="ServerError";}},P=class extends u{constructor(e,t){super(e,"QUOTA_EXCEEDED",{retryable:false,statusCode:402}),this.name="QuotaExceededError",this.tier=t.tier,this.limit=t.limit,this.used=t.used;}},v=class extends u{constructor(e,t){super(e,"WORKER_ERROR",{retryable:true}),this.name="WorkerError",this.workerDurationMs=t;}},O=class extends u{constructor(e,t){super(e,"FILE_PROCESSING_ERROR",{retryable:false}),this.name="FileProcessingError",this.filename=t.filename,this.mimeType=t.mimeType,this.reason=t.reason;}},D=class extends u{constructor(e,t){super(e,"INVALID_SCHEMA",{retryable:false}),this.name="InvalidSchemaError",this.schemaPath=t;}};function _(n){return n instanceof u}function F(n){return _(n)?n.retryable:false}var H="http://localhost:3000",$=6e5,B=3,N=[1e3,2e3,5e3];function J(n){let e=n.trim(),t=e.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/);t&&(e=t[1].trim());let r=e.indexOf("{"),a=e.indexOf("["),s;if(r===-1&&a===-1)return e;r===-1?s=a:a===-1?s=r:s=Math.min(r,a);let c=e[s],d=c==="{"?"}":"]",o=0,i=false,m=false,f=-1;for(let h=s;h<e.length;h++){let b=e[h];if(m){m=false;continue}if(b==="\\"&&i){m=true;continue}if(b==='"'&&!m){i=!i;continue}if(!i){if(b===c)o++;else if(b===d&&(o--,o===0)){f=h;break}}}if(f!==-1)return e.slice(s,f+1);let p=e.lastIndexOf(d);return p>s?e.slice(s,p+1):e}var M=class n{constructor(e){if(!e.secretKey)throw new k("secretKey is required");if(!U(e.secretKey))throw new k("Invalid secret key format. Expected format: aid_sk_<key>");this.secretKey=e.secretKey,this.baseUrl=(e.baseUrl||H).replace(/\/$/,""),this.timeout=e.timeout??$,this.maxRetries=e.maxRetries??B,this.keyPrefix=I(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 y("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}),o=await(await this.fetchWithTimeout(s.workerUrl,{method:"POST",headers:{"Content-Type":"application/json"},body:c},e.timeout??this.timeout)).json();if(!o.success||!o.content)return {success:false,data:{valid:[],invalid:[]},meta:{cached:false,modelUsed:o.modelId||"",tokensUsed:o.tokensUsed||{input:0,output:0},latencyMs:Date.now()-t,attemptedModels:[o.modelId||""]},error:{code:o.errorCode||"GENERATION_FAILED",message:o.errorMessage||"Generation failed",retryable:false}};let i=[],m=[],f=J(o.content);try{let h=JSON.parse(f);Array.isArray(h)?i=h:i=[h];}catch{i=[f];}o.completionToken&&this.cacheCompletionAsync({completionToken:o.completionToken,content:o.content,tokensUsed:o.tokensUsed||{input:0,output:0},finishReason:o.finishReason||"stop",chainId:e.chainId,modelUsed:o.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 p=Date.now()-t;return this.debug&&this.log("generate success (optimized 3-step)",{latencyMs:p,modelUsed:o.modelId,tokensUsed:o.tokensUsed}),{success:true,data:{valid:i,invalid:m},meta:{cached:false,modelUsed:o.modelId,tokensUsed:o.tokensUsed||{input:0,output:0},latencyMs:p,attemptedModels:[o.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 o=0;o<=this.maxRetries;o++)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:o+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 ${o+1} failed`,{error:s.message}),!this.isRetryable(i)||o>=this.maxRetries)break;let m=N[Math.min(o,N.length-1)];i instanceof E&&i.retryAfterMs>m?await this.sleep(i.retryAfterMs):await this.sleep(m);}let c=Date.now()-t,d=s instanceof R;return {success:false,data:{valid:[],invalid:[]},meta:{cached:false,modelUsed:"",tokensUsed:{input:0,output:0},latencyMs:c,attemptedModels:[]},error:{code:d?"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 S(this.secretKey,"POST",r,a,s),d=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(!d.ok){let p=await d.json();throw this.parseError(d.status,p)}let o=d.body?.getReader();if(!o)throw new w("Response body is not readable");let i=new TextDecoder,m="",f=[];for(;;){let{done:p,value:h}=await o.read();if(p)break;m+=i.decode(h,{stream:!0});let b=m.split(`
|
|
4
|
+
`);m=b.pop()||"";let T="";for(let x of b){if(x.startsWith("event: ")){T=x.slice(7).trim();continue}if(x.startsWith("data: ")){let j=x.slice(6);if(j==="[DONE]")break;try{let l=JSON.parse(j);switch(T){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 y(l.error||"Stream error",["STREAM_ERROR"]));break;default:l.chunk&&t.onChunk?.(l.chunk);}}catch{}T="";}}}}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 g(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 g(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 g(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 n({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 g(t.error?.message||"Failed to register webhook");return t.data}async unregisterWebhook(e){let t=await this.makeAuthenticatedRequest(`/api/v1/webhooks/${e}`,"DELETE","");if(!t.success||!t.data)throw new g(t.error?.message||"Failed to unregister webhook");return t.data}async listWebhooks(){let e=await this.makeAuthenticatedRequest("/api/v1/webhooks","GET","");if(!e.success)throw new g(e.error?.message||"Failed to list webhooks");return e.data||[]}async updateWebhook(e,t){let r=await this.makeAuthenticatedRequest(`/api/v1/webhooks/${e}`,"PATCH",JSON.stringify(t));if(!r.success||!r.data)throw new g(r.error?.message||"Failed to update webhook");return r.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 y(a.error?.message||"Batch generation failed",[]);return a.data}async makeAuthenticatedRequest(e,t,r,a){let s=Date.now(),c=await S(this.secretKey,t,e,r,s),d=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),o=await d.json();if(!d.ok)throw this.parseError(d.status,o);return o}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 R(r):c instanceof Error?new w(c.message,c):new w(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 A(r,e);case 429:let s=t?.retryAfterMs||6e4;return new E(r,s);case 500:case 502:case 503:case 504:return new g(r,e);default:return a==="CHAIN_FAILED"?new y(r,t?.error?.attemptedModels||[]):new u(r,a,{statusCode:e,retryable:e>=500})}}isRetryable(e){if(e instanceof u)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{M as AIDirector,u as AIDirectorError,A as AuthenticationError,y as ChainExecutionError,k as ConfigurationError,O as FileProcessingError,D as InvalidSchemaError,w as NetworkError,P as QuotaExceededError,E as RateLimitError,g as ServerError,R as TimeoutError,C as ValidationError,v as WorkerError,S as generateSignature,I as getKeyPrefix,_ as isAIDirectorError,F as isRetryableError,U as isValidSecretKey};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@altsafe/aidirector",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
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",
|
|
@@ -84,4 +84,4 @@
|
|
|
84
84
|
"engines": {
|
|
85
85
|
"node": ">=18.0.0"
|
|
86
86
|
}
|
|
87
|
-
}
|
|
87
|
+
}
|