hydra-aidirector 1.3.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 +21 -0
- package/README.md +302 -0
- package/dist/index.d.mts +1028 -0
- package/dist/index.d.ts +1028 -0
- package/dist/index.js +4 -0
- package/dist/index.mjs +4 -0
- package/package.json +87 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Hydra
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,302 @@
|
|
|
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
|