@altsafe/aidirector 1.2.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +177 -16
- package/dist/index.d.mts +153 -32
- package/dist/index.d.ts +153 -32
- package/dist/index.js +4 -4
- package/dist/index.mjs +4 -4
- package/package.json +8 -8
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,27 +1,35 @@
|
|
|
1
|
-
#
|
|
1
|
+
# hydra-aidirector - Client SDK
|
|
2
|
+
# hydra-aidirector
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
The official Node.js/TypeScript client for [Hydra](https://hydrai.dev).
|
|
5
|
+
|
|
6
|
+
Hydra is a high-performance AI API gateway that provides:
|
|
7
|
+
- 🔄 **Automatic Failover**: Never let an LLM outage break your app
|
|
8
|
+
- ⚡ **God-Tier Caching**: Reduce costs and latency with smart response caching
|
|
9
|
+
- 🛡️ **Self-Healing AI**: Auto-extract JSON, strip markdown, and repair malformed responses with `healingReport`
|
|
10
|
+
- 🧠 **Thinking Mode**: Support for reasoning models like Gemini 2.0 Flash Thinking
|
|
11
|
+
- 📊 **Detailed Usage**: Track token usage, latency, and costs per model
|
|
4
12
|
|
|
5
13
|
## Installation
|
|
6
14
|
|
|
7
15
|
```bash
|
|
8
|
-
npm install
|
|
16
|
+
npm install hydra-aidirector
|
|
9
17
|
# or
|
|
10
|
-
pnpm add
|
|
18
|
+
pnpm add hydra-aidirector
|
|
11
19
|
```
|
|
12
20
|
|
|
13
21
|
## Quick Start
|
|
14
22
|
|
|
15
23
|
```typescript
|
|
16
|
-
import {
|
|
24
|
+
import { Hydra } from 'hydra-aidirector';
|
|
17
25
|
|
|
18
|
-
const
|
|
19
|
-
secretKey: process.env.
|
|
26
|
+
const ai = new Hydra({
|
|
27
|
+
secretKey: process.env.HYDRA_SECRET_KEY!,
|
|
20
28
|
baseUrl: 'https://your-instance.vercel.app',
|
|
21
29
|
});
|
|
22
30
|
|
|
23
31
|
// Generate content
|
|
24
|
-
const result = await
|
|
32
|
+
const result = await ai.generate({
|
|
25
33
|
chainId: 'my-chain',
|
|
26
34
|
prompt: 'Generate 5 user profiles',
|
|
27
35
|
});
|
|
@@ -37,8 +45,10 @@ if (result.success) {
|
|
|
37
45
|
- ⚡ **3-Step Architecture** - Token → Worker → Complete (minimizes costs)
|
|
38
46
|
- 📎 **File Attachments** - Upload and process documents
|
|
39
47
|
- 🔄 **Automatic Retries** - Exponential backoff on failures
|
|
40
|
-
- 💾 **Smart Caching** -
|
|
41
|
-
- 🎯 **TypeScript** - Full type safety
|
|
48
|
+
- 💾 **Smart Caching** - Two-tier (user/global) cache with AI-directed scoping
|
|
49
|
+
- 🎯 **TypeScript** - Full type safety with comprehensive types
|
|
50
|
+
- 🛑 **Request Cancellation** - Support for AbortSignal
|
|
51
|
+
- 🪝 **Webhooks** - Async notification callbacks
|
|
42
52
|
|
|
43
53
|
## Streaming (Recommended for Large Responses)
|
|
44
54
|
|
|
@@ -63,6 +73,64 @@ await client.generateStream(
|
|
|
63
73
|
);
|
|
64
74
|
```
|
|
65
75
|
|
|
76
|
+
## Batch Generation
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
const result = await client.generateBatch('my-chain', [
|
|
80
|
+
{ id: 'item1', prompt: 'Describe product A' },
|
|
81
|
+
{ id: 'item2', prompt: 'Describe product B' },
|
|
82
|
+
{ id: 'item3', prompt: 'Describe product C' },
|
|
83
|
+
]);
|
|
84
|
+
|
|
85
|
+
console.log(`Processed ${result.summary.succeeded}/${result.summary.total}`);
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Request Cancellation
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
const controller = new AbortController();
|
|
92
|
+
|
|
93
|
+
// Cancel after 5 seconds
|
|
94
|
+
setTimeout(() => controller.abort(), 5000);
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
const result = await client.generate({
|
|
98
|
+
chainId: 'my-chain',
|
|
99
|
+
prompt: 'Long running prompt',
|
|
100
|
+
signal: controller.signal,
|
|
101
|
+
});
|
|
102
|
+
} catch (error) {
|
|
103
|
+
if (error instanceof TimeoutError) {
|
|
104
|
+
console.log('Request was cancelled');
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Cache Control
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
// Global cache (shared across users - default)
|
|
113
|
+
const result = await client.generate({
|
|
114
|
+
chainId: 'my-chain',
|
|
115
|
+
prompt: 'Static content',
|
|
116
|
+
cacheScope: 'global',
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// User-scoped cache (private to user)
|
|
120
|
+
const userResult = await client.generate({
|
|
121
|
+
chainId: 'my-chain',
|
|
122
|
+
prompt: 'Personalized content',
|
|
123
|
+
cacheScope: 'user',
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// Skip cache entirely
|
|
127
|
+
const freshResult = await client.generate({
|
|
128
|
+
chainId: 'my-chain',
|
|
129
|
+
prompt: 'Always fresh',
|
|
130
|
+
cacheScope: 'skip',
|
|
131
|
+
});
|
|
132
|
+
```
|
|
133
|
+
|
|
66
134
|
## File Attachments
|
|
67
135
|
|
|
68
136
|
```typescript
|
|
@@ -81,16 +149,62 @@ const result = await client.generate({
|
|
|
81
149
|
});
|
|
82
150
|
```
|
|
83
151
|
|
|
152
|
+
## Webhooks
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
// Register a webhook
|
|
156
|
+
await client.registerWebhook({
|
|
157
|
+
requestId: 'req_123',
|
|
158
|
+
url: 'https://your-domain.com/webhooks/hydra',
|
|
159
|
+
secret: 'your-webhook-secret',
|
|
160
|
+
retryCount: 3,
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// List webhooks
|
|
164
|
+
const webhooks = await client.listWebhooks();
|
|
165
|
+
|
|
166
|
+
// Update webhook
|
|
167
|
+
await client.updateWebhook('webhook_id', { retryCount: 5 });
|
|
168
|
+
|
|
169
|
+
// Unregister webhook
|
|
170
|
+
await client.unregisterWebhook('webhook_id');
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Thinking Mode (Reasoning Models)
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
const result = await client.generate({
|
|
177
|
+
chainId: 'reasoning-chain',
|
|
178
|
+
prompt: 'Solve this complex problem step by step',
|
|
179
|
+
options: {
|
|
180
|
+
thinkingMode: true, // Shows model reasoning process
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
```
|
|
184
|
+
|
|
84
185
|
## Configuration
|
|
85
186
|
|
|
86
187
|
| Option | Type | Default | Description |
|
|
87
188
|
|--------|------|---------|-------------|
|
|
88
|
-
| `secretKey` | `string` | **required** | Your API key (`
|
|
189
|
+
| `secretKey` | `string` | **required** | Your API key (`hyd_sk_...`) |
|
|
89
190
|
| `baseUrl` | `string` | `http://localhost:3000` | API base URL |
|
|
90
191
|
| `timeout` | `number` | `600000` | Request timeout (10 min) |
|
|
91
192
|
| `maxRetries` | `number` | `3` | Max retry attempts |
|
|
92
193
|
| `debug` | `boolean` | `false` | Enable debug logging |
|
|
93
|
-
|
|
194
|
+
|
|
195
|
+
## Generate Options
|
|
196
|
+
|
|
197
|
+
| Option | Type | Default | Description |
|
|
198
|
+
|--------|------|---------|-------------|
|
|
199
|
+
| `chainId` | `string` | **required** | Fallback chain ID |
|
|
200
|
+
| `prompt` | `string` | **required** | The prompt to send |
|
|
201
|
+
| `schema` | `object` | - | JSON schema for validation |
|
|
202
|
+
| `cacheScope` | `'global' \| 'user' \| 'skip'` | `'global'` | Cache behavior |
|
|
203
|
+
| `signal` | `AbortSignal` | - | Cancellation signal |
|
|
204
|
+
| `maxRetries` | `number` | Client setting | Override retries |
|
|
205
|
+
| `requestId` | `string` | Auto-generated | Custom request ID |
|
|
206
|
+
| `files` | `FileAttachment[]` | - | File attachments |
|
|
207
|
+
| `useOptimized` | `boolean` | `true` | Use 3-step flow |
|
|
94
208
|
|
|
95
209
|
## API Methods
|
|
96
210
|
|
|
@@ -98,10 +212,16 @@ const result = await client.generate({
|
|
|
98
212
|
|--------|-------------|
|
|
99
213
|
| `generate(options)` | Generate content with fallback chain |
|
|
100
214
|
| `generateStream(options, callbacks)` | Stream JSON objects in real-time |
|
|
215
|
+
| `generateBatch(chainId, items)` | Process multiple prompts |
|
|
101
216
|
| `listModels()` | List available AI models |
|
|
102
217
|
| `listChains()` | List your fallback chains |
|
|
103
|
-
| `
|
|
104
|
-
| `
|
|
218
|
+
| `getUsage(options)` | Get usage statistics |
|
|
219
|
+
| `health()` | Check API health |
|
|
220
|
+
| `healthDetailed()` | Get detailed component health |
|
|
221
|
+
| `registerWebhook(config)` | Register async webhook |
|
|
222
|
+
| `unregisterWebhook(id)` | Remove a webhook |
|
|
223
|
+
| `listWebhooks()` | List all webhooks |
|
|
224
|
+
| `updateWebhook(id, updates)` | Modify webhook config |
|
|
105
225
|
|
|
106
226
|
## Error Handling
|
|
107
227
|
|
|
@@ -110,21 +230,62 @@ import {
|
|
|
110
230
|
RateLimitError,
|
|
111
231
|
TimeoutError,
|
|
112
232
|
AuthenticationError,
|
|
113
|
-
|
|
233
|
+
QuotaExceededError,
|
|
234
|
+
WorkerError,
|
|
235
|
+
FileProcessingError,
|
|
236
|
+
} from 'hydra-aidirector';
|
|
114
237
|
|
|
115
238
|
try {
|
|
116
239
|
const result = await client.generate({ ... });
|
|
117
240
|
} catch (error) {
|
|
118
241
|
if (error instanceof RateLimitError) {
|
|
119
242
|
console.log(`Retry after ${error.retryAfterMs}ms`);
|
|
243
|
+
} else if (error instanceof QuotaExceededError) {
|
|
244
|
+
console.log(`Quota exceeded: ${error.used}/${error.limit} (${error.tier})`);
|
|
120
245
|
} else if (error instanceof TimeoutError) {
|
|
121
|
-
console.log(
|
|
246
|
+
console.log(`Request timed out after ${error.timeoutMs}ms`);
|
|
122
247
|
} else if (error instanceof AuthenticationError) {
|
|
123
248
|
console.log('Invalid API key');
|
|
249
|
+
} else if (error instanceof WorkerError) {
|
|
250
|
+
console.log('Worker processing failed - will retry');
|
|
251
|
+
} else if (error instanceof FileProcessingError) {
|
|
252
|
+
console.log(`File error: ${error.reason} - ${error.filename}`);
|
|
124
253
|
}
|
|
125
254
|
}
|
|
126
255
|
```
|
|
127
256
|
|
|
257
|
+
## Self-Healing Reports
|
|
258
|
+
|
|
259
|
+
When `hydra-aidirector` fixes a malformed JSON response, it includes a `healingReport` in the data object:
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
const result = await client.generate({ ... });
|
|
263
|
+
|
|
264
|
+
if (result.success && result.meta.recovered) {
|
|
265
|
+
console.log('JSON was malformed but healed!');
|
|
266
|
+
console.log(result.data.healingReport);
|
|
267
|
+
// [{ original: "{name: 'foo'", healed: {name: "foo"}, fixes: ["Added missing brace"] }]
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## AI-Directed Caching
|
|
272
|
+
|
|
273
|
+
The AI can control caching by including a `_cache` directive in its JSON output:
|
|
274
|
+
|
|
275
|
+
```json
|
|
276
|
+
{
|
|
277
|
+
"data": { ... },
|
|
278
|
+
"_cache": { "scope": "user" }
|
|
279
|
+
}
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Scopes:
|
|
283
|
+
- `global` - Share response across all users (default)
|
|
284
|
+
- `user` - Cache per-user only
|
|
285
|
+
- `skip` - Do not cache this response
|
|
286
|
+
|
|
287
|
+
The directive is automatically stripped from the final response.
|
|
288
|
+
|
|
128
289
|
## Pricing
|
|
129
290
|
|
|
130
291
|
BYOK (Bring Your Own Key) - You pay for AI costs directly to providers.
|
package/dist/index.d.mts
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Hydra Client Types
|
|
3
3
|
* Type definitions for the SDK
|
|
4
4
|
*/
|
|
5
5
|
/**
|
|
6
|
-
* Configuration for
|
|
6
|
+
* Configuration for Hydra client
|
|
7
7
|
*/
|
|
8
|
-
interface
|
|
8
|
+
interface HydraConfig {
|
|
9
9
|
/**
|
|
10
|
-
* Your
|
|
11
|
-
* Get this from your dashboard at https://
|
|
12
|
-
* Format:
|
|
10
|
+
* Your Hydra secret key
|
|
11
|
+
* Get this from your dashboard at https://hydrai.dev/dashboard/keys
|
|
12
|
+
* Format: hyd_sk_<random>
|
|
13
13
|
*/
|
|
14
14
|
secretKey: string;
|
|
15
15
|
/**
|
|
16
16
|
* API base URL
|
|
17
17
|
* @default 'http://localhost:3000' in development
|
|
18
|
-
* @example 'https://api.
|
|
18
|
+
* @example 'https://api.hydrai.dev'
|
|
19
19
|
*/
|
|
20
20
|
baseUrl?: string;
|
|
21
21
|
/**
|
|
@@ -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
|
|
@@ -180,6 +215,15 @@ interface GenerateData {
|
|
|
180
215
|
* Raw AI response text (useful for debugging)
|
|
181
216
|
*/
|
|
182
217
|
rawContent?: string;
|
|
218
|
+
/**
|
|
219
|
+
* Report of auto-healed validation errors
|
|
220
|
+
* detailed log of how the system fixed invalid AI output
|
|
221
|
+
*/
|
|
222
|
+
healingReport?: Array<{
|
|
223
|
+
original: unknown;
|
|
224
|
+
healed: unknown;
|
|
225
|
+
fixes: string[];
|
|
226
|
+
}>;
|
|
183
227
|
}
|
|
184
228
|
/**
|
|
185
229
|
* Generation metadata
|
|
@@ -579,9 +623,9 @@ interface WebhookConfig {
|
|
|
579
623
|
}
|
|
580
624
|
|
|
581
625
|
/**
|
|
582
|
-
*
|
|
626
|
+
* Hydra Client SDK
|
|
583
627
|
*
|
|
584
|
-
* Production-grade client for the
|
|
628
|
+
* Production-grade client for the Hydra API gateway.
|
|
585
629
|
* Use in Next.js API routes, Server Actions, or any Node.js/Edge environment.
|
|
586
630
|
*
|
|
587
631
|
* Features:
|
|
@@ -593,11 +637,11 @@ interface WebhookConfig {
|
|
|
593
637
|
*
|
|
594
638
|
* @example
|
|
595
639
|
* ```typescript
|
|
596
|
-
* import {
|
|
640
|
+
* import { Hydra } from 'hydra-aidirector';
|
|
597
641
|
*
|
|
598
|
-
* const client = new
|
|
599
|
-
* secretKey: process.env.
|
|
600
|
-
* baseUrl: 'https://api.
|
|
642
|
+
* const client = new Hydra({
|
|
643
|
+
* secretKey: process.env.HYDRA_SECRET_KEY!,
|
|
644
|
+
* baseUrl: 'https://api.hydrai.dev',
|
|
601
645
|
* });
|
|
602
646
|
*
|
|
603
647
|
* const result = await client.generate({
|
|
@@ -611,20 +655,20 @@ interface WebhookConfig {
|
|
|
611
655
|
* }
|
|
612
656
|
* ```
|
|
613
657
|
*/
|
|
614
|
-
declare class
|
|
658
|
+
declare class Hydra {
|
|
615
659
|
private readonly secretKey;
|
|
616
660
|
private readonly baseUrl;
|
|
617
661
|
private readonly timeout;
|
|
618
662
|
private readonly maxRetries;
|
|
619
663
|
private readonly keyPrefix;
|
|
620
664
|
private readonly debug;
|
|
621
|
-
constructor(config:
|
|
665
|
+
constructor(config: HydraConfig);
|
|
622
666
|
/**
|
|
623
667
|
* Generate content using your fallback chain
|
|
624
668
|
*
|
|
625
669
|
* @param options - Generation options
|
|
626
670
|
* @returns Promise resolving to GenerateResult
|
|
627
|
-
* @throws
|
|
671
|
+
* @throws HydraError subclasses on failure
|
|
628
672
|
*
|
|
629
673
|
* @example
|
|
630
674
|
* ```typescript
|
|
@@ -711,9 +755,9 @@ declare class AIDirector {
|
|
|
711
755
|
* Useful for testing or switching environments
|
|
712
756
|
*
|
|
713
757
|
* @param overrides - Configuration overrides
|
|
714
|
-
* @returns New
|
|
758
|
+
* @returns New Hydra instance
|
|
715
759
|
*/
|
|
716
|
-
withConfig(overrides: Partial<
|
|
760
|
+
withConfig(overrides: Partial<HydraConfig>): Hydra;
|
|
717
761
|
/**
|
|
718
762
|
* Register a webhook for async notifications
|
|
719
763
|
*
|
|
@@ -729,6 +773,43 @@ declare class AIDirector {
|
|
|
729
773
|
registered: boolean;
|
|
730
774
|
message: string;
|
|
731
775
|
}>;
|
|
776
|
+
/**
|
|
777
|
+
* Unregister a webhook
|
|
778
|
+
*
|
|
779
|
+
* @param webhookId - ID of the webhook to unregister
|
|
780
|
+
* @returns Unregistration result
|
|
781
|
+
*/
|
|
782
|
+
unregisterWebhook(webhookId: string): Promise<{
|
|
783
|
+
unregistered: boolean;
|
|
784
|
+
message: string;
|
|
785
|
+
}>;
|
|
786
|
+
/**
|
|
787
|
+
* List all registered webhooks
|
|
788
|
+
*
|
|
789
|
+
* @returns Array of webhook configurations
|
|
790
|
+
*/
|
|
791
|
+
listWebhooks(): Promise<Array<{
|
|
792
|
+
id: string;
|
|
793
|
+
requestId: string;
|
|
794
|
+
url: string;
|
|
795
|
+
status: 'pending' | 'delivered' | 'failed';
|
|
796
|
+
retryCount: number;
|
|
797
|
+
createdAt: string;
|
|
798
|
+
}>>;
|
|
799
|
+
/**
|
|
800
|
+
* Update a webhook configuration
|
|
801
|
+
*
|
|
802
|
+
* @param webhookId - ID of the webhook to update
|
|
803
|
+
* @param updates - Fields to update
|
|
804
|
+
* @returns Updated webhook info
|
|
805
|
+
*/
|
|
806
|
+
updateWebhook(webhookId: string, updates: {
|
|
807
|
+
url?: string;
|
|
808
|
+
retryCount?: number;
|
|
809
|
+
}): Promise<{
|
|
810
|
+
updated: boolean;
|
|
811
|
+
message: string;
|
|
812
|
+
}>;
|
|
732
813
|
/**
|
|
733
814
|
* Get detailed health information including component status
|
|
734
815
|
*
|
|
@@ -802,13 +883,13 @@ declare class AIDirector {
|
|
|
802
883
|
}
|
|
803
884
|
|
|
804
885
|
/**
|
|
805
|
-
*
|
|
886
|
+
* Hydra Error Classes
|
|
806
887
|
* Structured error types for better error handling
|
|
807
888
|
*/
|
|
808
889
|
/**
|
|
809
|
-
* Base error class for
|
|
890
|
+
* Base error class for Hydra errors
|
|
810
891
|
*/
|
|
811
|
-
declare class
|
|
892
|
+
declare class HydraError extends Error {
|
|
812
893
|
readonly code: string;
|
|
813
894
|
readonly retryable: boolean;
|
|
814
895
|
readonly statusCode?: number;
|
|
@@ -822,59 +903,99 @@ declare class AIDirectorError extends Error {
|
|
|
822
903
|
/**
|
|
823
904
|
* Configuration error - invalid client setup
|
|
824
905
|
*/
|
|
825
|
-
declare class ConfigurationError extends
|
|
906
|
+
declare class ConfigurationError extends HydraError {
|
|
826
907
|
constructor(message: string);
|
|
827
908
|
}
|
|
828
909
|
/**
|
|
829
910
|
* Authentication error - invalid or expired credentials
|
|
830
911
|
*/
|
|
831
|
-
declare class AuthenticationError extends
|
|
912
|
+
declare class AuthenticationError extends HydraError {
|
|
832
913
|
constructor(message: string, statusCode?: number);
|
|
833
914
|
}
|
|
834
915
|
/**
|
|
835
916
|
* Rate limit error - too many requests
|
|
836
917
|
*/
|
|
837
|
-
declare class RateLimitError extends
|
|
918
|
+
declare class RateLimitError extends HydraError {
|
|
838
919
|
readonly retryAfterMs: number;
|
|
839
920
|
constructor(message: string, retryAfterMs?: number);
|
|
840
921
|
}
|
|
841
922
|
/**
|
|
842
923
|
* Timeout error - request took too long
|
|
843
924
|
*/
|
|
844
|
-
declare class TimeoutError extends
|
|
925
|
+
declare class TimeoutError extends HydraError {
|
|
845
926
|
readonly timeoutMs: number;
|
|
846
927
|
constructor(timeoutMs: number);
|
|
847
928
|
}
|
|
848
929
|
/**
|
|
849
930
|
* Network error - connection failed
|
|
850
931
|
*/
|
|
851
|
-
declare class NetworkError extends
|
|
932
|
+
declare class NetworkError extends HydraError {
|
|
852
933
|
constructor(message: string, cause?: Error);
|
|
853
934
|
}
|
|
854
935
|
/**
|
|
855
936
|
* Chain error - fallback chain execution failed
|
|
856
937
|
*/
|
|
857
|
-
declare class ChainExecutionError extends
|
|
938
|
+
declare class ChainExecutionError extends HydraError {
|
|
858
939
|
readonly attemptedModels: string[];
|
|
859
940
|
constructor(message: string, attemptedModels?: string[]);
|
|
860
941
|
}
|
|
861
942
|
/**
|
|
862
943
|
* Validation error - schema validation failed
|
|
863
944
|
*/
|
|
864
|
-
declare class ValidationError extends
|
|
945
|
+
declare class ValidationError extends HydraError {
|
|
865
946
|
readonly validationErrors: unknown[];
|
|
866
947
|
constructor(message: string, validationErrors?: unknown[]);
|
|
867
948
|
}
|
|
868
949
|
/**
|
|
869
950
|
* Server error - internal server error
|
|
870
951
|
*/
|
|
871
|
-
declare class ServerError extends
|
|
952
|
+
declare class ServerError extends HydraError {
|
|
872
953
|
constructor(message: string, statusCode?: number);
|
|
873
954
|
}
|
|
874
955
|
/**
|
|
875
|
-
*
|
|
956
|
+
* Quota exceeded error - monthly request limit reached
|
|
957
|
+
*/
|
|
958
|
+
declare class QuotaExceededError extends HydraError {
|
|
959
|
+
readonly tier: string;
|
|
960
|
+
readonly limit: number;
|
|
961
|
+
readonly used: number;
|
|
962
|
+
constructor(message: string, options: {
|
|
963
|
+
tier: string;
|
|
964
|
+
limit: number;
|
|
965
|
+
used: number;
|
|
966
|
+
});
|
|
967
|
+
}
|
|
968
|
+
/**
|
|
969
|
+
* Worker error - Cloudflare Worker execution failed
|
|
970
|
+
*/
|
|
971
|
+
declare class WorkerError extends HydraError {
|
|
972
|
+
readonly workerDurationMs?: number;
|
|
973
|
+
constructor(message: string, workerDurationMs?: number);
|
|
974
|
+
}
|
|
975
|
+
/**
|
|
976
|
+
* File processing error - attachment handling failed
|
|
977
|
+
*/
|
|
978
|
+
declare class FileProcessingError extends HydraError {
|
|
979
|
+
readonly filename?: string;
|
|
980
|
+
readonly mimeType?: string;
|
|
981
|
+
readonly reason: 'unsupported_type' | 'conversion_failed' | 'too_large' | 'corrupted';
|
|
982
|
+
constructor(message: string, options: {
|
|
983
|
+
filename?: string;
|
|
984
|
+
mimeType?: string;
|
|
985
|
+
reason: 'unsupported_type' | 'conversion_failed' | 'too_large' | 'corrupted';
|
|
986
|
+
});
|
|
987
|
+
}
|
|
988
|
+
/**
|
|
989
|
+
* Invalid schema error - provided schema is malformed
|
|
990
|
+
*/
|
|
991
|
+
declare class InvalidSchemaError extends HydraError {
|
|
992
|
+
readonly schemaPath?: string;
|
|
993
|
+
constructor(message: string, schemaPath?: string);
|
|
994
|
+
}
|
|
995
|
+
/**
|
|
996
|
+
* Check if error is an Hydra error
|
|
876
997
|
*/
|
|
877
|
-
declare function
|
|
998
|
+
declare function isHydraError(error: unknown): error is HydraError;
|
|
878
999
|
/**
|
|
879
1000
|
* Check if error is retryable
|
|
880
1001
|
*/
|
|
@@ -904,4 +1025,4 @@ declare function getKeyPrefix(secretKey: string): string;
|
|
|
904
1025
|
*/
|
|
905
1026
|
declare function isValidSecretKey(secretKey: string): boolean;
|
|
906
1027
|
|
|
907
|
-
export {
|
|
1028
|
+
export { 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, Hydra, type HydraConfig, HydraError, InvalidSchemaError, type ModelInfo, NetworkError, QuotaExceededError, RateLimitError, ServerError, type StreamCallbacks, type StreamCompleteResult, type StreamProgress, TimeoutError, type TokenUsage, type UsageStats, ValidationError, type WebhookConfig, WorkerError, generateSignature, getKeyPrefix, isHydraError, isRetryableError, isValidSecretKey };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Hydra Client Types
|
|
3
3
|
* Type definitions for the SDK
|
|
4
4
|
*/
|
|
5
5
|
/**
|
|
6
|
-
* Configuration for
|
|
6
|
+
* Configuration for Hydra client
|
|
7
7
|
*/
|
|
8
|
-
interface
|
|
8
|
+
interface HydraConfig {
|
|
9
9
|
/**
|
|
10
|
-
* Your
|
|
11
|
-
* Get this from your dashboard at https://
|
|
12
|
-
* Format:
|
|
10
|
+
* Your Hydra secret key
|
|
11
|
+
* Get this from your dashboard at https://hydrai.dev/dashboard/keys
|
|
12
|
+
* Format: hyd_sk_<random>
|
|
13
13
|
*/
|
|
14
14
|
secretKey: string;
|
|
15
15
|
/**
|
|
16
16
|
* API base URL
|
|
17
17
|
* @default 'http://localhost:3000' in development
|
|
18
|
-
* @example 'https://api.
|
|
18
|
+
* @example 'https://api.hydrai.dev'
|
|
19
19
|
*/
|
|
20
20
|
baseUrl?: string;
|
|
21
21
|
/**
|
|
@@ -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
|
|
@@ -180,6 +215,15 @@ interface GenerateData {
|
|
|
180
215
|
* Raw AI response text (useful for debugging)
|
|
181
216
|
*/
|
|
182
217
|
rawContent?: string;
|
|
218
|
+
/**
|
|
219
|
+
* Report of auto-healed validation errors
|
|
220
|
+
* detailed log of how the system fixed invalid AI output
|
|
221
|
+
*/
|
|
222
|
+
healingReport?: Array<{
|
|
223
|
+
original: unknown;
|
|
224
|
+
healed: unknown;
|
|
225
|
+
fixes: string[];
|
|
226
|
+
}>;
|
|
183
227
|
}
|
|
184
228
|
/**
|
|
185
229
|
* Generation metadata
|
|
@@ -579,9 +623,9 @@ interface WebhookConfig {
|
|
|
579
623
|
}
|
|
580
624
|
|
|
581
625
|
/**
|
|
582
|
-
*
|
|
626
|
+
* Hydra Client SDK
|
|
583
627
|
*
|
|
584
|
-
* Production-grade client for the
|
|
628
|
+
* Production-grade client for the Hydra API gateway.
|
|
585
629
|
* Use in Next.js API routes, Server Actions, or any Node.js/Edge environment.
|
|
586
630
|
*
|
|
587
631
|
* Features:
|
|
@@ -593,11 +637,11 @@ interface WebhookConfig {
|
|
|
593
637
|
*
|
|
594
638
|
* @example
|
|
595
639
|
* ```typescript
|
|
596
|
-
* import {
|
|
640
|
+
* import { Hydra } from 'hydra-aidirector';
|
|
597
641
|
*
|
|
598
|
-
* const client = new
|
|
599
|
-
* secretKey: process.env.
|
|
600
|
-
* baseUrl: 'https://api.
|
|
642
|
+
* const client = new Hydra({
|
|
643
|
+
* secretKey: process.env.HYDRA_SECRET_KEY!,
|
|
644
|
+
* baseUrl: 'https://api.hydrai.dev',
|
|
601
645
|
* });
|
|
602
646
|
*
|
|
603
647
|
* const result = await client.generate({
|
|
@@ -611,20 +655,20 @@ interface WebhookConfig {
|
|
|
611
655
|
* }
|
|
612
656
|
* ```
|
|
613
657
|
*/
|
|
614
|
-
declare class
|
|
658
|
+
declare class Hydra {
|
|
615
659
|
private readonly secretKey;
|
|
616
660
|
private readonly baseUrl;
|
|
617
661
|
private readonly timeout;
|
|
618
662
|
private readonly maxRetries;
|
|
619
663
|
private readonly keyPrefix;
|
|
620
664
|
private readonly debug;
|
|
621
|
-
constructor(config:
|
|
665
|
+
constructor(config: HydraConfig);
|
|
622
666
|
/**
|
|
623
667
|
* Generate content using your fallback chain
|
|
624
668
|
*
|
|
625
669
|
* @param options - Generation options
|
|
626
670
|
* @returns Promise resolving to GenerateResult
|
|
627
|
-
* @throws
|
|
671
|
+
* @throws HydraError subclasses on failure
|
|
628
672
|
*
|
|
629
673
|
* @example
|
|
630
674
|
* ```typescript
|
|
@@ -711,9 +755,9 @@ declare class AIDirector {
|
|
|
711
755
|
* Useful for testing or switching environments
|
|
712
756
|
*
|
|
713
757
|
* @param overrides - Configuration overrides
|
|
714
|
-
* @returns New
|
|
758
|
+
* @returns New Hydra instance
|
|
715
759
|
*/
|
|
716
|
-
withConfig(overrides: Partial<
|
|
760
|
+
withConfig(overrides: Partial<HydraConfig>): Hydra;
|
|
717
761
|
/**
|
|
718
762
|
* Register a webhook for async notifications
|
|
719
763
|
*
|
|
@@ -729,6 +773,43 @@ declare class AIDirector {
|
|
|
729
773
|
registered: boolean;
|
|
730
774
|
message: string;
|
|
731
775
|
}>;
|
|
776
|
+
/**
|
|
777
|
+
* Unregister a webhook
|
|
778
|
+
*
|
|
779
|
+
* @param webhookId - ID of the webhook to unregister
|
|
780
|
+
* @returns Unregistration result
|
|
781
|
+
*/
|
|
782
|
+
unregisterWebhook(webhookId: string): Promise<{
|
|
783
|
+
unregistered: boolean;
|
|
784
|
+
message: string;
|
|
785
|
+
}>;
|
|
786
|
+
/**
|
|
787
|
+
* List all registered webhooks
|
|
788
|
+
*
|
|
789
|
+
* @returns Array of webhook configurations
|
|
790
|
+
*/
|
|
791
|
+
listWebhooks(): Promise<Array<{
|
|
792
|
+
id: string;
|
|
793
|
+
requestId: string;
|
|
794
|
+
url: string;
|
|
795
|
+
status: 'pending' | 'delivered' | 'failed';
|
|
796
|
+
retryCount: number;
|
|
797
|
+
createdAt: string;
|
|
798
|
+
}>>;
|
|
799
|
+
/**
|
|
800
|
+
* Update a webhook configuration
|
|
801
|
+
*
|
|
802
|
+
* @param webhookId - ID of the webhook to update
|
|
803
|
+
* @param updates - Fields to update
|
|
804
|
+
* @returns Updated webhook info
|
|
805
|
+
*/
|
|
806
|
+
updateWebhook(webhookId: string, updates: {
|
|
807
|
+
url?: string;
|
|
808
|
+
retryCount?: number;
|
|
809
|
+
}): Promise<{
|
|
810
|
+
updated: boolean;
|
|
811
|
+
message: string;
|
|
812
|
+
}>;
|
|
732
813
|
/**
|
|
733
814
|
* Get detailed health information including component status
|
|
734
815
|
*
|
|
@@ -802,13 +883,13 @@ declare class AIDirector {
|
|
|
802
883
|
}
|
|
803
884
|
|
|
804
885
|
/**
|
|
805
|
-
*
|
|
886
|
+
* Hydra Error Classes
|
|
806
887
|
* Structured error types for better error handling
|
|
807
888
|
*/
|
|
808
889
|
/**
|
|
809
|
-
* Base error class for
|
|
890
|
+
* Base error class for Hydra errors
|
|
810
891
|
*/
|
|
811
|
-
declare class
|
|
892
|
+
declare class HydraError extends Error {
|
|
812
893
|
readonly code: string;
|
|
813
894
|
readonly retryable: boolean;
|
|
814
895
|
readonly statusCode?: number;
|
|
@@ -822,59 +903,99 @@ declare class AIDirectorError extends Error {
|
|
|
822
903
|
/**
|
|
823
904
|
* Configuration error - invalid client setup
|
|
824
905
|
*/
|
|
825
|
-
declare class ConfigurationError extends
|
|
906
|
+
declare class ConfigurationError extends HydraError {
|
|
826
907
|
constructor(message: string);
|
|
827
908
|
}
|
|
828
909
|
/**
|
|
829
910
|
* Authentication error - invalid or expired credentials
|
|
830
911
|
*/
|
|
831
|
-
declare class AuthenticationError extends
|
|
912
|
+
declare class AuthenticationError extends HydraError {
|
|
832
913
|
constructor(message: string, statusCode?: number);
|
|
833
914
|
}
|
|
834
915
|
/**
|
|
835
916
|
* Rate limit error - too many requests
|
|
836
917
|
*/
|
|
837
|
-
declare class RateLimitError extends
|
|
918
|
+
declare class RateLimitError extends HydraError {
|
|
838
919
|
readonly retryAfterMs: number;
|
|
839
920
|
constructor(message: string, retryAfterMs?: number);
|
|
840
921
|
}
|
|
841
922
|
/**
|
|
842
923
|
* Timeout error - request took too long
|
|
843
924
|
*/
|
|
844
|
-
declare class TimeoutError extends
|
|
925
|
+
declare class TimeoutError extends HydraError {
|
|
845
926
|
readonly timeoutMs: number;
|
|
846
927
|
constructor(timeoutMs: number);
|
|
847
928
|
}
|
|
848
929
|
/**
|
|
849
930
|
* Network error - connection failed
|
|
850
931
|
*/
|
|
851
|
-
declare class NetworkError extends
|
|
932
|
+
declare class NetworkError extends HydraError {
|
|
852
933
|
constructor(message: string, cause?: Error);
|
|
853
934
|
}
|
|
854
935
|
/**
|
|
855
936
|
* Chain error - fallback chain execution failed
|
|
856
937
|
*/
|
|
857
|
-
declare class ChainExecutionError extends
|
|
938
|
+
declare class ChainExecutionError extends HydraError {
|
|
858
939
|
readonly attemptedModels: string[];
|
|
859
940
|
constructor(message: string, attemptedModels?: string[]);
|
|
860
941
|
}
|
|
861
942
|
/**
|
|
862
943
|
* Validation error - schema validation failed
|
|
863
944
|
*/
|
|
864
|
-
declare class ValidationError extends
|
|
945
|
+
declare class ValidationError extends HydraError {
|
|
865
946
|
readonly validationErrors: unknown[];
|
|
866
947
|
constructor(message: string, validationErrors?: unknown[]);
|
|
867
948
|
}
|
|
868
949
|
/**
|
|
869
950
|
* Server error - internal server error
|
|
870
951
|
*/
|
|
871
|
-
declare class ServerError extends
|
|
952
|
+
declare class ServerError extends HydraError {
|
|
872
953
|
constructor(message: string, statusCode?: number);
|
|
873
954
|
}
|
|
874
955
|
/**
|
|
875
|
-
*
|
|
956
|
+
* Quota exceeded error - monthly request limit reached
|
|
957
|
+
*/
|
|
958
|
+
declare class QuotaExceededError extends HydraError {
|
|
959
|
+
readonly tier: string;
|
|
960
|
+
readonly limit: number;
|
|
961
|
+
readonly used: number;
|
|
962
|
+
constructor(message: string, options: {
|
|
963
|
+
tier: string;
|
|
964
|
+
limit: number;
|
|
965
|
+
used: number;
|
|
966
|
+
});
|
|
967
|
+
}
|
|
968
|
+
/**
|
|
969
|
+
* Worker error - Cloudflare Worker execution failed
|
|
970
|
+
*/
|
|
971
|
+
declare class WorkerError extends HydraError {
|
|
972
|
+
readonly workerDurationMs?: number;
|
|
973
|
+
constructor(message: string, workerDurationMs?: number);
|
|
974
|
+
}
|
|
975
|
+
/**
|
|
976
|
+
* File processing error - attachment handling failed
|
|
977
|
+
*/
|
|
978
|
+
declare class FileProcessingError extends HydraError {
|
|
979
|
+
readonly filename?: string;
|
|
980
|
+
readonly mimeType?: string;
|
|
981
|
+
readonly reason: 'unsupported_type' | 'conversion_failed' | 'too_large' | 'corrupted';
|
|
982
|
+
constructor(message: string, options: {
|
|
983
|
+
filename?: string;
|
|
984
|
+
mimeType?: string;
|
|
985
|
+
reason: 'unsupported_type' | 'conversion_failed' | 'too_large' | 'corrupted';
|
|
986
|
+
});
|
|
987
|
+
}
|
|
988
|
+
/**
|
|
989
|
+
* Invalid schema error - provided schema is malformed
|
|
990
|
+
*/
|
|
991
|
+
declare class InvalidSchemaError extends HydraError {
|
|
992
|
+
readonly schemaPath?: string;
|
|
993
|
+
constructor(message: string, schemaPath?: string);
|
|
994
|
+
}
|
|
995
|
+
/**
|
|
996
|
+
* Check if error is an Hydra error
|
|
876
997
|
*/
|
|
877
|
-
declare function
|
|
998
|
+
declare function isHydraError(error: unknown): error is HydraError;
|
|
878
999
|
/**
|
|
879
1000
|
* Check if error is retryable
|
|
880
1001
|
*/
|
|
@@ -904,4 +1025,4 @@ declare function getKeyPrefix(secretKey: string): string;
|
|
|
904
1025
|
*/
|
|
905
1026
|
declare function isValidSecretKey(secretKey: string): boolean;
|
|
906
1027
|
|
|
907
|
-
export {
|
|
1028
|
+
export { 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, Hydra, type HydraConfig, HydraError, InvalidSchemaError, type ModelInfo, NetworkError, QuotaExceededError, RateLimitError, ServerError, type StreamCallbacks, type StreamCompleteResult, type StreamProgress, TimeoutError, type TokenUsage, type UsageStats, ValidationError, type WebhookConfig, WorkerError, generateSignature, getKeyPrefix, isHydraError, 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 H(n){let{createHash:e}=await import('crypto');return e("sha256").update(n).digest("hex")}async function G(n,e,t,r,o){let{createHmac:s}=await import('crypto'),c=[e.toUpperCase(),t,o.toString(),r].join(`
|
|
2
|
+
`),d=await H(n);return s("sha256",d).update(c).digest("hex")}async function L(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 W(n,e,t,r,o){let s=new TextEncoder,c=[e.toUpperCase(),t,o.toString(),r].join(`
|
|
3
|
+
`),d=await L(n),a=s.encode(d),i=await crypto.subtle.importKey("raw",a,{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 T(n,e,t,r,o){return K?G(n,e,t,r,o):W(n,e,t,r,o)}function U(n){return n.slice(0,12)}function C(n){return typeof n=="string"&&n.startsWith("hyd_sk_")&&n.length>=20}var u=class extends Error{constructor(e,t,r){super(e),this.name="HydraError",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";}},x=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";}},f=class extends u{constructor(e,t=[]){super(e,"CHAIN_FAILED",{retryable:false}),this.name="ChainExecutionError",this.attemptedModels=t;}},P=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";}},v=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;}},O=class extends u{constructor(e,t){super(e,"WORKER_ERROR",{retryable:true}),this.name="WorkerError",this.workerDurationMs=t;}},I=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;}},M=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 q(n){return _(n)?n.retryable:false}var F="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("{"),o=e.indexOf("["),s;if(r===-1&&o===-1)return e;r===-1?s=o:o===-1?s=r:s=Math.min(r,o);let c=e[s],d=c==="{"?"}":"]",a=0,i=false,m=false,y=-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)a++;else if(b===d&&(a--,a===0)){y=h;break}}}if(y!==-1)return e.slice(s,y+1);let p=e.lastIndexOf(d);return p>s?e.slice(s,p+1):e}var D=class n{constructor(e){if(!e.secretKey)throw new k("secretKey is required");if(!C(e.secretKey))throw new k("Invalid secret key format. Expected format: hyd_sk_<key>");this.secretKey=e.secretKey,this.baseUrl=(e.baseUrl||F).replace(/\/$/,""),this.timeout=e.timeout??$,this.maxRetries=e.maxRetries??B,this.keyPrefix=U(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",o=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",o);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 f("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}),a=await(await this.fetchWithTimeout(s.workerUrl,{method:"POST",headers:{"Content-Type":"application/json"},body:c},e.timeout??this.timeout)).json();if(!a.success||!a.content)return {success:false,data:{valid:[],invalid:[]},meta:{cached:false,modelUsed:a.modelId||"",tokensUsed:a.tokensUsed||{input:0,output:0},latencyMs:Date.now()-t,attemptedModels:[a.modelId||""]},error:{code:a.errorCode||"GENERATION_FAILED",message:a.errorMessage||"Generation failed",retryable:false}};let i=[],m=[],y=J(a.content);try{let h=JSON.parse(y);Array.isArray(h)?i=h:i=[h];}catch{i=[y];}a.completionToken&&this.cacheCompletionAsync({completionToken:a.completionToken,content:a.content,tokensUsed:a.tokensUsed||{input:0,output:0},finishReason:a.finishReason||"stop",chainId:e.chainId,modelUsed:a.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:a.modelId,tokensUsed:a.tokensUsed}),{success:true,data:{valid:i,invalid:m},meta:{cached:false,modelUsed:a.modelId,tokensUsed:a.tokensUsed||{input:0,output:0},latencyMs:p,attemptedModels:[a.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",o=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 a=0;a<=this.maxRetries;a++)try{let i=await this.makeAuthenticatedRequest(r,"POST",o,e.timeout),m=Date.now()-t;return this.debug&&this.log("generate success (legacy)",{latencyMs:m,attempt:a+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 ${a+1} failed`,{error:s.message}),!this.isRetryable(i)||a>=this.maxRetries)break;let m=N[Math.min(a,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",o=JSON.stringify({chainId:e.chainId,prompt:e.prompt,schema:e.schema,options:e.options});try{let s=Date.now(),c=await T(this.secretKey,"POST",r,o,s),d=await this.fetchWithTimeout(`${this.baseUrl}${r}`,{method:"POST",headers:{"Content-Type":"application/json",Accept:"text/event-stream","x-hydra-signature":c,"x-hydra-timestamp":s.toString(),"x-hydra-key":this.keyPrefix},body:o},e.timeout??this.timeout);if(!d.ok){let p=await d.json();throw this.parseError(d.status,p)}let a=d.body?.getReader();if(!a)throw new w("Response body is not readable");let i=new TextDecoder,m="",y=[];for(;;){let{done:p,value:h}=await a.read();if(p)break;m+=i.decode(h,{stream:!0});let b=m.split(`
|
|
4
|
+
`);m=b.pop()||"";let A="";for(let S of b){if(S.startsWith("event: ")){A=S.slice(7).trim();continue}if(S.startsWith("data: ")){let j=S.slice(6);if(j==="[DONE]")break;try{let l=JSON.parse(j);switch(A){case "start":this.debug&&this.log("Stream started",l);break;case "object":l.object!==void 0&&(y.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:y,objectCount:l.objectCount,tokensUsed:l.tokensUsed,cached:l.cached});break;case "error":t.onError&&t.onError(new f(l.error||"Stream error",["STREAM_ERROR"]));break;default:l.chunk&&t.onChunk?.(l.chunk);}}catch{}A="";}}}}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,o=await t.json().catch(()=>({}));return {ok:t.ok,latencyMs:r,version:o.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 o=await this.makeAuthenticatedRequest("/api/v1/generate/batch","POST",JSON.stringify({chainId:e,items:t,continueOnError:r.continueOnError??true}));if(!o.success||!o.data)throw new f(o.error?.message||"Batch generation failed",[]);return o.data}async makeAuthenticatedRequest(e,t,r,o){let s=Date.now(),c=await T(this.secretKey,t,e,r,s),d=await this.fetchWithTimeout(`${this.baseUrl}${e}`,{method:t,headers:{"Content-Type":"application/json",Accept:"application/json","x-hydra-signature":c,"x-hydra-timestamp":s.toString(),"x-hydra-key":this.keyPrefix},body:r},o??this.timeout),a=await d.json();if(!d.ok)throw this.parseError(d.status,a);return a}async fetchWithTimeout(e,t,r=this.timeout){let o=new AbortController,s=setTimeout(()=>o.abort(),r);try{return await fetch(e,{...t,signal:o.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}`,o=t?.error?.code||"UNKNOWN";switch(e){case 401:return new x(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 o==="CHAIN_FAILED"?new f(r,t?.error?.attemptedModels||[]):new u(r,o,{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="[Hydra]";t?console.log(r,e,t):console.log(r,e);}};exports.AuthenticationError=x;exports.ChainExecutionError=f;exports.ConfigurationError=k;exports.FileProcessingError=I;exports.Hydra=D;exports.HydraError=u;exports.InvalidSchemaError=M;exports.NetworkError=w;exports.QuotaExceededError=v;exports.RateLimitError=E;exports.ServerError=g;exports.TimeoutError=R;exports.ValidationError=P;exports.WorkerError=O;exports.generateSignature=T;exports.getKeyPrefix=U;exports.isHydraError=_;exports.isRetryableError=q;exports.isValidSecretKey=C;
|
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 H(n){let{createHash:e}=await import('crypto');return e("sha256").update(n).digest("hex")}async function G(n,e,t,r,o){let{createHmac:s}=await import('crypto'),c=[e.toUpperCase(),t,o.toString(),r].join(`
|
|
2
|
+
`),d=await H(n);return s("sha256",d).update(c).digest("hex")}async function L(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 W(n,e,t,r,o){let s=new TextEncoder,c=[e.toUpperCase(),t,o.toString(),r].join(`
|
|
3
|
+
`),d=await L(n),a=s.encode(d),i=await crypto.subtle.importKey("raw",a,{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 T(n,e,t,r,o){return K?G(n,e,t,r,o):W(n,e,t,r,o)}function U(n){return n.slice(0,12)}function C(n){return typeof n=="string"&&n.startsWith("hyd_sk_")&&n.length>=20}var u=class extends Error{constructor(e,t,r){super(e),this.name="HydraError",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";}},x=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";}},f=class extends u{constructor(e,t=[]){super(e,"CHAIN_FAILED",{retryable:false}),this.name="ChainExecutionError",this.attemptedModels=t;}},P=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";}},v=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;}},O=class extends u{constructor(e,t){super(e,"WORKER_ERROR",{retryable:true}),this.name="WorkerError",this.workerDurationMs=t;}},I=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;}},M=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 q(n){return _(n)?n.retryable:false}var F="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("{"),o=e.indexOf("["),s;if(r===-1&&o===-1)return e;r===-1?s=o:o===-1?s=r:s=Math.min(r,o);let c=e[s],d=c==="{"?"}":"]",a=0,i=false,m=false,y=-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)a++;else if(b===d&&(a--,a===0)){y=h;break}}}if(y!==-1)return e.slice(s,y+1);let p=e.lastIndexOf(d);return p>s?e.slice(s,p+1):e}var D=class n{constructor(e){if(!e.secretKey)throw new k("secretKey is required");if(!C(e.secretKey))throw new k("Invalid secret key format. Expected format: hyd_sk_<key>");this.secretKey=e.secretKey,this.baseUrl=(e.baseUrl||F).replace(/\/$/,""),this.timeout=e.timeout??$,this.maxRetries=e.maxRetries??B,this.keyPrefix=U(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",o=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",o);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 f("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}),a=await(await this.fetchWithTimeout(s.workerUrl,{method:"POST",headers:{"Content-Type":"application/json"},body:c},e.timeout??this.timeout)).json();if(!a.success||!a.content)return {success:false,data:{valid:[],invalid:[]},meta:{cached:false,modelUsed:a.modelId||"",tokensUsed:a.tokensUsed||{input:0,output:0},latencyMs:Date.now()-t,attemptedModels:[a.modelId||""]},error:{code:a.errorCode||"GENERATION_FAILED",message:a.errorMessage||"Generation failed",retryable:false}};let i=[],m=[],y=J(a.content);try{let h=JSON.parse(y);Array.isArray(h)?i=h:i=[h];}catch{i=[y];}a.completionToken&&this.cacheCompletionAsync({completionToken:a.completionToken,content:a.content,tokensUsed:a.tokensUsed||{input:0,output:0},finishReason:a.finishReason||"stop",chainId:e.chainId,modelUsed:a.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:a.modelId,tokensUsed:a.tokensUsed}),{success:true,data:{valid:i,invalid:m},meta:{cached:false,modelUsed:a.modelId,tokensUsed:a.tokensUsed||{input:0,output:0},latencyMs:p,attemptedModels:[a.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",o=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 a=0;a<=this.maxRetries;a++)try{let i=await this.makeAuthenticatedRequest(r,"POST",o,e.timeout),m=Date.now()-t;return this.debug&&this.log("generate success (legacy)",{latencyMs:m,attempt:a+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 ${a+1} failed`,{error:s.message}),!this.isRetryable(i)||a>=this.maxRetries)break;let m=N[Math.min(a,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",o=JSON.stringify({chainId:e.chainId,prompt:e.prompt,schema:e.schema,options:e.options});try{let s=Date.now(),c=await T(this.secretKey,"POST",r,o,s),d=await this.fetchWithTimeout(`${this.baseUrl}${r}`,{method:"POST",headers:{"Content-Type":"application/json",Accept:"text/event-stream","x-hydra-signature":c,"x-hydra-timestamp":s.toString(),"x-hydra-key":this.keyPrefix},body:o},e.timeout??this.timeout);if(!d.ok){let p=await d.json();throw this.parseError(d.status,p)}let a=d.body?.getReader();if(!a)throw new w("Response body is not readable");let i=new TextDecoder,m="",y=[];for(;;){let{done:p,value:h}=await a.read();if(p)break;m+=i.decode(h,{stream:!0});let b=m.split(`
|
|
4
|
+
`);m=b.pop()||"";let A="";for(let S of b){if(S.startsWith("event: ")){A=S.slice(7).trim();continue}if(S.startsWith("data: ")){let j=S.slice(6);if(j==="[DONE]")break;try{let l=JSON.parse(j);switch(A){case "start":this.debug&&this.log("Stream started",l);break;case "object":l.object!==void 0&&(y.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:y,objectCount:l.objectCount,tokensUsed:l.tokensUsed,cached:l.cached});break;case "error":t.onError&&t.onError(new f(l.error||"Stream error",["STREAM_ERROR"]));break;default:l.chunk&&t.onChunk?.(l.chunk);}}catch{}A="";}}}}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,o=await t.json().catch(()=>({}));return {ok:t.ok,latencyMs:r,version:o.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 o=await this.makeAuthenticatedRequest("/api/v1/generate/batch","POST",JSON.stringify({chainId:e,items:t,continueOnError:r.continueOnError??true}));if(!o.success||!o.data)throw new f(o.error?.message||"Batch generation failed",[]);return o.data}async makeAuthenticatedRequest(e,t,r,o){let s=Date.now(),c=await T(this.secretKey,t,e,r,s),d=await this.fetchWithTimeout(`${this.baseUrl}${e}`,{method:t,headers:{"Content-Type":"application/json",Accept:"application/json","x-hydra-signature":c,"x-hydra-timestamp":s.toString(),"x-hydra-key":this.keyPrefix},body:r},o??this.timeout),a=await d.json();if(!d.ok)throw this.parseError(d.status,a);return a}async fetchWithTimeout(e,t,r=this.timeout){let o=new AbortController,s=setTimeout(()=>o.abort(),r);try{return await fetch(e,{...t,signal:o.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}`,o=t?.error?.code||"UNKNOWN";switch(e){case 401:return new x(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 o==="CHAIN_FAILED"?new f(r,t?.error?.attemptedModels||[]):new u(r,o,{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="[Hydra]";t?console.log(r,e,t):console.log(r,e);}};export{x as AuthenticationError,f as ChainExecutionError,k as ConfigurationError,I as FileProcessingError,D as Hydra,u as HydraError,M as InvalidSchemaError,w as NetworkError,v as QuotaExceededError,E as RateLimitError,g as ServerError,R as TimeoutError,P as ValidationError,O as WorkerError,T as generateSignature,U as getKeyPrefix,_ as isHydraError,q as isRetryableError,C as isValidSecretKey};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@altsafe/aidirector",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Official TypeScript SDK for
|
|
3
|
+
"version": "1.4.0",
|
|
4
|
+
"description": "Official TypeScript SDK for Hydra - Intelligent AI API Gateway with automatic failover, caching, and JSON extraction",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"prepublishOnly": "npm run build",
|
|
33
33
|
"clean": "rimraf dist",
|
|
34
34
|
"publish:check": "npm pack --dry-run",
|
|
35
|
-
"release": "npm run build && npm publish",
|
|
35
|
+
"release": "npm run build && npm publish --access public",
|
|
36
36
|
"release:beta": "npm run build && npm publish --tag beta"
|
|
37
37
|
},
|
|
38
38
|
"keywords": [
|
|
@@ -51,17 +51,17 @@
|
|
|
51
51
|
"hmac"
|
|
52
52
|
],
|
|
53
53
|
"author": {
|
|
54
|
-
"name": "
|
|
55
|
-
"url": "https://
|
|
54
|
+
"name": "Hydra",
|
|
55
|
+
"url": "https://hydrai.dev"
|
|
56
56
|
},
|
|
57
57
|
"license": "MIT",
|
|
58
|
-
"homepage": "https://
|
|
58
|
+
"homepage": "https://hydrai.dev",
|
|
59
59
|
"bugs": {
|
|
60
|
-
"url": "https://github.com/
|
|
60
|
+
"url": "https://github.com/hydrai/client/issues"
|
|
61
61
|
},
|
|
62
62
|
"repository": {
|
|
63
63
|
"type": "git",
|
|
64
|
-
"url": "git+https://github.com/
|
|
64
|
+
"url": "git+https://github.com/hydrai/client.git"
|
|
65
65
|
},
|
|
66
66
|
"publishConfig": {
|
|
67
67
|
"access": "public",
|