@altsafe/aidirector 1.2.1 → 1.4.2
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 +33 -11
- package/dist/index.d.mts +45 -36
- package/dist/index.d.ts +45 -36
- package/dist/index.js +4 -4
- package/dist/index.mjs +4 -4
- package/package.json +88 -87
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
|
});
|
|
@@ -147,7 +155,7 @@ const result = await client.generate({
|
|
|
147
155
|
// Register a webhook
|
|
148
156
|
await client.registerWebhook({
|
|
149
157
|
requestId: 'req_123',
|
|
150
|
-
url: 'https://your-domain.com/webhooks/
|
|
158
|
+
url: 'https://your-domain.com/webhooks/hydra',
|
|
151
159
|
secret: 'your-webhook-secret',
|
|
152
160
|
retryCount: 3,
|
|
153
161
|
});
|
|
@@ -178,7 +186,7 @@ const result = await client.generate({
|
|
|
178
186
|
|
|
179
187
|
| Option | Type | Default | Description |
|
|
180
188
|
|--------|------|---------|-------------|
|
|
181
|
-
| `secretKey` | `string` | **required** | Your API key (`
|
|
189
|
+
| `secretKey` | `string` | **required** | Your API key (`hyd_sk_...`) |
|
|
182
190
|
| `baseUrl` | `string` | `http://localhost:3000` | API base URL |
|
|
183
191
|
| `timeout` | `number` | `600000` | Request timeout (10 min) |
|
|
184
192
|
| `maxRetries` | `number` | `3` | Max retry attempts |
|
|
@@ -225,7 +233,7 @@ import {
|
|
|
225
233
|
QuotaExceededError,
|
|
226
234
|
WorkerError,
|
|
227
235
|
FileProcessingError,
|
|
228
|
-
} from '
|
|
236
|
+
} from 'hydra-aidirector';
|
|
229
237
|
|
|
230
238
|
try {
|
|
231
239
|
const result = await client.generate({ ... });
|
|
@@ -246,6 +254,20 @@ try {
|
|
|
246
254
|
}
|
|
247
255
|
```
|
|
248
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
|
+
|
|
249
271
|
## AI-Directed Caching
|
|
250
272
|
|
|
251
273
|
The AI can control caching by including a `_cache` directive in its JSON output:
|
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
|
/**
|
|
@@ -215,6 +215,15 @@ interface GenerateData {
|
|
|
215
215
|
* Raw AI response text (useful for debugging)
|
|
216
216
|
*/
|
|
217
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
|
+
}>;
|
|
218
227
|
}
|
|
219
228
|
/**
|
|
220
229
|
* Generation metadata
|
|
@@ -614,9 +623,9 @@ interface WebhookConfig {
|
|
|
614
623
|
}
|
|
615
624
|
|
|
616
625
|
/**
|
|
617
|
-
*
|
|
626
|
+
* Hydra Client SDK
|
|
618
627
|
*
|
|
619
|
-
* Production-grade client for the
|
|
628
|
+
* Production-grade client for the Hydra API gateway.
|
|
620
629
|
* Use in Next.js API routes, Server Actions, or any Node.js/Edge environment.
|
|
621
630
|
*
|
|
622
631
|
* Features:
|
|
@@ -628,11 +637,11 @@ interface WebhookConfig {
|
|
|
628
637
|
*
|
|
629
638
|
* @example
|
|
630
639
|
* ```typescript
|
|
631
|
-
* import {
|
|
640
|
+
* import { Hydra } from 'hydra-aidirector';
|
|
632
641
|
*
|
|
633
|
-
* const client = new
|
|
634
|
-
* secretKey: process.env.
|
|
635
|
-
* baseUrl: 'https://api.
|
|
642
|
+
* const client = new Hydra({
|
|
643
|
+
* secretKey: process.env.HYDRA_SECRET_KEY!,
|
|
644
|
+
* baseUrl: 'https://api.hydrai.dev',
|
|
636
645
|
* });
|
|
637
646
|
*
|
|
638
647
|
* const result = await client.generate({
|
|
@@ -646,20 +655,20 @@ interface WebhookConfig {
|
|
|
646
655
|
* }
|
|
647
656
|
* ```
|
|
648
657
|
*/
|
|
649
|
-
declare class
|
|
658
|
+
declare class Hydra {
|
|
650
659
|
private readonly secretKey;
|
|
651
660
|
private readonly baseUrl;
|
|
652
661
|
private readonly timeout;
|
|
653
662
|
private readonly maxRetries;
|
|
654
663
|
private readonly keyPrefix;
|
|
655
664
|
private readonly debug;
|
|
656
|
-
constructor(config:
|
|
665
|
+
constructor(config: HydraConfig);
|
|
657
666
|
/**
|
|
658
667
|
* Generate content using your fallback chain
|
|
659
668
|
*
|
|
660
669
|
* @param options - Generation options
|
|
661
670
|
* @returns Promise resolving to GenerateResult
|
|
662
|
-
* @throws
|
|
671
|
+
* @throws HydraError subclasses on failure
|
|
663
672
|
*
|
|
664
673
|
* @example
|
|
665
674
|
* ```typescript
|
|
@@ -746,9 +755,9 @@ declare class AIDirector {
|
|
|
746
755
|
* Useful for testing or switching environments
|
|
747
756
|
*
|
|
748
757
|
* @param overrides - Configuration overrides
|
|
749
|
-
* @returns New
|
|
758
|
+
* @returns New Hydra instance
|
|
750
759
|
*/
|
|
751
|
-
withConfig(overrides: Partial<
|
|
760
|
+
withConfig(overrides: Partial<HydraConfig>): Hydra;
|
|
752
761
|
/**
|
|
753
762
|
* Register a webhook for async notifications
|
|
754
763
|
*
|
|
@@ -874,13 +883,13 @@ declare class AIDirector {
|
|
|
874
883
|
}
|
|
875
884
|
|
|
876
885
|
/**
|
|
877
|
-
*
|
|
886
|
+
* Hydra Error Classes
|
|
878
887
|
* Structured error types for better error handling
|
|
879
888
|
*/
|
|
880
889
|
/**
|
|
881
|
-
* Base error class for
|
|
890
|
+
* Base error class for Hydra errors
|
|
882
891
|
*/
|
|
883
|
-
declare class
|
|
892
|
+
declare class HydraError extends Error {
|
|
884
893
|
readonly code: string;
|
|
885
894
|
readonly retryable: boolean;
|
|
886
895
|
readonly statusCode?: number;
|
|
@@ -894,59 +903,59 @@ declare class AIDirectorError extends Error {
|
|
|
894
903
|
/**
|
|
895
904
|
* Configuration error - invalid client setup
|
|
896
905
|
*/
|
|
897
|
-
declare class ConfigurationError extends
|
|
906
|
+
declare class ConfigurationError extends HydraError {
|
|
898
907
|
constructor(message: string);
|
|
899
908
|
}
|
|
900
909
|
/**
|
|
901
910
|
* Authentication error - invalid or expired credentials
|
|
902
911
|
*/
|
|
903
|
-
declare class AuthenticationError extends
|
|
912
|
+
declare class AuthenticationError extends HydraError {
|
|
904
913
|
constructor(message: string, statusCode?: number);
|
|
905
914
|
}
|
|
906
915
|
/**
|
|
907
916
|
* Rate limit error - too many requests
|
|
908
917
|
*/
|
|
909
|
-
declare class RateLimitError extends
|
|
918
|
+
declare class RateLimitError extends HydraError {
|
|
910
919
|
readonly retryAfterMs: number;
|
|
911
920
|
constructor(message: string, retryAfterMs?: number);
|
|
912
921
|
}
|
|
913
922
|
/**
|
|
914
923
|
* Timeout error - request took too long
|
|
915
924
|
*/
|
|
916
|
-
declare class TimeoutError extends
|
|
925
|
+
declare class TimeoutError extends HydraError {
|
|
917
926
|
readonly timeoutMs: number;
|
|
918
927
|
constructor(timeoutMs: number);
|
|
919
928
|
}
|
|
920
929
|
/**
|
|
921
930
|
* Network error - connection failed
|
|
922
931
|
*/
|
|
923
|
-
declare class NetworkError extends
|
|
932
|
+
declare class NetworkError extends HydraError {
|
|
924
933
|
constructor(message: string, cause?: Error);
|
|
925
934
|
}
|
|
926
935
|
/**
|
|
927
936
|
* Chain error - fallback chain execution failed
|
|
928
937
|
*/
|
|
929
|
-
declare class ChainExecutionError extends
|
|
938
|
+
declare class ChainExecutionError extends HydraError {
|
|
930
939
|
readonly attemptedModels: string[];
|
|
931
940
|
constructor(message: string, attemptedModels?: string[]);
|
|
932
941
|
}
|
|
933
942
|
/**
|
|
934
943
|
* Validation error - schema validation failed
|
|
935
944
|
*/
|
|
936
|
-
declare class ValidationError extends
|
|
945
|
+
declare class ValidationError extends HydraError {
|
|
937
946
|
readonly validationErrors: unknown[];
|
|
938
947
|
constructor(message: string, validationErrors?: unknown[]);
|
|
939
948
|
}
|
|
940
949
|
/**
|
|
941
950
|
* Server error - internal server error
|
|
942
951
|
*/
|
|
943
|
-
declare class ServerError extends
|
|
952
|
+
declare class ServerError extends HydraError {
|
|
944
953
|
constructor(message: string, statusCode?: number);
|
|
945
954
|
}
|
|
946
955
|
/**
|
|
947
956
|
* Quota exceeded error - monthly request limit reached
|
|
948
957
|
*/
|
|
949
|
-
declare class QuotaExceededError extends
|
|
958
|
+
declare class QuotaExceededError extends HydraError {
|
|
950
959
|
readonly tier: string;
|
|
951
960
|
readonly limit: number;
|
|
952
961
|
readonly used: number;
|
|
@@ -959,14 +968,14 @@ declare class QuotaExceededError extends AIDirectorError {
|
|
|
959
968
|
/**
|
|
960
969
|
* Worker error - Cloudflare Worker execution failed
|
|
961
970
|
*/
|
|
962
|
-
declare class WorkerError extends
|
|
971
|
+
declare class WorkerError extends HydraError {
|
|
963
972
|
readonly workerDurationMs?: number;
|
|
964
973
|
constructor(message: string, workerDurationMs?: number);
|
|
965
974
|
}
|
|
966
975
|
/**
|
|
967
976
|
* File processing error - attachment handling failed
|
|
968
977
|
*/
|
|
969
|
-
declare class FileProcessingError extends
|
|
978
|
+
declare class FileProcessingError extends HydraError {
|
|
970
979
|
readonly filename?: string;
|
|
971
980
|
readonly mimeType?: string;
|
|
972
981
|
readonly reason: 'unsupported_type' | 'conversion_failed' | 'too_large' | 'corrupted';
|
|
@@ -979,14 +988,14 @@ declare class FileProcessingError extends AIDirectorError {
|
|
|
979
988
|
/**
|
|
980
989
|
* Invalid schema error - provided schema is malformed
|
|
981
990
|
*/
|
|
982
|
-
declare class InvalidSchemaError extends
|
|
991
|
+
declare class InvalidSchemaError extends HydraError {
|
|
983
992
|
readonly schemaPath?: string;
|
|
984
993
|
constructor(message: string, schemaPath?: string);
|
|
985
994
|
}
|
|
986
995
|
/**
|
|
987
|
-
* Check if error is an
|
|
996
|
+
* Check if error is an Hydra error
|
|
988
997
|
*/
|
|
989
|
-
declare function
|
|
998
|
+
declare function isHydraError(error: unknown): error is HydraError;
|
|
990
999
|
/**
|
|
991
1000
|
* Check if error is retryable
|
|
992
1001
|
*/
|
|
@@ -1016,4 +1025,4 @@ declare function getKeyPrefix(secretKey: string): string;
|
|
|
1016
1025
|
*/
|
|
1017
1026
|
declare function isValidSecretKey(secretKey: string): boolean;
|
|
1018
1027
|
|
|
1019
|
-
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
|
/**
|
|
@@ -215,6 +215,15 @@ interface GenerateData {
|
|
|
215
215
|
* Raw AI response text (useful for debugging)
|
|
216
216
|
*/
|
|
217
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
|
+
}>;
|
|
218
227
|
}
|
|
219
228
|
/**
|
|
220
229
|
* Generation metadata
|
|
@@ -614,9 +623,9 @@ interface WebhookConfig {
|
|
|
614
623
|
}
|
|
615
624
|
|
|
616
625
|
/**
|
|
617
|
-
*
|
|
626
|
+
* Hydra Client SDK
|
|
618
627
|
*
|
|
619
|
-
* Production-grade client for the
|
|
628
|
+
* Production-grade client for the Hydra API gateway.
|
|
620
629
|
* Use in Next.js API routes, Server Actions, or any Node.js/Edge environment.
|
|
621
630
|
*
|
|
622
631
|
* Features:
|
|
@@ -628,11 +637,11 @@ interface WebhookConfig {
|
|
|
628
637
|
*
|
|
629
638
|
* @example
|
|
630
639
|
* ```typescript
|
|
631
|
-
* import {
|
|
640
|
+
* import { Hydra } from 'hydra-aidirector';
|
|
632
641
|
*
|
|
633
|
-
* const client = new
|
|
634
|
-
* secretKey: process.env.
|
|
635
|
-
* baseUrl: 'https://api.
|
|
642
|
+
* const client = new Hydra({
|
|
643
|
+
* secretKey: process.env.HYDRA_SECRET_KEY!,
|
|
644
|
+
* baseUrl: 'https://api.hydrai.dev',
|
|
636
645
|
* });
|
|
637
646
|
*
|
|
638
647
|
* const result = await client.generate({
|
|
@@ -646,20 +655,20 @@ interface WebhookConfig {
|
|
|
646
655
|
* }
|
|
647
656
|
* ```
|
|
648
657
|
*/
|
|
649
|
-
declare class
|
|
658
|
+
declare class Hydra {
|
|
650
659
|
private readonly secretKey;
|
|
651
660
|
private readonly baseUrl;
|
|
652
661
|
private readonly timeout;
|
|
653
662
|
private readonly maxRetries;
|
|
654
663
|
private readonly keyPrefix;
|
|
655
664
|
private readonly debug;
|
|
656
|
-
constructor(config:
|
|
665
|
+
constructor(config: HydraConfig);
|
|
657
666
|
/**
|
|
658
667
|
* Generate content using your fallback chain
|
|
659
668
|
*
|
|
660
669
|
* @param options - Generation options
|
|
661
670
|
* @returns Promise resolving to GenerateResult
|
|
662
|
-
* @throws
|
|
671
|
+
* @throws HydraError subclasses on failure
|
|
663
672
|
*
|
|
664
673
|
* @example
|
|
665
674
|
* ```typescript
|
|
@@ -746,9 +755,9 @@ declare class AIDirector {
|
|
|
746
755
|
* Useful for testing or switching environments
|
|
747
756
|
*
|
|
748
757
|
* @param overrides - Configuration overrides
|
|
749
|
-
* @returns New
|
|
758
|
+
* @returns New Hydra instance
|
|
750
759
|
*/
|
|
751
|
-
withConfig(overrides: Partial<
|
|
760
|
+
withConfig(overrides: Partial<HydraConfig>): Hydra;
|
|
752
761
|
/**
|
|
753
762
|
* Register a webhook for async notifications
|
|
754
763
|
*
|
|
@@ -874,13 +883,13 @@ declare class AIDirector {
|
|
|
874
883
|
}
|
|
875
884
|
|
|
876
885
|
/**
|
|
877
|
-
*
|
|
886
|
+
* Hydra Error Classes
|
|
878
887
|
* Structured error types for better error handling
|
|
879
888
|
*/
|
|
880
889
|
/**
|
|
881
|
-
* Base error class for
|
|
890
|
+
* Base error class for Hydra errors
|
|
882
891
|
*/
|
|
883
|
-
declare class
|
|
892
|
+
declare class HydraError extends Error {
|
|
884
893
|
readonly code: string;
|
|
885
894
|
readonly retryable: boolean;
|
|
886
895
|
readonly statusCode?: number;
|
|
@@ -894,59 +903,59 @@ declare class AIDirectorError extends Error {
|
|
|
894
903
|
/**
|
|
895
904
|
* Configuration error - invalid client setup
|
|
896
905
|
*/
|
|
897
|
-
declare class ConfigurationError extends
|
|
906
|
+
declare class ConfigurationError extends HydraError {
|
|
898
907
|
constructor(message: string);
|
|
899
908
|
}
|
|
900
909
|
/**
|
|
901
910
|
* Authentication error - invalid or expired credentials
|
|
902
911
|
*/
|
|
903
|
-
declare class AuthenticationError extends
|
|
912
|
+
declare class AuthenticationError extends HydraError {
|
|
904
913
|
constructor(message: string, statusCode?: number);
|
|
905
914
|
}
|
|
906
915
|
/**
|
|
907
916
|
* Rate limit error - too many requests
|
|
908
917
|
*/
|
|
909
|
-
declare class RateLimitError extends
|
|
918
|
+
declare class RateLimitError extends HydraError {
|
|
910
919
|
readonly retryAfterMs: number;
|
|
911
920
|
constructor(message: string, retryAfterMs?: number);
|
|
912
921
|
}
|
|
913
922
|
/**
|
|
914
923
|
* Timeout error - request took too long
|
|
915
924
|
*/
|
|
916
|
-
declare class TimeoutError extends
|
|
925
|
+
declare class TimeoutError extends HydraError {
|
|
917
926
|
readonly timeoutMs: number;
|
|
918
927
|
constructor(timeoutMs: number);
|
|
919
928
|
}
|
|
920
929
|
/**
|
|
921
930
|
* Network error - connection failed
|
|
922
931
|
*/
|
|
923
|
-
declare class NetworkError extends
|
|
932
|
+
declare class NetworkError extends HydraError {
|
|
924
933
|
constructor(message: string, cause?: Error);
|
|
925
934
|
}
|
|
926
935
|
/**
|
|
927
936
|
* Chain error - fallback chain execution failed
|
|
928
937
|
*/
|
|
929
|
-
declare class ChainExecutionError extends
|
|
938
|
+
declare class ChainExecutionError extends HydraError {
|
|
930
939
|
readonly attemptedModels: string[];
|
|
931
940
|
constructor(message: string, attemptedModels?: string[]);
|
|
932
941
|
}
|
|
933
942
|
/**
|
|
934
943
|
* Validation error - schema validation failed
|
|
935
944
|
*/
|
|
936
|
-
declare class ValidationError extends
|
|
945
|
+
declare class ValidationError extends HydraError {
|
|
937
946
|
readonly validationErrors: unknown[];
|
|
938
947
|
constructor(message: string, validationErrors?: unknown[]);
|
|
939
948
|
}
|
|
940
949
|
/**
|
|
941
950
|
* Server error - internal server error
|
|
942
951
|
*/
|
|
943
|
-
declare class ServerError extends
|
|
952
|
+
declare class ServerError extends HydraError {
|
|
944
953
|
constructor(message: string, statusCode?: number);
|
|
945
954
|
}
|
|
946
955
|
/**
|
|
947
956
|
* Quota exceeded error - monthly request limit reached
|
|
948
957
|
*/
|
|
949
|
-
declare class QuotaExceededError extends
|
|
958
|
+
declare class QuotaExceededError extends HydraError {
|
|
950
959
|
readonly tier: string;
|
|
951
960
|
readonly limit: number;
|
|
952
961
|
readonly used: number;
|
|
@@ -959,14 +968,14 @@ declare class QuotaExceededError extends AIDirectorError {
|
|
|
959
968
|
/**
|
|
960
969
|
* Worker error - Cloudflare Worker execution failed
|
|
961
970
|
*/
|
|
962
|
-
declare class WorkerError extends
|
|
971
|
+
declare class WorkerError extends HydraError {
|
|
963
972
|
readonly workerDurationMs?: number;
|
|
964
973
|
constructor(message: string, workerDurationMs?: number);
|
|
965
974
|
}
|
|
966
975
|
/**
|
|
967
976
|
* File processing error - attachment handling failed
|
|
968
977
|
*/
|
|
969
|
-
declare class FileProcessingError extends
|
|
978
|
+
declare class FileProcessingError extends HydraError {
|
|
970
979
|
readonly filename?: string;
|
|
971
980
|
readonly mimeType?: string;
|
|
972
981
|
readonly reason: 'unsupported_type' | 'conversion_failed' | 'too_large' | 'corrupted';
|
|
@@ -979,14 +988,14 @@ declare class FileProcessingError extends AIDirectorError {
|
|
|
979
988
|
/**
|
|
980
989
|
* Invalid schema error - provided schema is malformed
|
|
981
990
|
*/
|
|
982
|
-
declare class InvalidSchemaError extends
|
|
991
|
+
declare class InvalidSchemaError extends HydraError {
|
|
983
992
|
readonly schemaPath?: string;
|
|
984
993
|
constructor(message: string, schemaPath?: string);
|
|
985
994
|
}
|
|
986
995
|
/**
|
|
987
|
-
* Check if error is an
|
|
996
|
+
* Check if error is an Hydra error
|
|
988
997
|
*/
|
|
989
|
-
declare function
|
|
998
|
+
declare function isHydraError(error: unknown): error is HydraError;
|
|
990
999
|
/**
|
|
991
1000
|
* Check if error is retryable
|
|
992
1001
|
*/
|
|
@@ -1016,4 +1025,4 @@ declare function getKeyPrefix(secretKey: string): string;
|
|
|
1016
1025
|
*/
|
|
1017
1026
|
declare function isValidSecretKey(secretKey: string): boolean;
|
|
1018
1027
|
|
|
1019
|
-
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 K=typeof process<"u"&&process.versions?.node;async function
|
|
2
|
-
`),d=await
|
|
3
|
-
`),d=await
|
|
4
|
-
`);m=b.pop()||"";let
|
|
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=typeof t?.error=="string"?t.error: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 K=typeof process<"u"&&process.versions?.node;async function
|
|
2
|
-
`),d=await
|
|
3
|
-
`),d=await
|
|
4
|
-
`);m=b.pop()||"";let
|
|
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=typeof t?.error=="string"?t.error: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,87 +1,88 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@altsafe/aidirector",
|
|
3
|
-
"version": "1.2
|
|
4
|
-
"description": "Official TypeScript SDK for
|
|
5
|
-
"main": "./dist/index.js",
|
|
6
|
-
"module": "./dist/index.mjs",
|
|
7
|
-
"types": "./dist/index.d.ts",
|
|
8
|
-
"exports": {
|
|
9
|
-
".": {
|
|
10
|
-
"import": {
|
|
11
|
-
"types": "./dist/index.d.mts",
|
|
12
|
-
"default": "./dist/index.mjs"
|
|
13
|
-
},
|
|
14
|
-
"require": {
|
|
15
|
-
"types": "./dist/index.d.ts",
|
|
16
|
-
"default": "./dist/index.js"
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
},
|
|
20
|
-
"files": [
|
|
21
|
-
"dist",
|
|
22
|
-
"README.md",
|
|
23
|
-
"LICENSE"
|
|
24
|
-
],
|
|
25
|
-
"sideEffects": false,
|
|
26
|
-
"scripts": {
|
|
27
|
-
"build": "tsup",
|
|
28
|
-
"dev": "tsup --watch",
|
|
29
|
-
"lint": "tsc --noEmit",
|
|
30
|
-
"test": "vitest run",
|
|
31
|
-
"test:watch": "vitest",
|
|
32
|
-
"prepublishOnly": "npm run build",
|
|
33
|
-
"clean": "rimraf dist",
|
|
34
|
-
"publish:check": "npm pack --dry-run",
|
|
35
|
-
"release": "npm run build && npm publish",
|
|
36
|
-
"release:
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
"
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
"
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
"
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@altsafe/aidirector",
|
|
3
|
+
"version": "1.4.2",
|
|
4
|
+
"description": "Official TypeScript SDK for Hydra - Intelligent AI API Gateway with automatic failover, caching, and JSON extraction",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": {
|
|
11
|
+
"types": "./dist/index.d.mts",
|
|
12
|
+
"default": "./dist/index.mjs"
|
|
13
|
+
},
|
|
14
|
+
"require": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"default": "./dist/index.js"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"README.md",
|
|
23
|
+
"LICENSE"
|
|
24
|
+
],
|
|
25
|
+
"sideEffects": false,
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsup",
|
|
28
|
+
"dev": "tsup --watch",
|
|
29
|
+
"lint": "tsc --noEmit",
|
|
30
|
+
"test": "vitest run",
|
|
31
|
+
"test:watch": "vitest",
|
|
32
|
+
"prepublishOnly": "npm run build",
|
|
33
|
+
"clean": "rimraf dist",
|
|
34
|
+
"publish:check": "npm pack --dry-run",
|
|
35
|
+
"release": "npm run build && npm publish --access public",
|
|
36
|
+
"release:dual": "node scripts/publish-dual.js",
|
|
37
|
+
"release:beta": "npm run build && npm publish --tag beta"
|
|
38
|
+
},
|
|
39
|
+
"keywords": [
|
|
40
|
+
"ai",
|
|
41
|
+
"api",
|
|
42
|
+
"gateway",
|
|
43
|
+
"gemini",
|
|
44
|
+
"openrouter",
|
|
45
|
+
"openai",
|
|
46
|
+
"llm",
|
|
47
|
+
"fallback",
|
|
48
|
+
"sdk",
|
|
49
|
+
"typescript",
|
|
50
|
+
"json",
|
|
51
|
+
"cache",
|
|
52
|
+
"hmac"
|
|
53
|
+
],
|
|
54
|
+
"author": {
|
|
55
|
+
"name": "Hydra",
|
|
56
|
+
"url": "https://hydrai.dev"
|
|
57
|
+
},
|
|
58
|
+
"license": "MIT",
|
|
59
|
+
"homepage": "https://hydrai.dev",
|
|
60
|
+
"bugs": {
|
|
61
|
+
"url": "https://github.com/hydrai/client/issues"
|
|
62
|
+
},
|
|
63
|
+
"repository": {
|
|
64
|
+
"type": "git",
|
|
65
|
+
"url": "git+https://github.com/hydrai/client.git"
|
|
66
|
+
},
|
|
67
|
+
"publishConfig": {
|
|
68
|
+
"access": "public",
|
|
69
|
+
"registry": "https://registry.npmjs.org/"
|
|
70
|
+
},
|
|
71
|
+
"devDependencies": {
|
|
72
|
+
"rimraf": "^5.0.0",
|
|
73
|
+
"tsup": "^8.0.0",
|
|
74
|
+
"typescript": "^5.0.0",
|
|
75
|
+
"vitest": "^4.0.0"
|
|
76
|
+
},
|
|
77
|
+
"peerDependencies": {
|
|
78
|
+
"typescript": ">=5.0.0"
|
|
79
|
+
},
|
|
80
|
+
"peerDependenciesMeta": {
|
|
81
|
+
"typescript": {
|
|
82
|
+
"optional": true
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
"engines": {
|
|
86
|
+
"node": ">=18.0.0"
|
|
87
|
+
}
|
|
88
|
+
}
|