@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 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** - Hybrid Redis caching with 7-day TTL
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
- | `useOptimized` | `boolean` | `true` | Use 3-step Worker flow |
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
- | `getUsageStats(options)` | Get usage statistics |
104
- | `healthCheck()` | Check API health |
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('Request timed out');
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 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;
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 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};
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.0",
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
+ }