@node-llm/core 1.7.0 → 1.9.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/README.md +9 -8
- package/dist/aliases.d.ts +216 -0
- package/dist/aliases.d.ts.map +1 -1
- package/dist/aliases.js +241 -25
- package/dist/chat/Chat.d.ts +8 -4
- package/dist/chat/Chat.d.ts.map +1 -1
- package/dist/chat/Chat.js +33 -5
- package/dist/chat/ChatResponse.d.ts +28 -3
- package/dist/chat/ChatResponse.d.ts.map +1 -1
- package/dist/chat/ChatResponse.js +79 -6
- package/dist/chat/ChatStream.d.ts.map +1 -1
- package/dist/chat/ChatStream.js +13 -1
- package/dist/chat/Content.d.ts +7 -0
- package/dist/chat/Content.d.ts.map +1 -1
- package/dist/config.d.ts +28 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +58 -6
- package/dist/constants.d.ts +6 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +6 -0
- package/dist/errors/index.d.ts +20 -2
- package/dist/errors/index.d.ts.map +1 -1
- package/dist/errors/index.js +31 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/llm.d.ts.map +1 -1
- package/dist/llm.js +3 -2
- package/dist/models/ModelRegistry.d.ts.map +1 -1
- package/dist/models/ModelRegistry.js +4 -2
- package/dist/models/PricingRegistry.js +3 -3
- package/dist/models/models.json +16696 -0
- package/dist/providers/Provider.d.ts +5 -0
- package/dist/providers/Provider.d.ts.map +1 -1
- package/dist/providers/anthropic/AnthropicProvider.d.ts.map +1 -1
- package/dist/providers/anthropic/AnthropicProvider.js +2 -1
- package/dist/providers/anthropic/Capabilities.d.ts +1 -0
- package/dist/providers/anthropic/Capabilities.d.ts.map +1 -1
- package/dist/providers/anthropic/Capabilities.js +8 -5
- package/dist/providers/anthropic/Chat.d.ts.map +1 -1
- package/dist/providers/anthropic/Chat.js +2 -1
- package/dist/providers/anthropic/Errors.d.ts.map +1 -1
- package/dist/providers/anthropic/Errors.js +15 -1
- package/dist/providers/anthropic/Streaming.d.ts.map +1 -1
- package/dist/providers/anthropic/Streaming.js +19 -3
- package/dist/providers/bedrock/BedrockProvider.d.ts +53 -0
- package/dist/providers/bedrock/BedrockProvider.d.ts.map +1 -0
- package/dist/providers/bedrock/BedrockProvider.js +107 -0
- package/dist/providers/bedrock/Capabilities.d.ts +50 -0
- package/dist/providers/bedrock/Capabilities.d.ts.map +1 -0
- package/dist/providers/bedrock/Capabilities.js +233 -0
- package/dist/providers/bedrock/Chat.d.ts +26 -0
- package/dist/providers/bedrock/Chat.d.ts.map +1 -0
- package/dist/providers/bedrock/Chat.js +152 -0
- package/dist/providers/bedrock/Embeddings.d.ts +22 -0
- package/dist/providers/bedrock/Embeddings.d.ts.map +1 -0
- package/dist/providers/bedrock/Embeddings.js +100 -0
- package/dist/providers/bedrock/Errors.d.ts +2 -0
- package/dist/providers/bedrock/Errors.d.ts.map +1 -0
- package/dist/providers/bedrock/Errors.js +51 -0
- package/dist/providers/bedrock/Image.d.ts +33 -0
- package/dist/providers/bedrock/Image.d.ts.map +1 -0
- package/dist/providers/bedrock/Image.js +154 -0
- package/dist/providers/bedrock/Models.d.ts +34 -0
- package/dist/providers/bedrock/Models.d.ts.map +1 -0
- package/dist/providers/bedrock/Models.js +131 -0
- package/dist/providers/bedrock/Moderation.d.ts +23 -0
- package/dist/providers/bedrock/Moderation.d.ts.map +1 -0
- package/dist/providers/bedrock/Moderation.js +138 -0
- package/dist/providers/bedrock/Streaming.d.ts +21 -0
- package/dist/providers/bedrock/Streaming.d.ts.map +1 -0
- package/dist/providers/bedrock/Streaming.js +239 -0
- package/dist/providers/bedrock/config.d.ts +57 -0
- package/dist/providers/bedrock/config.d.ts.map +1 -0
- package/dist/providers/bedrock/config.js +33 -0
- package/dist/providers/bedrock/index.d.ts +8 -0
- package/dist/providers/bedrock/index.d.ts.map +1 -0
- package/dist/providers/bedrock/index.js +30 -0
- package/dist/providers/bedrock/mapper.d.ts +37 -0
- package/dist/providers/bedrock/mapper.d.ts.map +1 -0
- package/dist/providers/bedrock/mapper.js +204 -0
- package/dist/providers/bedrock/types.d.ts +179 -0
- package/dist/providers/bedrock/types.d.ts.map +1 -0
- package/dist/providers/bedrock/types.js +7 -0
- package/dist/providers/deepseek/Capabilities.d.ts +3 -2
- package/dist/providers/deepseek/Capabilities.d.ts.map +1 -1
- package/dist/providers/deepseek/Capabilities.js +19 -5
- package/dist/providers/deepseek/Chat.d.ts.map +1 -1
- package/dist/providers/deepseek/Chat.js +2 -2
- package/dist/providers/deepseek/DeepSeekProvider.d.ts.map +1 -1
- package/dist/providers/deepseek/DeepSeekProvider.js +2 -1
- package/dist/providers/deepseek/Errors.d.ts +2 -0
- package/dist/providers/deepseek/Errors.d.ts.map +1 -0
- package/dist/providers/deepseek/Errors.js +45 -0
- package/dist/providers/deepseek/Streaming.d.ts.map +1 -1
- package/dist/providers/deepseek/Streaming.js +13 -2
- package/dist/providers/gemini/Capabilities.d.ts +1 -0
- package/dist/providers/gemini/Capabilities.d.ts.map +1 -1
- package/dist/providers/gemini/Capabilities.js +9 -6
- package/dist/providers/gemini/Chat.d.ts.map +1 -1
- package/dist/providers/gemini/Chat.js +6 -23
- package/dist/providers/gemini/Errors.d.ts.map +1 -1
- package/dist/providers/gemini/Errors.js +13 -1
- package/dist/providers/gemini/GeminiProvider.d.ts.map +1 -1
- package/dist/providers/gemini/GeminiProvider.js +2 -1
- package/dist/providers/ollama/Capabilities.d.ts.map +1 -1
- package/dist/providers/ollama/Capabilities.js +4 -1
- package/dist/providers/ollama/OllamaProvider.d.ts.map +1 -1
- package/dist/providers/ollama/OllamaProvider.js +2 -2
- package/dist/providers/openai/Capabilities.d.ts +1 -0
- package/dist/providers/openai/Capabilities.d.ts.map +1 -1
- package/dist/providers/openai/Capabilities.js +14 -11
- package/dist/providers/openai/Errors.d.ts.map +1 -1
- package/dist/providers/openai/Errors.js +31 -5
- package/dist/providers/openai/OpenAIProvider.d.ts.map +1 -1
- package/dist/providers/openai/OpenAIProvider.js +2 -1
- package/dist/providers/openai/Streaming.d.ts.map +1 -1
- package/dist/providers/openai/Streaming.js +10 -0
- package/dist/providers/openrouter/OpenRouterProvider.d.ts.map +1 -1
- package/dist/providers/openrouter/OpenRouterProvider.js +2 -1
- package/dist/providers/registry.d.ts +5 -1
- package/dist/providers/registry.d.ts.map +1 -1
- package/dist/providers/registry.js +12 -3
- package/dist/utils/AwsSigV4.d.ts +51 -0
- package/dist/utils/AwsSigV4.d.ts.map +1 -0
- package/dist/utils/AwsSigV4.js +209 -0
- package/dist/utils/json.d.ts +6 -0
- package/dist/utils/json.d.ts.map +1 -0
- package/dist/utils/json.js +43 -0
- package/package.json +1 -1
- package/dist/models/models.d.ts +0 -327
- package/dist/models/models.d.ts.map +0 -1
- package/dist/models/models.js +0 -11138
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OllamaProvider.d.ts","sourceRoot":"","sources":["../../../src/providers/ollama/OllamaProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"OllamaProvider.d.ts","sourceRoot":"","sources":["../../../src/providers/ollama/OllamaProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAO7D,MAAM,WAAW,qBAAqB;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,cAAe,SAAQ,cAAc;gBACpC,OAAO,GAAE,qBAA0B;IA2B/C,SAAS,CAAC,YAAY,IAAI,MAAM;IAIhB,YAAY,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM;CAGxD"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { OpenAIProvider } from "../openai/OpenAIProvider.js";
|
|
2
|
-
import {
|
|
2
|
+
import { DEFAULT_OLLAMA_BASE_URL } from "../../constants.js";
|
|
3
3
|
import { OllamaModels } from "./Models.js";
|
|
4
4
|
import { OllamaEmbedding } from "./Embedding.js";
|
|
5
5
|
import { OllamaCapabilities } from "./Capabilities.js";
|
|
@@ -7,7 +7,7 @@ export class OllamaProvider extends OpenAIProvider {
|
|
|
7
7
|
constructor(options = {}) {
|
|
8
8
|
super({
|
|
9
9
|
apiKey: "ollama",
|
|
10
|
-
baseUrl: options.baseUrl ||
|
|
10
|
+
baseUrl: options.baseUrl || DEFAULT_OLLAMA_BASE_URL
|
|
11
11
|
});
|
|
12
12
|
// Override handlers with Ollama-specific ones
|
|
13
13
|
this.modelsHandler = new OllamaModels(this.baseUrl, this.options.apiKey);
|
|
@@ -22,5 +22,6 @@ export declare class Capabilities {
|
|
|
22
22
|
static normalizeTemperature(temperature: number | undefined, modelId: string): number | undefined | null;
|
|
23
23
|
static formatDisplayName(modelId: string): string;
|
|
24
24
|
static getPricing(modelId: string): ModelPricing | undefined;
|
|
25
|
+
private static findModel;
|
|
25
26
|
}
|
|
26
27
|
//# sourceMappingURL=Capabilities.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Capabilities.d.ts","sourceRoot":"","sources":["../../../src/providers/openai/Capabilities.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAErD,qBAAa,YAAY;IACvB,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAUvD,MAAM,CAAC,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IASzD,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAO/C,MAAM,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAQ9C,MAAM,CAAC,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAOzD,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAIjD,MAAM,CAAC,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAOnD,MAAM,CAAC,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAWxD,MAAM,CAAC,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAOtD,MAAM,CAAC,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAOnD,MAAM,CAAC,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAOlD,MAAM,CAAC,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAItD,MAAM,CAAC,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAIzD,MAAM,CAAC,YAAY,CACjB,OAAO,EAAE,MAAM,GAEb,YAAY,GACZ,OAAO,GACP,YAAY,GACZ,OAAO,GACP,MAAM,GACN,qBAAqB,GACrB,cAAc;IAWlB,MAAM,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG;QAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE;IAiB5E,MAAM,CAAC,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE;IAsBjD,MAAM,CAAC,oBAAoB,CACzB,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,OAAO,EAAE,MAAM,GACd,MAAM,GAAG,SAAS,GAAG,IAAI;IAM5B,MAAM,CAAC,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAOjD,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;
|
|
1
|
+
{"version":3,"file":"Capabilities.d.ts","sourceRoot":"","sources":["../../../src/providers/openai/Capabilities.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAErD,qBAAa,YAAY;IACvB,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAUvD,MAAM,CAAC,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IASzD,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAO/C,MAAM,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAQ9C,MAAM,CAAC,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAOzD,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAIjD,MAAM,CAAC,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAOnD,MAAM,CAAC,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAWxD,MAAM,CAAC,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAOtD,MAAM,CAAC,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAOnD,MAAM,CAAC,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAOlD,MAAM,CAAC,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAItD,MAAM,CAAC,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAIzD,MAAM,CAAC,YAAY,CACjB,OAAO,EAAE,MAAM,GAEb,YAAY,GACZ,OAAO,GACP,YAAY,GACZ,OAAO,GACP,MAAM,GACN,qBAAqB,GACrB,cAAc;IAWlB,MAAM,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG;QAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE;IAiB5E,MAAM,CAAC,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE;IAsBjD,MAAM,CAAC,oBAAoB,CACzB,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,OAAO,EAAE,MAAM,GACd,MAAM,GAAG,SAAS,GAAG,IAAI;IAM5B,MAAM,CAAC,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAOjD,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAI5D,OAAO,CAAC,MAAM,CAAC,SAAS;CAGzB"}
|
|
@@ -24,19 +24,19 @@ export class Capabilities {
|
|
|
24
24
|
return 4_096;
|
|
25
25
|
}
|
|
26
26
|
static supportsVision(modelId) {
|
|
27
|
-
const model =
|
|
27
|
+
const model = this.findModel(modelId);
|
|
28
28
|
if (model?.modalities?.input?.includes("image"))
|
|
29
29
|
return true;
|
|
30
30
|
return /gpt-4(?!-3)|o1/.test(modelId) && !/audio|realtime|voice/.test(modelId);
|
|
31
31
|
}
|
|
32
32
|
static supportsTools(modelId) {
|
|
33
|
-
const model =
|
|
33
|
+
const model = this.findModel(modelId);
|
|
34
34
|
if (model?.capabilities?.includes("function_calling") || model?.capabilities?.includes("tools"))
|
|
35
35
|
return true;
|
|
36
36
|
return !/embedding|moderation|dall-e|tts|whisper/.test(modelId);
|
|
37
37
|
}
|
|
38
38
|
static supportsStructuredOutput(modelId) {
|
|
39
|
-
const model =
|
|
39
|
+
const model = this.findModel(modelId);
|
|
40
40
|
if (model?.capabilities?.includes("structured_output"))
|
|
41
41
|
return true;
|
|
42
42
|
return /gpt-4|o1|o3/.test(modelId);
|
|
@@ -45,32 +45,32 @@ export class Capabilities {
|
|
|
45
45
|
return this.supportsStructuredOutput(modelId);
|
|
46
46
|
}
|
|
47
47
|
static supportsEmbeddings(modelId) {
|
|
48
|
-
const model =
|
|
48
|
+
const model = this.findModel(modelId);
|
|
49
49
|
if (model?.modalities?.output?.includes("embeddings"))
|
|
50
50
|
return true;
|
|
51
51
|
return /embedding/.test(modelId);
|
|
52
52
|
}
|
|
53
53
|
static supportsImageGeneration(modelId) {
|
|
54
|
-
const model =
|
|
54
|
+
const model = this.findModel(modelId);
|
|
55
55
|
if (model?.capabilities?.includes("image_generation") ||
|
|
56
56
|
model?.modalities?.output?.includes("image"))
|
|
57
57
|
return true;
|
|
58
58
|
return /dall-e|image/.test(modelId);
|
|
59
59
|
}
|
|
60
60
|
static supportsTranscription(modelId) {
|
|
61
|
-
const model =
|
|
61
|
+
const model = this.findModel(modelId);
|
|
62
62
|
if (model?.modalities?.input?.includes("audio"))
|
|
63
63
|
return true;
|
|
64
64
|
return /whisper|audio|transcribe/.test(modelId);
|
|
65
65
|
}
|
|
66
66
|
static supportsModeration(modelId) {
|
|
67
|
-
const model =
|
|
67
|
+
const model = this.findModel(modelId);
|
|
68
68
|
if (model?.modalities?.output?.includes("moderation"))
|
|
69
69
|
return true;
|
|
70
70
|
return /moderation/.test(modelId);
|
|
71
71
|
}
|
|
72
72
|
static supportsReasoning(modelId) {
|
|
73
|
-
const model =
|
|
73
|
+
const model = this.findModel(modelId);
|
|
74
74
|
if (model?.capabilities?.includes("reasoning"))
|
|
75
75
|
return true;
|
|
76
76
|
return /o\d|gpt-5/.test(modelId);
|
|
@@ -101,7 +101,7 @@ export class Capabilities {
|
|
|
101
101
|
static getModalities(modelId) {
|
|
102
102
|
const input = ["text"];
|
|
103
103
|
const output = ["text"];
|
|
104
|
-
const model =
|
|
104
|
+
const model = this.findModel(modelId);
|
|
105
105
|
if (model?.modalities)
|
|
106
106
|
return model.modalities;
|
|
107
107
|
if (this.supportsVision(modelId))
|
|
@@ -118,7 +118,7 @@ export class Capabilities {
|
|
|
118
118
|
}
|
|
119
119
|
static getCapabilities(modelId) {
|
|
120
120
|
const caps = ["streaming"];
|
|
121
|
-
const model =
|
|
121
|
+
const model = this.findModel(modelId);
|
|
122
122
|
if (model) {
|
|
123
123
|
model.capabilities.forEach((c) => {
|
|
124
124
|
if (!caps.includes(c))
|
|
@@ -148,7 +148,7 @@ export class Capabilities {
|
|
|
148
148
|
return temperature;
|
|
149
149
|
}
|
|
150
150
|
static formatDisplayName(modelId) {
|
|
151
|
-
const model =
|
|
151
|
+
const model = this.findModel(modelId);
|
|
152
152
|
if (model?.name && model.name !== modelId)
|
|
153
153
|
return model.name;
|
|
154
154
|
return modelId.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
@@ -156,4 +156,7 @@ export class Capabilities {
|
|
|
156
156
|
static getPricing(modelId) {
|
|
157
157
|
return PricingRegistry.getPricing(modelId, "openai");
|
|
158
158
|
}
|
|
159
|
+
static findModel(modelId) {
|
|
160
|
+
return ModelRegistry.find(modelId, "openai");
|
|
161
|
+
}
|
|
159
162
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Errors.d.ts","sourceRoot":"","sources":["../../../src/providers/openai/Errors.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Errors.d.ts","sourceRoot":"","sources":["../../../src/providers/openai/Errors.ts"],"names":[],"mappings":"AAaA,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAmE1F"}
|
|
@@ -1,14 +1,26 @@
|
|
|
1
|
-
import { BadRequestError, AuthenticationError, RateLimitError, ServerError, ServiceUnavailableError, APIError } from "../../errors/index.js";
|
|
1
|
+
import { BadRequestError, ContextWindowExceededError, InsufficientQuotaError, InvalidModelError, NotFoundError, AuthenticationError, RateLimitError, ServerError, ServiceUnavailableError, APIError } from "../../errors/index.js";
|
|
2
2
|
export async function handleOpenAIError(response, model) {
|
|
3
3
|
const status = response.status;
|
|
4
4
|
let body;
|
|
5
5
|
let message = `OpenAI error (${status})`;
|
|
6
|
+
let code;
|
|
6
7
|
try {
|
|
7
8
|
body = await response.json();
|
|
8
|
-
if (body && typeof body === "object"
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
if (body && typeof body === "object") {
|
|
10
|
+
if ("error" in body) {
|
|
11
|
+
const err = body.error;
|
|
12
|
+
if (typeof err === "string") {
|
|
13
|
+
message = err;
|
|
14
|
+
}
|
|
15
|
+
else if (err && typeof err === "object") {
|
|
16
|
+
if (err.message)
|
|
17
|
+
message = err.message;
|
|
18
|
+
if (err.code)
|
|
19
|
+
code = err.code;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
else if ("message" in body) {
|
|
23
|
+
message = body.message;
|
|
12
24
|
}
|
|
13
25
|
}
|
|
14
26
|
}
|
|
@@ -19,12 +31,26 @@ export async function handleOpenAIError(response, model) {
|
|
|
19
31
|
}
|
|
20
32
|
const provider = "openai";
|
|
21
33
|
if (status === 400) {
|
|
34
|
+
if (code === "context_length_exceeded" ||
|
|
35
|
+
message.includes("context length") ||
|
|
36
|
+
message.includes("too many tokens")) {
|
|
37
|
+
throw new ContextWindowExceededError(message, body, provider, model);
|
|
38
|
+
}
|
|
22
39
|
throw new BadRequestError(message, body, provider, model);
|
|
23
40
|
}
|
|
24
41
|
if (status === 401 || status === 403) {
|
|
25
42
|
throw new AuthenticationError(message, status, body, provider);
|
|
26
43
|
}
|
|
44
|
+
if (status === 404) {
|
|
45
|
+
if (code === "model_not_found") {
|
|
46
|
+
throw new InvalidModelError(message, body, provider, model);
|
|
47
|
+
}
|
|
48
|
+
throw new NotFoundError(message, status, body, provider, model);
|
|
49
|
+
}
|
|
27
50
|
if (status === 429) {
|
|
51
|
+
if (code === "insufficient_quota") {
|
|
52
|
+
throw new InsufficientQuotaError(message, body, provider, model);
|
|
53
|
+
}
|
|
28
54
|
throw new RateLimitError(message, body, provider, model);
|
|
29
55
|
}
|
|
30
56
|
if (status === 502 || status === 503) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OpenAIProvider.d.ts","sourceRoot":"","sources":["../../../src/providers/openai/OpenAIProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,WAAW,EACX,YAAY,EACZ,SAAS,EACT,SAAS,EACT,YAAY,EACZ,aAAa,EACb,oBAAoB,EACpB,qBAAqB,EACrB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"OpenAIProvider.d.ts","sourceRoot":"","sources":["../../../src/providers/openai/OpenAIProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,WAAW,EACX,YAAY,EACZ,SAAS,EACT,SAAS,EACT,YAAY,EACZ,aAAa,EACb,oBAAoB,EACpB,qBAAqB,EACrB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGlD,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,cAAe,SAAQ,YAAa,YAAW,QAAQ;IAwBtD,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,qBAAqB;IAvB7D,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,WAAW,EAAE,UAAU,CAAC;IAClC,SAAS,CAAC,gBAAgB,EAAE,eAAe,CAAC;IAC5C,SAAS,CAAC,aAAa,EAAE,YAAY,CAAC;IACtC,SAAS,CAAC,YAAY,EAAE,WAAW,CAAC;IACpC,SAAS,CAAC,oBAAoB,EAAE,mBAAmB,CAAC;IACpD,SAAS,CAAC,iBAAiB,EAAE,gBAAgB,CAAC;IAC9C,SAAS,CAAC,gBAAgB,EAAE,eAAe,CAAC;IAErC,YAAY;gCACO,MAAM;+BACP,MAAM;0CACK,MAAM;oCACZ,MAAM;yCACD,MAAM;uCACR,MAAM;oCACT,MAAM;mCACP,MAAM;yCACA,MAAM;kCAEb,MAAM;MAChC;gBAE6B,OAAO,EAAE,qBAAqB;IAYtD,OAAO,IAAI,MAAM;IAIjB,OAAO,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAOxC,SAAS,CAAC,YAAY,IAAI,MAAM;IAIhB,YAAY,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM;IAIjD,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IAIhD,MAAM,CAAC,OAAO,EAAE,WAAW,GAAG,cAAc,CAAC,SAAS,CAAC;IAIxD,UAAU,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IAIlC,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC;IAIpD,UAAU,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAIzE,QAAQ,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAIjE,KAAK,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,iBAAiB,CAAC;CAGnE"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BaseProvider } from "../BaseProvider.js";
|
|
2
|
+
import { DEFAULT_OPENAI_BASE_URL } from "../../constants.js";
|
|
2
3
|
import { Capabilities } from "./Capabilities.js";
|
|
3
4
|
import { OpenAIChat } from "./Chat.js";
|
|
4
5
|
import { OpenAIStreaming } from "./Streaming.js";
|
|
@@ -32,7 +33,7 @@ export class OpenAIProvider extends BaseProvider {
|
|
|
32
33
|
constructor(options) {
|
|
33
34
|
super();
|
|
34
35
|
this.options = options;
|
|
35
|
-
this.baseUrl = options.baseUrl ??
|
|
36
|
+
this.baseUrl = options.baseUrl ?? DEFAULT_OPENAI_BASE_URL;
|
|
36
37
|
this.chatHandler = new OpenAIChat(this, options.apiKey);
|
|
37
38
|
this.streamingHandler = new OpenAIStreaming(this, options.apiKey);
|
|
38
39
|
this.modelsHandler = new OpenAIModels(this.baseUrl, options.apiKey);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Streaming.d.ts","sourceRoot":"","sources":["../../../src/providers/openai/Streaming.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAQxD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAGrD,qBAAa,eAAe;IAIxB,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAGd,aAAa,EAAE,cAAc,GAAG,MAAM,EACtC,MAAM,EAAE,MAAM;IAK1B,OAAO,CAAC,OAAO,EAAE,WAAW,EAAE,UAAU,CAAC,EAAE,eAAe,GAAG,cAAc,CAAC,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"Streaming.d.ts","sourceRoot":"","sources":["../../../src/providers/openai/Streaming.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAQxD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAGrD,qBAAa,eAAe;IAIxB,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAGd,aAAa,EAAE,cAAc,GAAG,MAAM,EACtC,MAAM,EAAE,MAAM;IAK1B,OAAO,CAAC,OAAO,EAAE,WAAW,EAAE,UAAU,CAAC,EAAE,eAAe,GAAG,cAAc,CAAC,SAAS,CAAC;CAgN9F"}
|
|
@@ -50,6 +50,7 @@ export class OpenAIStreaming {
|
|
|
50
50
|
if (request.thinking?.effort && request.thinking.effort !== "none") {
|
|
51
51
|
body.reasoning_effort = request.thinking.effort;
|
|
52
52
|
}
|
|
53
|
+
body.stream_options = { include_usage: true };
|
|
53
54
|
let done = false;
|
|
54
55
|
// Track tool calls being built across chunks
|
|
55
56
|
const toolCallsMap = new Map();
|
|
@@ -154,6 +155,15 @@ export class OpenAIStreaming {
|
|
|
154
155
|
}
|
|
155
156
|
}
|
|
156
157
|
}
|
|
158
|
+
// Handle usage (usually in the special chunk when stream_options.include_usage: true)
|
|
159
|
+
if (json.usage) {
|
|
160
|
+
const usage = {
|
|
161
|
+
input_tokens: json.usage.prompt_tokens,
|
|
162
|
+
output_tokens: json.usage.completion_tokens,
|
|
163
|
+
total_tokens: json.usage.total_tokens
|
|
164
|
+
};
|
|
165
|
+
yield { content: "", usage };
|
|
166
|
+
}
|
|
157
167
|
}
|
|
158
168
|
catch (e) {
|
|
159
169
|
// Re-throw APIError
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OpenRouterProvider.d.ts","sourceRoot":"","sources":["../../../src/providers/openrouter/OpenRouterProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"OpenRouterProvider.d.ts","sourceRoot":"","sources":["../../../src/providers/openrouter/OpenRouterProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAI7D,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,kBAAmB,SAAQ,cAAc;IAC7C,YAAY;gCACO,MAAM;+BACP,MAAM;0CACK,MAAM;oCAEZ,MAAM;yCACD,MAAM;uCAER,MAAM;oCACT,MAAM;mCACP,MAAM;wCACD,MAAM;kCACZ,MAAM;MAChC;gBAEU,OAAO,EAAE,yBAAyB;IAO9C,SAAS,CAAC,YAAY,IAAI,MAAM;CAGjC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { OpenAIProvider } from "../openai/OpenAIProvider.js";
|
|
2
|
+
import { DEFAULT_OPENROUTER_BASE_URL } from "../../constants.js";
|
|
2
3
|
import { OpenRouterCapabilities } from "./Capabilities.js";
|
|
3
4
|
export class OpenRouterProvider extends OpenAIProvider {
|
|
4
5
|
capabilities = {
|
|
@@ -16,7 +17,7 @@ export class OpenRouterProvider extends OpenAIProvider {
|
|
|
16
17
|
constructor(options) {
|
|
17
18
|
super({
|
|
18
19
|
apiKey: options.apiKey,
|
|
19
|
-
baseUrl: options.baseUrl ||
|
|
20
|
+
baseUrl: options.baseUrl || DEFAULT_OPENROUTER_BASE_URL
|
|
20
21
|
});
|
|
21
22
|
}
|
|
22
23
|
providerName() {
|
|
@@ -5,10 +5,14 @@ import { registerGeminiProvider } from "./gemini/index.js";
|
|
|
5
5
|
import { registerDeepSeekProvider } from "./deepseek/index.js";
|
|
6
6
|
import { registerOllamaProvider } from "./ollama/index.js";
|
|
7
7
|
import { registerOpenRouterProvider } from "./openrouter/index.js";
|
|
8
|
+
import { registerBedrockProvider } from "./bedrock/index.js";
|
|
8
9
|
import { NodeLLMConfig } from "../config.js";
|
|
9
10
|
type ProviderFactory = (config?: NodeLLMConfig) => Provider;
|
|
11
|
+
export type ProviderInterceptor = (provider: Provider) => Provider;
|
|
10
12
|
declare class ProviderRegistry {
|
|
11
13
|
private providers;
|
|
14
|
+
private globalInterceptor?;
|
|
15
|
+
setInterceptor(interceptor: ProviderInterceptor | undefined): void;
|
|
12
16
|
/**
|
|
13
17
|
* Register a provider factory
|
|
14
18
|
*/
|
|
@@ -42,5 +46,5 @@ declare class ProviderRegistry {
|
|
|
42
46
|
* ```
|
|
43
47
|
*/
|
|
44
48
|
export declare const providerRegistry: ProviderRegistry;
|
|
45
|
-
export { registerOpenAIProvider as ensureOpenAIRegistered, registerOpenAIProvider, registerAnthropicProvider, registerGeminiProvider, registerDeepSeekProvider, registerOllamaProvider, registerOpenRouterProvider };
|
|
49
|
+
export { registerOpenAIProvider as ensureOpenAIRegistered, registerOpenAIProvider, registerAnthropicProvider, registerGeminiProvider, registerDeepSeekProvider, registerOllamaProvider, registerOpenRouterProvider, registerBedrockProvider };
|
|
46
50
|
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/providers/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/providers/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;AACnE,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAE7D,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,KAAK,eAAe,GAAG,CAAC,MAAM,CAAC,EAAE,aAAa,KAAK,QAAQ,CAAC;AAC5D,MAAM,MAAM,mBAAmB,GAAG,CAAC,QAAQ,EAAE,QAAQ,KAAK,QAAQ,CAAC;AAEnE,cAAM,gBAAgB;IACpB,OAAO,CAAC,SAAS,CAAsC;IACvD,OAAO,CAAC,iBAAiB,CAAC,CAAsB;IAEzC,cAAc,CAAC,WAAW,EAAE,mBAAmB,GAAG,SAAS;IAIlE;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe;IAO/C;;OAEG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,QAAQ;IAgBvD;;OAEG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI1B;;OAEG;IACH,IAAI,IAAI,MAAM,EAAE;CAGjB;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,gBAAgB,kBAAyB,CAAC;AAGvD,OAAO,EACL,sBAAsB,IAAI,sBAAsB,EAChD,sBAAsB,EACtB,yBAAyB,EACzB,sBAAsB,EACtB,wBAAwB,EACxB,sBAAsB,EACtB,0BAA0B,EAC1B,uBAAuB,EACxB,CAAC"}
|
|
@@ -4,8 +4,13 @@ import { registerGeminiProvider } from "./gemini/index.js";
|
|
|
4
4
|
import { registerDeepSeekProvider } from "./deepseek/index.js";
|
|
5
5
|
import { registerOllamaProvider } from "./ollama/index.js";
|
|
6
6
|
import { registerOpenRouterProvider } from "./openrouter/index.js";
|
|
7
|
+
import { registerBedrockProvider } from "./bedrock/index.js";
|
|
7
8
|
class ProviderRegistry {
|
|
8
9
|
providers = new Map();
|
|
10
|
+
globalInterceptor;
|
|
11
|
+
setInterceptor(interceptor) {
|
|
12
|
+
this.globalInterceptor = interceptor;
|
|
13
|
+
}
|
|
9
14
|
/**
|
|
10
15
|
* Register a provider factory
|
|
11
16
|
*/
|
|
@@ -21,9 +26,13 @@ class ProviderRegistry {
|
|
|
21
26
|
resolve(name, config) {
|
|
22
27
|
const factory = this.providers.get(name);
|
|
23
28
|
if (!factory) {
|
|
24
|
-
throw new Error(`
|
|
29
|
+
throw new Error(`Provider ${name} not registered`);
|
|
30
|
+
}
|
|
31
|
+
let provider = factory(config);
|
|
32
|
+
if (this.globalInterceptor) {
|
|
33
|
+
provider = this.globalInterceptor(provider);
|
|
25
34
|
}
|
|
26
|
-
return
|
|
35
|
+
return provider;
|
|
27
36
|
}
|
|
28
37
|
/**
|
|
29
38
|
* Check if a provider is registered
|
|
@@ -55,4 +64,4 @@ class ProviderRegistry {
|
|
|
55
64
|
*/
|
|
56
65
|
export const providerRegistry = new ProviderRegistry();
|
|
57
66
|
// Exported registration functions (delegates to provider-specific index files)
|
|
58
|
-
export { registerOpenAIProvider as ensureOpenAIRegistered, registerOpenAIProvider, registerAnthropicProvider, registerGeminiProvider, registerDeepSeekProvider, registerOllamaProvider, registerOpenRouterProvider };
|
|
67
|
+
export { registerOpenAIProvider as ensureOpenAIRegistered, registerOpenAIProvider, registerAnthropicProvider, registerGeminiProvider, registerDeepSeekProvider, registerOllamaProvider, registerOpenRouterProvider, registerBedrockProvider };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AWS Signature Version 4 (SigV4) Request Signing Utility
|
|
3
|
+
*
|
|
4
|
+
* This is a minimal, focused implementation for signing AWS API requests.
|
|
5
|
+
* It uses only Node.js native `crypto` module — no AWS SDK dependencies.
|
|
6
|
+
*
|
|
7
|
+
* Reference: https://docs.aws.amazon.com/IAM/latest/UserGuide/create-signed-request.html
|
|
8
|
+
*/
|
|
9
|
+
export interface AwsCredentials {
|
|
10
|
+
accessKeyId: string;
|
|
11
|
+
secretAccessKey: string;
|
|
12
|
+
sessionToken?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface SignRequestOptions {
|
|
15
|
+
method: "POST" | "GET";
|
|
16
|
+
url: string;
|
|
17
|
+
body: string;
|
|
18
|
+
credentials: AwsCredentials;
|
|
19
|
+
region: string;
|
|
20
|
+
service: string;
|
|
21
|
+
timestamp?: Date;
|
|
22
|
+
}
|
|
23
|
+
export interface SignedHeaders {
|
|
24
|
+
host: string;
|
|
25
|
+
"x-amz-date": string;
|
|
26
|
+
"x-amz-content-sha256": string;
|
|
27
|
+
authorization: string;
|
|
28
|
+
"x-amz-security-token"?: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Sign an AWS API request using SigV4.
|
|
32
|
+
*
|
|
33
|
+
* @param options - The request details and credentials
|
|
34
|
+
* @returns Headers to include in the HTTP request
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```ts
|
|
38
|
+
* const headers = signRequest({
|
|
39
|
+
* method: "POST",
|
|
40
|
+
* url: "https://bedrock-runtime.us-east-1.amazonaws.com/model/.../converse",
|
|
41
|
+
* body: JSON.stringify({ messages: [...] }),
|
|
42
|
+
* credentials: { accessKeyId: "...", secretAccessKey: "..." },
|
|
43
|
+
* region: "us-east-1",
|
|
44
|
+
* service: "bedrock"
|
|
45
|
+
* });
|
|
46
|
+
*
|
|
47
|
+
* fetch(url, { headers: { ...headers, "Content-Type": "application/json" }, body });
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export declare function signRequest(options: SignRequestOptions): SignedHeaders;
|
|
51
|
+
//# sourceMappingURL=AwsSigV4.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AwsSigV4.d.ts","sourceRoot":"","sources":["../../src/utils/AwsSigV4.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAQH,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,GAAG,KAAK,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,cAAc,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,IAAI,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC;AA8KD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,aAAa,CAwEtE"}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AWS Signature Version 4 (SigV4) Request Signing Utility
|
|
3
|
+
*
|
|
4
|
+
* This is a minimal, focused implementation for signing AWS API requests.
|
|
5
|
+
* It uses only Node.js native `crypto` module — no AWS SDK dependencies.
|
|
6
|
+
*
|
|
7
|
+
* Reference: https://docs.aws.amazon.com/IAM/latest/UserGuide/create-signed-request.html
|
|
8
|
+
*/
|
|
9
|
+
import { createHmac, createHash } from "node:crypto";
|
|
10
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
11
|
+
// Constants
|
|
12
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
13
|
+
const ALGORITHM = "AWS4-HMAC-SHA256";
|
|
14
|
+
const AWS4_REQUEST = "aws4_request";
|
|
15
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
16
|
+
// Helper Functions
|
|
17
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
18
|
+
/**
|
|
19
|
+
* SHA256 hash of a string, returned as hex.
|
|
20
|
+
*/
|
|
21
|
+
function sha256Hex(data) {
|
|
22
|
+
return createHash("sha256").update(data, "utf8").digest("hex");
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* HMAC-SHA256, returns raw bytes (Buffer).
|
|
26
|
+
*/
|
|
27
|
+
function hmacSha256(key, data) {
|
|
28
|
+
return createHmac("sha256", key).update(data, "utf8").digest();
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* HMAC-SHA256, returns hex string.
|
|
32
|
+
*/
|
|
33
|
+
function hmacSha256Hex(key, data) {
|
|
34
|
+
return createHmac("sha256", key).update(data, "utf8").digest("hex");
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Format a Date as AWS timestamp: YYYYMMDD'T'HHMMSS'Z'
|
|
38
|
+
*/
|
|
39
|
+
function formatAmzDate(date) {
|
|
40
|
+
return date.toISOString().replace(/[:-]|\.\d{3}/g, "");
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Extract just the date portion: YYYYMMDD
|
|
44
|
+
*/
|
|
45
|
+
function formatDateOnly(amzDate) {
|
|
46
|
+
return amzDate.substring(0, 8);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Parse host from URL.
|
|
50
|
+
*/
|
|
51
|
+
function extractHost(url) {
|
|
52
|
+
const parsed = new URL(url);
|
|
53
|
+
return parsed.host;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Parse path from URL (without query string).
|
|
57
|
+
*/
|
|
58
|
+
function extractPath(url) {
|
|
59
|
+
const parsed = new URL(url);
|
|
60
|
+
const path = parsed.pathname || "/";
|
|
61
|
+
return path
|
|
62
|
+
.split("/")
|
|
63
|
+
.map((segment) => encodeURIComponent(segment))
|
|
64
|
+
.join("/")
|
|
65
|
+
.replace(/%20/g, "%20") // encodeURIComponent already does %20
|
|
66
|
+
.replace(/'/g, "%27") // AWS expects single quotes encoded
|
|
67
|
+
.replace(/\(/g, "%28")
|
|
68
|
+
.replace(/\)/g, "%29")
|
|
69
|
+
.replace(/\*/g, "%2A");
|
|
70
|
+
}
|
|
71
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
72
|
+
// Core Signing Logic
|
|
73
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
74
|
+
/**
|
|
75
|
+
* Step 4: Derive the signing key.
|
|
76
|
+
*
|
|
77
|
+
* kSecret = "AWS4" + secretAccessKey
|
|
78
|
+
* kDate = HMAC-SHA256(kSecret, dateStamp)
|
|
79
|
+
* kRegion = HMAC-SHA256(kDate, region)
|
|
80
|
+
* kService = HMAC-SHA256(kRegion, service)
|
|
81
|
+
* kSigning = HMAC-SHA256(kService, "aws4_request")
|
|
82
|
+
*/
|
|
83
|
+
function deriveSigningKey(secretAccessKey, dateStamp, region, service) {
|
|
84
|
+
const kSecret = "AWS4" + secretAccessKey;
|
|
85
|
+
const kDate = hmacSha256(kSecret, dateStamp);
|
|
86
|
+
const kRegion = hmacSha256(kDate, region);
|
|
87
|
+
const kService = hmacSha256(kRegion, service);
|
|
88
|
+
const kSigning = hmacSha256(kService, AWS4_REQUEST);
|
|
89
|
+
return kSigning;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Step 2: Build the canonical request string.
|
|
93
|
+
*
|
|
94
|
+
* Format:
|
|
95
|
+
* HTTPMethod
|
|
96
|
+
* CanonicalURI
|
|
97
|
+
* CanonicalQueryString (empty for us)
|
|
98
|
+
* CanonicalHeaders
|
|
99
|
+
* (blank line)
|
|
100
|
+
* SignedHeaders
|
|
101
|
+
* HashedPayload
|
|
102
|
+
*/
|
|
103
|
+
function buildCanonicalRequest(method, path, headers, signedHeadersList, payloadHash) {
|
|
104
|
+
// Sort headers alphabetically
|
|
105
|
+
const sortedHeaders = signedHeadersList.sort();
|
|
106
|
+
// Build canonical headers: each line is "lowercase-key:trimmed-value\n"
|
|
107
|
+
const canonicalHeaders = sortedHeaders
|
|
108
|
+
.map((key) => `${key}:${(headers[key] ?? "").trim()}`)
|
|
109
|
+
.join("\n");
|
|
110
|
+
// Signed headers: semicolon-separated list
|
|
111
|
+
const signedHeaders = sortedHeaders.join(";");
|
|
112
|
+
return [
|
|
113
|
+
method,
|
|
114
|
+
path,
|
|
115
|
+
"", // Query string (empty)
|
|
116
|
+
canonicalHeaders + "\n", // Must end with newline
|
|
117
|
+
signedHeaders,
|
|
118
|
+
payloadHash
|
|
119
|
+
].join("\n");
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Step 3: Build the string to sign.
|
|
123
|
+
*
|
|
124
|
+
* Format:
|
|
125
|
+
* Algorithm
|
|
126
|
+
* RequestDateTime
|
|
127
|
+
* CredentialScope
|
|
128
|
+
* HashedCanonicalRequest
|
|
129
|
+
*/
|
|
130
|
+
function buildStringToSign(amzDate, credentialScope, canonicalRequest) {
|
|
131
|
+
const hashedCanonicalRequest = sha256Hex(canonicalRequest);
|
|
132
|
+
return [ALGORITHM, amzDate, credentialScope, hashedCanonicalRequest].join("\n");
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Step 6: Build the Authorization header value.
|
|
136
|
+
*/
|
|
137
|
+
function buildAuthorizationHeader(accessKeyId, credentialScope, signedHeaders, signature) {
|
|
138
|
+
return `${ALGORITHM} Credential=${accessKeyId}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signature}`;
|
|
139
|
+
}
|
|
140
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
141
|
+
// Public API
|
|
142
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
143
|
+
/**
|
|
144
|
+
* Sign an AWS API request using SigV4.
|
|
145
|
+
*
|
|
146
|
+
* @param options - The request details and credentials
|
|
147
|
+
* @returns Headers to include in the HTTP request
|
|
148
|
+
*
|
|
149
|
+
* @example
|
|
150
|
+
* ```ts
|
|
151
|
+
* const headers = signRequest({
|
|
152
|
+
* method: "POST",
|
|
153
|
+
* url: "https://bedrock-runtime.us-east-1.amazonaws.com/model/.../converse",
|
|
154
|
+
* body: JSON.stringify({ messages: [...] }),
|
|
155
|
+
* credentials: { accessKeyId: "...", secretAccessKey: "..." },
|
|
156
|
+
* region: "us-east-1",
|
|
157
|
+
* service: "bedrock"
|
|
158
|
+
* });
|
|
159
|
+
*
|
|
160
|
+
* fetch(url, { headers: { ...headers, "Content-Type": "application/json" }, body });
|
|
161
|
+
* ```
|
|
162
|
+
*/
|
|
163
|
+
export function signRequest(options) {
|
|
164
|
+
const { method, url, body, credentials, region, service, timestamp } = options;
|
|
165
|
+
// Timestamps (use provided timestamp for testing, or current time)
|
|
166
|
+
const now = timestamp ?? new Date();
|
|
167
|
+
const amzDate = formatAmzDate(now);
|
|
168
|
+
const dateStamp = formatDateOnly(amzDate);
|
|
169
|
+
// Step 1: Hash the payload
|
|
170
|
+
const payloadHash = sha256Hex(body);
|
|
171
|
+
// Extract URL components
|
|
172
|
+
const host = extractHost(url);
|
|
173
|
+
const path = extractPath(url);
|
|
174
|
+
// Build headers to sign (sorted alphabetically)
|
|
175
|
+
const headersToSign = {
|
|
176
|
+
host: host,
|
|
177
|
+
"x-amz-content-sha256": payloadHash,
|
|
178
|
+
"x-amz-date": amzDate
|
|
179
|
+
};
|
|
180
|
+
// Add session token if present
|
|
181
|
+
if (credentials.sessionToken) {
|
|
182
|
+
headersToSign["x-amz-security-token"] = credentials.sessionToken;
|
|
183
|
+
}
|
|
184
|
+
const signedHeadersList = Object.keys(headersToSign);
|
|
185
|
+
// Step 2: Canonical Request
|
|
186
|
+
const canonicalRequest = buildCanonicalRequest(method, path, headersToSign, signedHeadersList, payloadHash);
|
|
187
|
+
// Credential scope: date/region/service/aws4_request
|
|
188
|
+
const credentialScope = `${dateStamp}/${region}/${service}/${AWS4_REQUEST}`;
|
|
189
|
+
// Step 3: String to Sign
|
|
190
|
+
const stringToSign = buildStringToSign(amzDate, credentialScope, canonicalRequest);
|
|
191
|
+
// Step 4: Signing Key
|
|
192
|
+
const signingKey = deriveSigningKey(credentials.secretAccessKey, dateStamp, region, service);
|
|
193
|
+
// Step 5: Signature
|
|
194
|
+
const signature = hmacSha256Hex(signingKey, stringToSign);
|
|
195
|
+
// Step 6: Authorization Header
|
|
196
|
+
const signedHeaders = signedHeadersList.sort().join(";");
|
|
197
|
+
const authorization = buildAuthorizationHeader(credentials.accessKeyId, credentialScope, signedHeaders, signature);
|
|
198
|
+
// Build final headers
|
|
199
|
+
const result = {
|
|
200
|
+
host: host,
|
|
201
|
+
"x-amz-date": amzDate,
|
|
202
|
+
"x-amz-content-sha256": payloadHash,
|
|
203
|
+
authorization: authorization
|
|
204
|
+
};
|
|
205
|
+
if (credentials.sessionToken) {
|
|
206
|
+
result["x-amz-security-token"] = credentials.sessionToken;
|
|
207
|
+
}
|
|
208
|
+
return result;
|
|
209
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../../src/utils/json.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAwChD"}
|