hydra-aidirector 1.4.2 → 1.6.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
@@ -1,302 +1,304 @@
1
- # hydra-aidirector - Client SDK
2
- # hydra-aidirector
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
12
-
13
- ## Installation
14
-
15
- ```bash
16
- npm install hydra-aidirector
17
- # or
18
- pnpm add hydra-aidirector
19
- ```
20
-
21
- ## Quick Start
22
-
23
- ```typescript
24
- import { Hydra } from 'hydra-aidirector';
25
-
26
- const ai = new Hydra({
27
- secretKey: process.env.HYDRA_SECRET_KEY!,
28
- baseUrl: 'https://your-instance.vercel.app',
29
- });
30
-
31
- // Generate content
32
- const result = await ai.generate({
33
- chainId: 'my-chain',
34
- prompt: 'Generate 5 user profiles',
35
- });
36
-
37
- if (result.success) {
38
- console.log(result.data.valid);
39
- }
40
- ```
41
-
42
- ## Features
43
-
44
- - 🔐 **HMAC Authentication** - Secure request signing
45
- - **3-Step Architecture** - Token → Worker → Complete (minimizes costs)
46
- - 📎 **File Attachments** - Upload and process documents
47
- - 🔄 **Automatic Retries** - Exponential backoff on failures
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
52
-
53
- ## Streaming (Recommended for Large Responses)
54
-
55
- ```typescript
56
- await client.generateStream(
57
- {
58
- chainId: 'my-chain',
59
- prompt: 'Generate 100 product descriptions',
60
- },
61
- {
62
- onObject: (obj, index) => {
63
- console.log(`Object ${index}:`, obj);
64
- renderToUI(obj); // Render immediately!
65
- },
66
- onComplete: (result) => {
67
- console.log(`Done! ${result.objectCount} objects`);
68
- },
69
- onError: (error) => {
70
- console.error('Stream failed:', error);
71
- },
72
- }
73
- );
74
- ```
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
-
134
- ## File Attachments
135
-
136
- ```typescript
137
- import fs from 'fs';
138
-
139
- const fileBuffer = fs.readFileSync('report.pdf');
140
-
141
- const result = await client.generate({
142
- chainId: 'document-analysis',
143
- prompt: 'Summarize this document',
144
- files: [{
145
- data: fileBuffer.toString('base64'),
146
- filename: 'report.pdf',
147
- mimeType: 'application/pdf',
148
- }],
149
- });
150
- ```
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
-
185
- ## Configuration
186
-
187
- | Option | Type | Default | Description |
188
- |--------|------|---------|-------------|
189
- | `secretKey` | `string` | **required** | Your API key (`hyd_sk_...`) |
190
- | `baseUrl` | `string` | `http://localhost:3000` | API base URL |
191
- | `timeout` | `number` | `600000` | Request timeout (10 min) |
192
- | `maxRetries` | `number` | `3` | Max retry attempts |
193
- | `debug` | `boolean` | `false` | Enable debug logging |
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 |
208
-
209
- ## API Methods
210
-
211
- | Method | Description |
212
- |--------|-------------|
213
- | `generate(options)` | Generate content with fallback chain |
214
- | `generateStream(options, callbacks)` | Stream JSON objects in real-time |
215
- | `generateBatch(chainId, items)` | Process multiple prompts |
216
- | `listModels()` | List available AI models |
217
- | `listChains()` | List your fallback chains |
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 |
225
-
226
- ## Error Handling
227
-
228
- ```typescript
229
- import {
230
- RateLimitError,
231
- TimeoutError,
232
- AuthenticationError,
233
- QuotaExceededError,
234
- WorkerError,
235
- FileProcessingError,
236
- } from 'hydra-aidirector';
237
-
238
- try {
239
- const result = await client.generate({ ... });
240
- } catch (error) {
241
- if (error instanceof RateLimitError) {
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})`);
245
- } else if (error instanceof TimeoutError) {
246
- console.log(`Request timed out after ${error.timeoutMs}ms`);
247
- } else if (error instanceof AuthenticationError) {
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}`);
253
- }
254
- }
255
- ```
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
-
289
- ## Pricing
290
-
291
- BYOK (Bring Your Own Key) - You pay for AI costs directly to providers.
292
-
293
- | Tier | Price | Requests | Overage |
294
- |------|-------|----------|---------|
295
- | Free | $0 | 1K | Blocked |
296
- | Starter | $9 | 25K | $0.50/1K |
297
- | Pro | $29 | 100K | $0.40/1K |
298
- | Scale | $79 | 500K | $0.30/1K |
299
-
300
- ## License
301
-
302
- MIT
1
+ # hydra-aidirector
2
+
3
+ The official TypeScript SDK for [Hydra](https://hydrai.dev).
4
+
5
+ Hydra gives your app one stable AI gateway instead of a pile of provider glue. You send one request. Hydra handles routing, failover, cache behavior, JSON recovery, and operational visibility behind the same API.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ bun add hydra-aidirector
11
+ # or
12
+ npm install hydra-aidirector
13
+ # or
14
+ pnpm add hydra-aidirector
15
+ # or
16
+ yarn add hydra-aidirector
17
+ ```
18
+
19
+ ## Quick Start
20
+
21
+ ```ts
22
+ import { Hydra } from 'hydra-aidirector';
23
+
24
+ const client = new Hydra({
25
+ secretKey: process.env.HYDRA_SECRET_KEY!, // hyd_sk_...
26
+ baseUrl: 'https://your-hydra-instance.com',
27
+ });
28
+
29
+ const result = await client.generate({
30
+ chainId: 'support-replies',
31
+ prompt: 'Write 3 concise onboarding emails as JSON',
32
+ });
33
+
34
+ if (result.success) {
35
+ console.log(result.data.valid);
36
+ }
37
+ ```
38
+
39
+ ## What You Get
40
+
41
+ - Automatic failover across the models in your chain.
42
+ - JSON extraction and repair when providers return noisy output.
43
+ - User-safe caching with request-level cache controls.
44
+ - Streaming for long responses.
45
+ - Batch generation for parallel prompt execution.
46
+ - Usage, health, and webhook management from the same client.
47
+
48
+ ## Generation Modes
49
+
50
+ Hydra uses the optimized 3-step flow by default for simple requests. That keeps the fast path cheap and responsive.
51
+
52
+ When you ask for features that require the full server pipeline, the SDK automatically falls back to the legacy endpoint. That includes:
53
+
54
+ - `schema`
55
+ - `noCache`
56
+ - `cacheScope`
57
+ - `cacheQuality`
58
+ - `strictJson`
59
+ - `maxRetries`
60
+
61
+ You can also force the legacy endpoint yourself:
62
+
63
+ ```ts
64
+ await client.generate({
65
+ chainId: 'support-replies',
66
+ prompt: 'Return a fresh answer',
67
+ useOptimized: false,
68
+ });
69
+ ```
70
+
71
+ ## Structured Output
72
+
73
+ Use `schema` when you want validated JSON back:
74
+
75
+ ```ts
76
+ const result = await client.generate({
77
+ chainId: 'invoice-parser',
78
+ prompt: 'Extract invoice fields',
79
+ schema: {
80
+ invoiceNumber: 'string',
81
+ amount: 'number',
82
+ currency: 'string',
83
+ },
84
+ strictJson: true,
85
+ });
86
+ ```
87
+
88
+ If the provider returns malformed JSON, Hydra will still try to recover it before the request fails.
89
+
90
+ ## Cache Control
91
+
92
+ Use request-level cache controls when the prompt should behave differently from the default path:
93
+
94
+ ```ts
95
+ await client.generate({
96
+ chainId: 'account-summary',
97
+ prompt: 'Summarize this customer account',
98
+ cacheScope: 'user',
99
+ });
100
+
101
+ await client.generate({
102
+ chainId: 'pricing-faq',
103
+ prompt: 'Return the current pricing summary',
104
+ cacheScope: 'global',
105
+ });
106
+
107
+ await client.generate({
108
+ chainId: 'one-off',
109
+ prompt: 'Generate a fresh variant',
110
+ noCache: true,
111
+ });
112
+ ```
113
+
114
+ You can also tune cache matching:
115
+
116
+ ```ts
117
+ await client.generate({
118
+ chainId: 'reporting',
119
+ prompt: 'Generate the weekly report',
120
+ cacheQuality: 'HIGH',
121
+ });
122
+ ```
123
+
124
+ ## Streaming
125
+
126
+ Stream parsed objects as they arrive:
127
+
128
+ ```ts
129
+ await client.generateStream(
130
+ {
131
+ chainId: 'catalog-generator',
132
+ prompt: 'Generate 50 product blurbs as JSON',
133
+ },
134
+ {
135
+ onObject: (object, index) => {
136
+ console.log(index, object);
137
+ },
138
+ onComplete: (result) => {
139
+ console.log(result.objectCount);
140
+ },
141
+ onError: (error) => {
142
+ console.error(error);
143
+ },
144
+ }
145
+ );
146
+ ```
147
+
148
+ ## Batch Generation
149
+
150
+ Run multiple prompts in one call:
151
+
152
+ ```ts
153
+ const batch = await client.generateBatch('catalog-generator', [
154
+ { id: 'a', prompt: 'Describe product A' },
155
+ { id: 'b', prompt: 'Describe product B' },
156
+ { id: 'c', prompt: 'Describe product C' },
157
+ ]);
158
+
159
+ console.log(batch.summary);
160
+ ```
161
+
162
+ ## Files
163
+
164
+ Send file attachments as base64:
165
+
166
+ ```ts
167
+ import fs from 'node:fs';
168
+
169
+ const file = fs.readFileSync('report.pdf');
170
+
171
+ const result = await client.generate({
172
+ chainId: 'document-analysis',
173
+ prompt: 'Summarize this report',
174
+ files: [
175
+ {
176
+ data: file.toString('base64'),
177
+ filename: 'report.pdf',
178
+ mimeType: 'application/pdf',
179
+ },
180
+ ],
181
+ });
182
+ ```
183
+
184
+ ## Usage and Health
185
+
186
+ Get normalized usage metrics:
187
+
188
+ ```ts
189
+ const usage = await client.getUsage({
190
+ startDate: new Date('2026-03-01T00:00:00.000Z'),
191
+ endDate: new Date('2026-04-01T00:00:00.000Z'),
192
+ });
193
+
194
+ console.log(usage.totalRequests);
195
+ console.log(usage.cachedRequests);
196
+ console.log(usage.byModelList);
197
+ ```
198
+
199
+ Basic health:
200
+
201
+ ```ts
202
+ const health = await client.health();
203
+ console.log(health.ok, health.latencyMs);
204
+ ```
205
+
206
+ Detailed health requires an admin key:
207
+
208
+ ```ts
209
+ const detailed = await client.healthDetailed();
210
+ console.log(detailed.status);
211
+ ```
212
+
213
+ ## Webhooks
214
+
215
+ Register, list, update, and remove webhooks from the SDK:
216
+
217
+ ```ts
218
+ await client.registerWebhook({
219
+ requestId: 'req_123',
220
+ url: 'https://example.com/webhooks/hydra',
221
+ secret: 'super-secret-webhook-key',
222
+ retryCount: 3,
223
+ });
224
+
225
+ const webhooks = await client.listWebhooks();
226
+
227
+ await client.updateWebhook(webhooks[0].id, {
228
+ retryCount: 5,
229
+ });
230
+
231
+ await client.unregisterWebhook(webhooks[0].id);
232
+ ```
233
+
234
+ ## Cancellation
235
+
236
+ Cancel long-running requests with `AbortController`:
237
+
238
+ ```ts
239
+ const controller = new AbortController();
240
+
241
+ setTimeout(() => controller.abort(), 5000);
242
+
243
+ await client.generate({
244
+ chainId: 'long-job',
245
+ prompt: 'Generate a long report',
246
+ signal: controller.signal,
247
+ });
248
+ ```
249
+
250
+ ## Error Handling
251
+
252
+ ```ts
253
+ import {
254
+ AuthenticationError,
255
+ QuotaExceededError,
256
+ RateLimitError,
257
+ TimeoutError,
258
+ isRetryableError,
259
+ } from 'hydra-aidirector';
260
+
261
+ try {
262
+ await client.generate({
263
+ chainId: 'support-replies',
264
+ prompt: 'Write a response',
265
+ });
266
+ } catch (error) {
267
+ if (error instanceof AuthenticationError) {
268
+ console.error('The Hydra secret key is invalid.');
269
+ } else if (error instanceof QuotaExceededError) {
270
+ console.error(error.tier, error.used, error.limit);
271
+ } else if (error instanceof RateLimitError) {
272
+ console.error(error.retryAfterMs);
273
+ } else if (error instanceof TimeoutError) {
274
+ console.error(error.timeoutMs);
275
+ } else if (isRetryableError(error)) {
276
+ console.error('Safe to retry.');
277
+ }
278
+ }
279
+ ```
280
+
281
+ ## API Surface
282
+
283
+ - `generate(options)`
284
+ - `generateStream(options, callbacks)`
285
+ - `generateBatch(chainId, items, options?)`
286
+ - `listModels()`
287
+ - `listChains()`
288
+ - `getUsage(options?)`
289
+ - `health()`
290
+ - `healthDetailed()`
291
+ - `registerWebhook(config)`
292
+ - `listWebhooks()`
293
+ - `updateWebhook(id, updates)`
294
+ - `unregisterWebhook(id)`
295
+ - `withConfig(overrides)`
296
+
297
+ ## Requirements
298
+
299
+ - Node.js 18+
300
+ - TypeScript 5+ for typed projects
301
+
302
+ ## License
303
+
304
+ MIT