@altsafe/aidirector 1.2.1 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2024 AI Director
3
+ Copyright (c) 2026 Hydra
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,27 +1,35 @@
1
- # @altsafe/aidirector - Client SDK
1
+ # hydra-aidirector - Client SDK
2
+ # hydra-aidirector
2
3
 
3
- Production-grade TypeScript SDK for the AI Director API gateway.
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 @altsafe/aidirector
16
+ npm install hydra-aidirector
9
17
  # or
10
- pnpm add @altsafe/aidirector
18
+ pnpm add hydra-aidirector
11
19
  ```
12
20
 
13
21
  ## Quick Start
14
22
 
15
23
  ```typescript
16
- import { AIDirector } from '@altsafe/aidirector';
24
+ import { Hydra } from 'hydra-aidirector';
17
25
 
18
- const client = new AIDirector({
19
- secretKey: process.env.AIDIRECTOR_SECRET_KEY!,
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 client.generate({
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/ai-director',
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 (`aid_sk_...`) |
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 '@altsafe/aidirector';
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
- * AI Director Client Types
2
+ * Hydra Client Types
3
3
  * Type definitions for the SDK
4
4
  */
5
5
  /**
6
- * Configuration for AI Director client
6
+ * Configuration for Hydra client
7
7
  */
8
- interface AIDirectorConfig {
8
+ interface HydraConfig {
9
9
  /**
10
- * Your AI Director secret key
11
- * Get this from your dashboard at https://aidirector.dev/dashboard/keys
12
- * Format: aid_sk_<random>
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.aidirector.dev'
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
- * AI Director Client SDK
626
+ * Hydra Client SDK
618
627
  *
619
- * Production-grade client for the AI Director API gateway.
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 { AIDirector } from '@aidirector/client';
640
+ * import { Hydra } from 'hydra-aidirector';
632
641
  *
633
- * const client = new AIDirector({
634
- * secretKey: process.env.AIDIRECTOR_SECRET_KEY!,
635
- * baseUrl: 'https://api.aidirector.dev',
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 AIDirector {
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: AIDirectorConfig);
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 AIDirectorError subclasses on failure
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 AIDirector instance
758
+ * @returns New Hydra instance
750
759
  */
751
- withConfig(overrides: Partial<AIDirectorConfig>): AIDirector;
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
- * AI Director Error Classes
886
+ * Hydra Error Classes
878
887
  * Structured error types for better error handling
879
888
  */
880
889
  /**
881
- * Base error class for AI Director errors
890
+ * Base error class for Hydra errors
882
891
  */
883
- declare class AIDirectorError extends Error {
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 AIDirectorError {
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 AIDirectorError {
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 AIDirectorError {
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 AIDirectorError {
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 AIDirectorError {
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 AIDirectorError {
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 AIDirectorError {
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 AIDirectorError {
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 AIDirectorError {
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 AIDirectorError {
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 AIDirectorError {
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 AIDirectorError {
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 AI Director error
996
+ * Check if error is an Hydra error
988
997
  */
989
- declare function isAIDirectorError(error: unknown): error is AIDirectorError;
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 { AIDirector, type AIDirectorConfig, AIDirectorError, AuthenticationError, ChainExecutionError, type ChainInfo, type ChainStep, ConfigurationError, type DetailedHealthResult, type FileAttachment, FileProcessingError, type GenerateData, type GenerateError, type GenerateMeta, type GenerateOptions, type GenerateResult, type GenerationParameters, type HealthResult, InvalidSchemaError, type ModelInfo, NetworkError, QuotaExceededError, RateLimitError, ServerError, type StreamCallbacks, type StreamCompleteResult, type StreamProgress, TimeoutError, type TokenUsage, type UsageStats, ValidationError, type WebhookConfig, WorkerError, generateSignature, getKeyPrefix, isAIDirectorError, isRetryableError, isValidSecretKey };
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
- * AI Director Client Types
2
+ * Hydra Client Types
3
3
  * Type definitions for the SDK
4
4
  */
5
5
  /**
6
- * Configuration for AI Director client
6
+ * Configuration for Hydra client
7
7
  */
8
- interface AIDirectorConfig {
8
+ interface HydraConfig {
9
9
  /**
10
- * Your AI Director secret key
11
- * Get this from your dashboard at https://aidirector.dev/dashboard/keys
12
- * Format: aid_sk_<random>
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.aidirector.dev'
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
- * AI Director Client SDK
626
+ * Hydra Client SDK
618
627
  *
619
- * Production-grade client for the AI Director API gateway.
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 { AIDirector } from '@aidirector/client';
640
+ * import { Hydra } from 'hydra-aidirector';
632
641
  *
633
- * const client = new AIDirector({
634
- * secretKey: process.env.AIDIRECTOR_SECRET_KEY!,
635
- * baseUrl: 'https://api.aidirector.dev',
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 AIDirector {
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: AIDirectorConfig);
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 AIDirectorError subclasses on failure
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 AIDirector instance
758
+ * @returns New Hydra instance
750
759
  */
751
- withConfig(overrides: Partial<AIDirectorConfig>): AIDirector;
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
- * AI Director Error Classes
886
+ * Hydra Error Classes
878
887
  * Structured error types for better error handling
879
888
  */
880
889
  /**
881
- * Base error class for AI Director errors
890
+ * Base error class for Hydra errors
882
891
  */
883
- declare class AIDirectorError extends Error {
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 AIDirectorError {
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 AIDirectorError {
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 AIDirectorError {
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 AIDirectorError {
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 AIDirectorError {
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 AIDirectorError {
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 AIDirectorError {
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 AIDirectorError {
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 AIDirectorError {
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 AIDirectorError {
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 AIDirectorError {
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 AIDirectorError {
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 AI Director error
996
+ * Check if error is an Hydra error
988
997
  */
989
- declare function isAIDirectorError(error: unknown): error is AIDirectorError;
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 { AIDirector, type AIDirectorConfig, AIDirectorError, AuthenticationError, ChainExecutionError, type ChainInfo, type ChainStep, ConfigurationError, type DetailedHealthResult, type FileAttachment, FileProcessingError, type GenerateData, type GenerateError, type GenerateMeta, type GenerateOptions, type GenerateResult, type GenerationParameters, type HealthResult, InvalidSchemaError, type ModelInfo, NetworkError, QuotaExceededError, RateLimitError, ServerError, type StreamCallbacks, type StreamCompleteResult, type StreamProgress, TimeoutError, type TokenUsage, type UsageStats, ValidationError, type WebhookConfig, WorkerError, generateSignature, getKeyPrefix, isAIDirectorError, isRetryableError, isValidSecretKey };
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 G(n){let{createHash:e}=await import('crypto');return e("sha256").update(n).digest("hex")}async function L(n,e,t,r,a){let{createHmac:s}=await import('crypto'),c=[e.toUpperCase(),t,a.toString(),r].join(`
2
- `),d=await G(n);return s("sha256",d).update(c).digest("hex")}async function W(n){let t=new TextEncoder().encode(n),r=await crypto.subtle.digest("SHA-256",t);return Array.from(new Uint8Array(r)).map(s=>s.toString(16).padStart(2,"0")).join("")}async function q(n,e,t,r,a){let s=new TextEncoder,c=[e.toUpperCase(),t,a.toString(),r].join(`
3
- `),d=await W(n),o=s.encode(d),i=await crypto.subtle.importKey("raw",o,{name:"HMAC",hash:"SHA-256"},false,["sign"]),m=await crypto.subtle.sign("HMAC",i,s.encode(c));return Array.from(new Uint8Array(m)).map(p=>p.toString(16).padStart(2,"0")).join("")}async function S(n,e,t,r,a){return K?L(n,e,t,r,a):q(n,e,t,r,a)}function I(n){return n.slice(0,12)}function U(n){return typeof n=="string"&&n.startsWith("aid_sk_")&&n.length>=20}var u=class extends Error{constructor(e,t,r){super(e),this.name="AIDirectorError",this.code=t,this.retryable=r?.retryable??false,this.statusCode=r?.statusCode,this.originalError=r?.cause;}},k=class extends u{constructor(e){super(e,"CONFIGURATION_ERROR",{retryable:false}),this.name="ConfigurationError";}},A=class extends u{constructor(e,t){super(e,"AUTH_ERROR",{retryable:false,statusCode:t}),this.name="AuthenticationError";}},E=class extends u{constructor(e,t=6e4){super(e,"RATE_LIMITED",{retryable:true,statusCode:429}),this.name="RateLimitError",this.retryAfterMs=t;}},R=class extends u{constructor(e){super(`Request timed out after ${e}ms`,"TIMEOUT",{retryable:true}),this.name="TimeoutError",this.timeoutMs=e;}},w=class extends u{constructor(e,t){super(e,"NETWORK_ERROR",{retryable:true,cause:t}),this.name="NetworkError";}},y=class extends u{constructor(e,t=[]){super(e,"CHAIN_FAILED",{retryable:false}),this.name="ChainExecutionError",this.attemptedModels=t;}},C=class extends u{constructor(e,t=[]){super(e,"VALIDATION_ERROR",{retryable:false}),this.name="ValidationError",this.validationErrors=t;}},g=class extends u{constructor(e,t=500){super(e,"SERVER_ERROR",{retryable:true,statusCode:t}),this.name="ServerError";}},P=class extends u{constructor(e,t){super(e,"QUOTA_EXCEEDED",{retryable:false,statusCode:402}),this.name="QuotaExceededError",this.tier=t.tier,this.limit=t.limit,this.used=t.used;}},v=class extends u{constructor(e,t){super(e,"WORKER_ERROR",{retryable:true}),this.name="WorkerError",this.workerDurationMs=t;}},O=class extends u{constructor(e,t){super(e,"FILE_PROCESSING_ERROR",{retryable:false}),this.name="FileProcessingError",this.filename=t.filename,this.mimeType=t.mimeType,this.reason=t.reason;}},D=class extends u{constructor(e,t){super(e,"INVALID_SCHEMA",{retryable:false}),this.name="InvalidSchemaError",this.schemaPath=t;}};function _(n){return n instanceof u}function F(n){return _(n)?n.retryable:false}var H="http://localhost:3000",$=6e5,B=3,N=[1e3,2e3,5e3];function J(n){let e=n.trim(),t=e.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/);t&&(e=t[1].trim());let r=e.indexOf("{"),a=e.indexOf("["),s;if(r===-1&&a===-1)return e;r===-1?s=a:a===-1?s=r:s=Math.min(r,a);let c=e[s],d=c==="{"?"}":"]",o=0,i=false,m=false,f=-1;for(let h=s;h<e.length;h++){let b=e[h];if(m){m=false;continue}if(b==="\\"&&i){m=true;continue}if(b==='"'&&!m){i=!i;continue}if(!i){if(b===c)o++;else if(b===d&&(o--,o===0)){f=h;break}}}if(f!==-1)return e.slice(s,f+1);let p=e.lastIndexOf(d);return p>s?e.slice(s,p+1):e}var M=class n{constructor(e){if(!e.secretKey)throw new k("secretKey is required");if(!U(e.secretKey))throw new k("Invalid secret key format. Expected format: aid_sk_<key>");this.secretKey=e.secretKey,this.baseUrl=(e.baseUrl||H).replace(/\/$/,""),this.timeout=e.timeout??$,this.maxRetries=e.maxRetries??B,this.keyPrefix=I(e.secretKey),this.debug=e.debug??false,this.debug&&this.log("Initialized",{baseUrl:this.baseUrl,timeout:this.timeout});}async generate(e){if(e.useOptimized!==false)try{return await this.generateOptimized(e)}catch(r){this.debug&&this.log("3-step generation failed, falling back to legacy",{error:r instanceof Error?r.message:String(r)});}return this.generateLegacy(e)}async generateOptimized(e){let t=Date.now(),r="/api/v1/generate/token",a=JSON.stringify({chainId:e.chainId,prompt:e.prompt,systemPrompt:e.options?.systemPrompt,temperature:e.options?.temperature,maxTokens:e.options?.maxTokens,topP:e.options?.topP,topK:e.options?.topK,files:e.files}),s=await this.makeAuthenticatedRequest(r,"POST",a);if(s.cached&&s.data)return this.debug&&this.log("generate cache hit (optimized)",{latencyMs:Date.now()-t}),{success:true,data:s.data,meta:{cached:true,modelUsed:s.meta?.modelUsed||"",tokensUsed:{input:0,output:0},latencyMs:Date.now()-t,attemptedModels:[]}};if(!s.token||!s.workerUrl)throw new y("Token generation failed - no token or worker URL returned",[]);let c=JSON.stringify({token:s.token,prompt:e.prompt,systemPrompt:e.options?.systemPrompt,timeout:e.timeout??this.timeout}),o=await(await this.fetchWithTimeout(s.workerUrl,{method:"POST",headers:{"Content-Type":"application/json"},body:c},e.timeout??this.timeout)).json();if(!o.success||!o.content)return {success:false,data:{valid:[],invalid:[]},meta:{cached:false,modelUsed:o.modelId||"",tokensUsed:o.tokensUsed||{input:0,output:0},latencyMs:Date.now()-t,attemptedModels:[o.modelId||""]},error:{code:o.errorCode||"GENERATION_FAILED",message:o.errorMessage||"Generation failed",retryable:false}};let i=[],m=[],f=J(o.content);try{let h=JSON.parse(f);Array.isArray(h)?i=h:i=[h];}catch{i=[f];}o.completionToken&&this.cacheCompletionAsync({completionToken:o.completionToken,content:o.content,tokensUsed:o.tokensUsed||{input:0,output:0},finishReason:o.finishReason||"stop",chainId:e.chainId,modelUsed:o.modelId,prompt:e.prompt,systemPrompt:e.options?.systemPrompt,temperature:e.options?.temperature,maxTokens:e.options?.maxTokens,topP:e.options?.topP,topK:e.options?.topK}).catch(h=>{this.debug&&this.log("cacheCompletion failed (non-blocking)",{error:h});});let p=Date.now()-t;return this.debug&&this.log("generate success (optimized 3-step)",{latencyMs:p,modelUsed:o.modelId,tokensUsed:o.tokensUsed}),{success:true,data:{valid:i,invalid:m},meta:{cached:false,modelUsed:o.modelId,tokensUsed:o.tokensUsed||{input:0,output:0},latencyMs:p,attemptedModels:[o.modelId]}}}async cacheCompletionAsync(e){let t="/api/v1/generate/complete",r=JSON.stringify(e);await this.makeAuthenticatedRequest(t,"POST",r);}async generateLegacy(e){let t=Date.now(),r="/api/v1/generate",a=JSON.stringify({chainId:e.chainId,prompt:e.prompt,schema:e.schema,noCache:e.noCache,files:e.files,options:{...e.options,timeout:e.timeout??this.timeout}}),s=null;for(let o=0;o<=this.maxRetries;o++)try{let i=await this.makeAuthenticatedRequest(r,"POST",a,e.timeout),m=Date.now()-t;return this.debug&&this.log("generate success (legacy)",{latencyMs:m,attempt:o+1}),i.meta&&(i.meta.latencyMs=m),i}catch(i){if(s=i instanceof Error?i:new Error(String(i)),this.debug&&this.log(`generate attempt ${o+1} failed`,{error:s.message}),!this.isRetryable(i)||o>=this.maxRetries)break;let m=N[Math.min(o,N.length-1)];i instanceof E&&i.retryAfterMs>m?await this.sleep(i.retryAfterMs):await this.sleep(m);}let c=Date.now()-t,d=s instanceof R;return {success:false,data:{valid:[],invalid:[]},meta:{cached:false,modelUsed:"",tokensUsed:{input:0,output:0},latencyMs:c,attemptedModels:[]},error:{code:d?"TIMEOUT":"REQUEST_FAILED",message:s?.message||"Request failed after all retries",retryable:false}}}async generateStream(e,t){let r="/api/v1/generate/stream",a=JSON.stringify({chainId:e.chainId,prompt:e.prompt,schema:e.schema,options:e.options});try{let s=Date.now(),c=await S(this.secretKey,"POST",r,a,s),d=await this.fetchWithTimeout(`${this.baseUrl}${r}`,{method:"POST",headers:{"Content-Type":"application/json",Accept:"text/event-stream","x-aidirector-signature":c,"x-aidirector-timestamp":s.toString(),"x-aidirector-key":this.keyPrefix},body:a},e.timeout??this.timeout);if(!d.ok){let p=await d.json();throw this.parseError(d.status,p)}let o=d.body?.getReader();if(!o)throw new w("Response body is not readable");let i=new TextDecoder,m="",f=[];for(;;){let{done:p,value:h}=await o.read();if(p)break;m+=i.decode(h,{stream:!0});let b=m.split(`
4
- `);m=b.pop()||"";let T="";for(let x of b){if(x.startsWith("event: ")){T=x.slice(7).trim();continue}if(x.startsWith("data: ")){let j=x.slice(6);if(j==="[DONE]")break;try{let l=JSON.parse(j);switch(T){case "start":this.debug&&this.log("Stream started",l);break;case "object":l.object!==void 0&&(f.push(l.object),t.onObject?.(l.object,l.index)),t.onChunk&&l.object&&t.onChunk(JSON.stringify(l.object));break;case "array_start":t.onArrayStart?.();break;case "complete":t.onComplete&&t.onComplete({objects:f,objectCount:l.objectCount,tokensUsed:l.tokensUsed,cached:l.cached});break;case "error":t.onError&&t.onError(new y(l.error||"Stream error",["STREAM_ERROR"]));break;default:l.chunk&&t.onChunk?.(l.chunk);}}catch{}T="";}}}}catch(s){if(t.onError)t.onError(s instanceof Error?s:new Error(String(s)));else throw s}}async listModels(){let t=await(await this.fetchWithTimeout(`${this.baseUrl}/api/v1/models`,{method:"GET",headers:{Accept:"application/json"}})).json();if(!t.success)throw new g(t.error?.message||"Failed to list models");return t.data}async listChains(){let t=await(await this.fetchWithTimeout(`${this.baseUrl}/api/v1/chains`,{method:"GET",credentials:"include",headers:{Accept:"application/json"}})).json();if(!t.success)throw new g(t.error?.message||"Failed to list chains");return t.data}async getUsage(e){let t=new URLSearchParams;e?.startDate&&t.set("startDate",e.startDate.toISOString()),e?.endDate&&t.set("endDate",e.endDate.toISOString());let r=`${this.baseUrl}/api/v1/usage${t.toString()?"?"+t:""}`,s=await(await this.fetchWithTimeout(r,{method:"GET",credentials:"include",headers:{Accept:"application/json"}})).json();if(!s.success)throw new g(s.error?.message||"Failed to get usage");return s.data}async health(){let e=Date.now();try{let t=await this.fetchWithTimeout(`${this.baseUrl}/api/health`,{method:"GET"},5e3),r=Date.now()-e,a=await t.json().catch(()=>({}));return {ok:t.ok,latencyMs:r,version:a.version}}catch{return {ok:false,latencyMs:Date.now()-e}}}withConfig(e){return new n({secretKey:this.secretKey,baseUrl:this.baseUrl,timeout:this.timeout,maxRetries:this.maxRetries,debug:this.debug,...e})}async registerWebhook(e){let t=await this.makeAuthenticatedRequest("/api/v1/webhooks","POST",JSON.stringify(e));if(!t.success||!t.data)throw new g(t.error?.message||"Failed to register webhook");return t.data}async unregisterWebhook(e){let t=await this.makeAuthenticatedRequest(`/api/v1/webhooks/${e}`,"DELETE","");if(!t.success||!t.data)throw new g(t.error?.message||"Failed to unregister webhook");return t.data}async listWebhooks(){let e=await this.makeAuthenticatedRequest("/api/v1/webhooks","GET","");if(!e.success)throw new g(e.error?.message||"Failed to list webhooks");return e.data||[]}async updateWebhook(e,t){let r=await this.makeAuthenticatedRequest(`/api/v1/webhooks/${e}`,"PATCH",JSON.stringify(t));if(!r.success||!r.data)throw new g(r.error?.message||"Failed to update webhook");return r.data}async healthDetailed(){return (await fetch(`${this.baseUrl}/api/v1/health/detailed`,{headers:{Accept:"application/json"}})).json()}async generateBatch(e,t,r={}){let a=await this.makeAuthenticatedRequest("/api/v1/generate/batch","POST",JSON.stringify({chainId:e,items:t,continueOnError:r.continueOnError??true}));if(!a.success||!a.data)throw new y(a.error?.message||"Batch generation failed",[]);return a.data}async makeAuthenticatedRequest(e,t,r,a){let s=Date.now(),c=await S(this.secretKey,t,e,r,s),d=await this.fetchWithTimeout(`${this.baseUrl}${e}`,{method:t,headers:{"Content-Type":"application/json",Accept:"application/json","x-aidirector-signature":c,"x-aidirector-timestamp":s.toString(),"x-aidirector-key":this.keyPrefix},body:r},a??this.timeout),o=await d.json();if(!d.ok)throw this.parseError(d.status,o);return o}async fetchWithTimeout(e,t,r=this.timeout){let a=new AbortController,s=setTimeout(()=>a.abort(),r);try{return await fetch(e,{...t,signal:a.signal})}catch(c){throw c instanceof Error&&c.name==="AbortError"?new R(r):c instanceof Error?new w(c.message,c):new w(String(c))}finally{clearTimeout(s);}}parseError(e,t){let r=t?.error?.message||`HTTP ${e}`,a=t?.error?.code||"UNKNOWN";switch(e){case 401:return new A(r,e);case 429:let s=t?.retryAfterMs||6e4;return new E(r,s);case 500:case 502:case 503:case 504:return new g(r,e);default:return a==="CHAIN_FAILED"?new y(r,t?.error?.attemptedModels||[]):new u(r,a,{statusCode:e,retryable:e>=500})}}isRetryable(e){if(e instanceof u)return e.retryable;if(e instanceof Error){let t=e.message.toLowerCase();return t.includes("network")||t.includes("timeout")||e.name==="AbortError"}return false}sleep(e){return new Promise(t=>setTimeout(t,e))}log(e,t){let r="[AIDirector]";t?console.log(r,e,t):console.log(r,e);}};exports.AIDirector=M;exports.AIDirectorError=u;exports.AuthenticationError=A;exports.ChainExecutionError=y;exports.ConfigurationError=k;exports.FileProcessingError=O;exports.InvalidSchemaError=D;exports.NetworkError=w;exports.QuotaExceededError=P;exports.RateLimitError=E;exports.ServerError=g;exports.TimeoutError=R;exports.ValidationError=C;exports.WorkerError=v;exports.generateSignature=S;exports.getKeyPrefix=I;exports.isAIDirectorError=_;exports.isRetryableError=F;exports.isValidSecretKey=U;
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 K=typeof process<"u"&&process.versions?.node;async function G(n){let{createHash:e}=await import('crypto');return e("sha256").update(n).digest("hex")}async function L(n,e,t,r,a){let{createHmac:s}=await import('crypto'),c=[e.toUpperCase(),t,a.toString(),r].join(`
2
- `),d=await G(n);return s("sha256",d).update(c).digest("hex")}async function W(n){let t=new TextEncoder().encode(n),r=await crypto.subtle.digest("SHA-256",t);return Array.from(new Uint8Array(r)).map(s=>s.toString(16).padStart(2,"0")).join("")}async function q(n,e,t,r,a){let s=new TextEncoder,c=[e.toUpperCase(),t,a.toString(),r].join(`
3
- `),d=await W(n),o=s.encode(d),i=await crypto.subtle.importKey("raw",o,{name:"HMAC",hash:"SHA-256"},false,["sign"]),m=await crypto.subtle.sign("HMAC",i,s.encode(c));return Array.from(new Uint8Array(m)).map(p=>p.toString(16).padStart(2,"0")).join("")}async function S(n,e,t,r,a){return K?L(n,e,t,r,a):q(n,e,t,r,a)}function I(n){return n.slice(0,12)}function U(n){return typeof n=="string"&&n.startsWith("aid_sk_")&&n.length>=20}var u=class extends Error{constructor(e,t,r){super(e),this.name="AIDirectorError",this.code=t,this.retryable=r?.retryable??false,this.statusCode=r?.statusCode,this.originalError=r?.cause;}},k=class extends u{constructor(e){super(e,"CONFIGURATION_ERROR",{retryable:false}),this.name="ConfigurationError";}},A=class extends u{constructor(e,t){super(e,"AUTH_ERROR",{retryable:false,statusCode:t}),this.name="AuthenticationError";}},E=class extends u{constructor(e,t=6e4){super(e,"RATE_LIMITED",{retryable:true,statusCode:429}),this.name="RateLimitError",this.retryAfterMs=t;}},R=class extends u{constructor(e){super(`Request timed out after ${e}ms`,"TIMEOUT",{retryable:true}),this.name="TimeoutError",this.timeoutMs=e;}},w=class extends u{constructor(e,t){super(e,"NETWORK_ERROR",{retryable:true,cause:t}),this.name="NetworkError";}},y=class extends u{constructor(e,t=[]){super(e,"CHAIN_FAILED",{retryable:false}),this.name="ChainExecutionError",this.attemptedModels=t;}},C=class extends u{constructor(e,t=[]){super(e,"VALIDATION_ERROR",{retryable:false}),this.name="ValidationError",this.validationErrors=t;}},g=class extends u{constructor(e,t=500){super(e,"SERVER_ERROR",{retryable:true,statusCode:t}),this.name="ServerError";}},P=class extends u{constructor(e,t){super(e,"QUOTA_EXCEEDED",{retryable:false,statusCode:402}),this.name="QuotaExceededError",this.tier=t.tier,this.limit=t.limit,this.used=t.used;}},v=class extends u{constructor(e,t){super(e,"WORKER_ERROR",{retryable:true}),this.name="WorkerError",this.workerDurationMs=t;}},O=class extends u{constructor(e,t){super(e,"FILE_PROCESSING_ERROR",{retryable:false}),this.name="FileProcessingError",this.filename=t.filename,this.mimeType=t.mimeType,this.reason=t.reason;}},D=class extends u{constructor(e,t){super(e,"INVALID_SCHEMA",{retryable:false}),this.name="InvalidSchemaError",this.schemaPath=t;}};function _(n){return n instanceof u}function F(n){return _(n)?n.retryable:false}var H="http://localhost:3000",$=6e5,B=3,N=[1e3,2e3,5e3];function J(n){let e=n.trim(),t=e.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/);t&&(e=t[1].trim());let r=e.indexOf("{"),a=e.indexOf("["),s;if(r===-1&&a===-1)return e;r===-1?s=a:a===-1?s=r:s=Math.min(r,a);let c=e[s],d=c==="{"?"}":"]",o=0,i=false,m=false,f=-1;for(let h=s;h<e.length;h++){let b=e[h];if(m){m=false;continue}if(b==="\\"&&i){m=true;continue}if(b==='"'&&!m){i=!i;continue}if(!i){if(b===c)o++;else if(b===d&&(o--,o===0)){f=h;break}}}if(f!==-1)return e.slice(s,f+1);let p=e.lastIndexOf(d);return p>s?e.slice(s,p+1):e}var M=class n{constructor(e){if(!e.secretKey)throw new k("secretKey is required");if(!U(e.secretKey))throw new k("Invalid secret key format. Expected format: aid_sk_<key>");this.secretKey=e.secretKey,this.baseUrl=(e.baseUrl||H).replace(/\/$/,""),this.timeout=e.timeout??$,this.maxRetries=e.maxRetries??B,this.keyPrefix=I(e.secretKey),this.debug=e.debug??false,this.debug&&this.log("Initialized",{baseUrl:this.baseUrl,timeout:this.timeout});}async generate(e){if(e.useOptimized!==false)try{return await this.generateOptimized(e)}catch(r){this.debug&&this.log("3-step generation failed, falling back to legacy",{error:r instanceof Error?r.message:String(r)});}return this.generateLegacy(e)}async generateOptimized(e){let t=Date.now(),r="/api/v1/generate/token",a=JSON.stringify({chainId:e.chainId,prompt:e.prompt,systemPrompt:e.options?.systemPrompt,temperature:e.options?.temperature,maxTokens:e.options?.maxTokens,topP:e.options?.topP,topK:e.options?.topK,files:e.files}),s=await this.makeAuthenticatedRequest(r,"POST",a);if(s.cached&&s.data)return this.debug&&this.log("generate cache hit (optimized)",{latencyMs:Date.now()-t}),{success:true,data:s.data,meta:{cached:true,modelUsed:s.meta?.modelUsed||"",tokensUsed:{input:0,output:0},latencyMs:Date.now()-t,attemptedModels:[]}};if(!s.token||!s.workerUrl)throw new y("Token generation failed - no token or worker URL returned",[]);let c=JSON.stringify({token:s.token,prompt:e.prompt,systemPrompt:e.options?.systemPrompt,timeout:e.timeout??this.timeout}),o=await(await this.fetchWithTimeout(s.workerUrl,{method:"POST",headers:{"Content-Type":"application/json"},body:c},e.timeout??this.timeout)).json();if(!o.success||!o.content)return {success:false,data:{valid:[],invalid:[]},meta:{cached:false,modelUsed:o.modelId||"",tokensUsed:o.tokensUsed||{input:0,output:0},latencyMs:Date.now()-t,attemptedModels:[o.modelId||""]},error:{code:o.errorCode||"GENERATION_FAILED",message:o.errorMessage||"Generation failed",retryable:false}};let i=[],m=[],f=J(o.content);try{let h=JSON.parse(f);Array.isArray(h)?i=h:i=[h];}catch{i=[f];}o.completionToken&&this.cacheCompletionAsync({completionToken:o.completionToken,content:o.content,tokensUsed:o.tokensUsed||{input:0,output:0},finishReason:o.finishReason||"stop",chainId:e.chainId,modelUsed:o.modelId,prompt:e.prompt,systemPrompt:e.options?.systemPrompt,temperature:e.options?.temperature,maxTokens:e.options?.maxTokens,topP:e.options?.topP,topK:e.options?.topK}).catch(h=>{this.debug&&this.log("cacheCompletion failed (non-blocking)",{error:h});});let p=Date.now()-t;return this.debug&&this.log("generate success (optimized 3-step)",{latencyMs:p,modelUsed:o.modelId,tokensUsed:o.tokensUsed}),{success:true,data:{valid:i,invalid:m},meta:{cached:false,modelUsed:o.modelId,tokensUsed:o.tokensUsed||{input:0,output:0},latencyMs:p,attemptedModels:[o.modelId]}}}async cacheCompletionAsync(e){let t="/api/v1/generate/complete",r=JSON.stringify(e);await this.makeAuthenticatedRequest(t,"POST",r);}async generateLegacy(e){let t=Date.now(),r="/api/v1/generate",a=JSON.stringify({chainId:e.chainId,prompt:e.prompt,schema:e.schema,noCache:e.noCache,files:e.files,options:{...e.options,timeout:e.timeout??this.timeout}}),s=null;for(let o=0;o<=this.maxRetries;o++)try{let i=await this.makeAuthenticatedRequest(r,"POST",a,e.timeout),m=Date.now()-t;return this.debug&&this.log("generate success (legacy)",{latencyMs:m,attempt:o+1}),i.meta&&(i.meta.latencyMs=m),i}catch(i){if(s=i instanceof Error?i:new Error(String(i)),this.debug&&this.log(`generate attempt ${o+1} failed`,{error:s.message}),!this.isRetryable(i)||o>=this.maxRetries)break;let m=N[Math.min(o,N.length-1)];i instanceof E&&i.retryAfterMs>m?await this.sleep(i.retryAfterMs):await this.sleep(m);}let c=Date.now()-t,d=s instanceof R;return {success:false,data:{valid:[],invalid:[]},meta:{cached:false,modelUsed:"",tokensUsed:{input:0,output:0},latencyMs:c,attemptedModels:[]},error:{code:d?"TIMEOUT":"REQUEST_FAILED",message:s?.message||"Request failed after all retries",retryable:false}}}async generateStream(e,t){let r="/api/v1/generate/stream",a=JSON.stringify({chainId:e.chainId,prompt:e.prompt,schema:e.schema,options:e.options});try{let s=Date.now(),c=await S(this.secretKey,"POST",r,a,s),d=await this.fetchWithTimeout(`${this.baseUrl}${r}`,{method:"POST",headers:{"Content-Type":"application/json",Accept:"text/event-stream","x-aidirector-signature":c,"x-aidirector-timestamp":s.toString(),"x-aidirector-key":this.keyPrefix},body:a},e.timeout??this.timeout);if(!d.ok){let p=await d.json();throw this.parseError(d.status,p)}let o=d.body?.getReader();if(!o)throw new w("Response body is not readable");let i=new TextDecoder,m="",f=[];for(;;){let{done:p,value:h}=await o.read();if(p)break;m+=i.decode(h,{stream:!0});let b=m.split(`
4
- `);m=b.pop()||"";let T="";for(let x of b){if(x.startsWith("event: ")){T=x.slice(7).trim();continue}if(x.startsWith("data: ")){let j=x.slice(6);if(j==="[DONE]")break;try{let l=JSON.parse(j);switch(T){case "start":this.debug&&this.log("Stream started",l);break;case "object":l.object!==void 0&&(f.push(l.object),t.onObject?.(l.object,l.index)),t.onChunk&&l.object&&t.onChunk(JSON.stringify(l.object));break;case "array_start":t.onArrayStart?.();break;case "complete":t.onComplete&&t.onComplete({objects:f,objectCount:l.objectCount,tokensUsed:l.tokensUsed,cached:l.cached});break;case "error":t.onError&&t.onError(new y(l.error||"Stream error",["STREAM_ERROR"]));break;default:l.chunk&&t.onChunk?.(l.chunk);}}catch{}T="";}}}}catch(s){if(t.onError)t.onError(s instanceof Error?s:new Error(String(s)));else throw s}}async listModels(){let t=await(await this.fetchWithTimeout(`${this.baseUrl}/api/v1/models`,{method:"GET",headers:{Accept:"application/json"}})).json();if(!t.success)throw new g(t.error?.message||"Failed to list models");return t.data}async listChains(){let t=await(await this.fetchWithTimeout(`${this.baseUrl}/api/v1/chains`,{method:"GET",credentials:"include",headers:{Accept:"application/json"}})).json();if(!t.success)throw new g(t.error?.message||"Failed to list chains");return t.data}async getUsage(e){let t=new URLSearchParams;e?.startDate&&t.set("startDate",e.startDate.toISOString()),e?.endDate&&t.set("endDate",e.endDate.toISOString());let r=`${this.baseUrl}/api/v1/usage${t.toString()?"?"+t:""}`,s=await(await this.fetchWithTimeout(r,{method:"GET",credentials:"include",headers:{Accept:"application/json"}})).json();if(!s.success)throw new g(s.error?.message||"Failed to get usage");return s.data}async health(){let e=Date.now();try{let t=await this.fetchWithTimeout(`${this.baseUrl}/api/health`,{method:"GET"},5e3),r=Date.now()-e,a=await t.json().catch(()=>({}));return {ok:t.ok,latencyMs:r,version:a.version}}catch{return {ok:false,latencyMs:Date.now()-e}}}withConfig(e){return new n({secretKey:this.secretKey,baseUrl:this.baseUrl,timeout:this.timeout,maxRetries:this.maxRetries,debug:this.debug,...e})}async registerWebhook(e){let t=await this.makeAuthenticatedRequest("/api/v1/webhooks","POST",JSON.stringify(e));if(!t.success||!t.data)throw new g(t.error?.message||"Failed to register webhook");return t.data}async unregisterWebhook(e){let t=await this.makeAuthenticatedRequest(`/api/v1/webhooks/${e}`,"DELETE","");if(!t.success||!t.data)throw new g(t.error?.message||"Failed to unregister webhook");return t.data}async listWebhooks(){let e=await this.makeAuthenticatedRequest("/api/v1/webhooks","GET","");if(!e.success)throw new g(e.error?.message||"Failed to list webhooks");return e.data||[]}async updateWebhook(e,t){let r=await this.makeAuthenticatedRequest(`/api/v1/webhooks/${e}`,"PATCH",JSON.stringify(t));if(!r.success||!r.data)throw new g(r.error?.message||"Failed to update webhook");return r.data}async healthDetailed(){return (await fetch(`${this.baseUrl}/api/v1/health/detailed`,{headers:{Accept:"application/json"}})).json()}async generateBatch(e,t,r={}){let a=await this.makeAuthenticatedRequest("/api/v1/generate/batch","POST",JSON.stringify({chainId:e,items:t,continueOnError:r.continueOnError??true}));if(!a.success||!a.data)throw new y(a.error?.message||"Batch generation failed",[]);return a.data}async makeAuthenticatedRequest(e,t,r,a){let s=Date.now(),c=await S(this.secretKey,t,e,r,s),d=await this.fetchWithTimeout(`${this.baseUrl}${e}`,{method:t,headers:{"Content-Type":"application/json",Accept:"application/json","x-aidirector-signature":c,"x-aidirector-timestamp":s.toString(),"x-aidirector-key":this.keyPrefix},body:r},a??this.timeout),o=await d.json();if(!d.ok)throw this.parseError(d.status,o);return o}async fetchWithTimeout(e,t,r=this.timeout){let a=new AbortController,s=setTimeout(()=>a.abort(),r);try{return await fetch(e,{...t,signal:a.signal})}catch(c){throw c instanceof Error&&c.name==="AbortError"?new R(r):c instanceof Error?new w(c.message,c):new w(String(c))}finally{clearTimeout(s);}}parseError(e,t){let r=t?.error?.message||`HTTP ${e}`,a=t?.error?.code||"UNKNOWN";switch(e){case 401:return new A(r,e);case 429:let s=t?.retryAfterMs||6e4;return new E(r,s);case 500:case 502:case 503:case 504:return new g(r,e);default:return a==="CHAIN_FAILED"?new y(r,t?.error?.attemptedModels||[]):new u(r,a,{statusCode:e,retryable:e>=500})}}isRetryable(e){if(e instanceof u)return e.retryable;if(e instanceof Error){let t=e.message.toLowerCase();return t.includes("network")||t.includes("timeout")||e.name==="AbortError"}return false}sleep(e){return new Promise(t=>setTimeout(t,e))}log(e,t){let r="[AIDirector]";t?console.log(r,e,t):console.log(r,e);}};export{M as AIDirector,u as AIDirectorError,A as AuthenticationError,y as ChainExecutionError,k as ConfigurationError,O as FileProcessingError,D as InvalidSchemaError,w as NetworkError,P as QuotaExceededError,E as RateLimitError,g as ServerError,R as TimeoutError,C as ValidationError,v as WorkerError,S as generateSignature,I as getKeyPrefix,_ as isAIDirectorError,F as isRetryableError,U as isValidSecretKey};
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.2.1",
4
- "description": "Official TypeScript SDK for AI Director - Intelligent AI API Gateway with automatic failover, caching, and JSON extraction",
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": "AI Director",
55
- "url": "https://aidirector.dev"
54
+ "name": "Hydra",
55
+ "url": "https://hydrai.dev"
56
56
  },
57
57
  "license": "MIT",
58
- "homepage": "https://aidirector.dev",
58
+ "homepage": "https://hydrai.dev",
59
59
  "bugs": {
60
- "url": "https://github.com/aidirector/client/issues"
60
+ "url": "https://github.com/hydrai/client/issues"
61
61
  },
62
62
  "repository": {
63
63
  "type": "git",
64
- "url": "git+https://github.com/aidirector/client.git"
64
+ "url": "git+https://github.com/hydrai/client.git"
65
65
  },
66
66
  "publishConfig": {
67
67
  "access": "public",
@@ -84,4 +84,4 @@
84
84
  "engines": {
85
85
  "node": ">=18.0.0"
86
86
  }
87
- }
87
+ }