@node-llm/core 0.8.0 → 1.1.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 +0 -167
- package/dist/aliases.json +126 -0
- package/dist/chat/Chat.d.ts +7 -3
- package/dist/chat/Chat.d.ts.map +1 -1
- package/dist/chat/Chat.js +6 -7
- package/dist/chat/ChatStream.d.ts +25 -0
- package/dist/chat/ChatStream.d.ts.map +1 -0
- package/dist/chat/ChatStream.js +93 -0
- package/dist/config.d.ts +5 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -0
- package/dist/embedding/Embedding.d.ts +1 -1
- package/dist/embedding/Embedding.d.ts.map +1 -1
- package/dist/errors/index.d.ts +22 -0
- package/dist/errors/index.d.ts.map +1 -1
- package/dist/errors/index.js +32 -0
- package/dist/index.d.ts +10 -15
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -9
- package/dist/llm.d.ts +4 -3
- package/dist/llm.d.ts.map +1 -1
- package/dist/llm.js +44 -37
- package/dist/model_aliases.d.ts +3 -0
- package/dist/model_aliases.d.ts.map +1 -0
- package/dist/model_aliases.js +20 -0
- package/dist/models/ModelRegistry.d.ts +4 -0
- package/dist/models/ModelRegistry.d.ts.map +1 -1
- package/dist/models/ModelRegistry.js +15 -0
- package/dist/models/models.d.ts +729 -60
- package/dist/models/models.d.ts.map +1 -1
- package/dist/models/models.js +24375 -2312
- package/dist/models/types.d.ts +3 -3
- package/dist/models/types.d.ts.map +1 -1
- package/dist/models/types.js +3 -0
- package/dist/providers/BaseProvider.d.ts +23 -0
- package/dist/providers/BaseProvider.d.ts.map +1 -0
- package/dist/providers/BaseProvider.js +31 -0
- package/dist/providers/Provider.d.ts +17 -1
- package/dist/providers/Provider.d.ts.map +1 -1
- package/dist/providers/anthropic/AnthropicProvider.d.ts +6 -7
- package/dist/providers/anthropic/AnthropicProvider.d.ts.map +1 -1
- package/dist/providers/anthropic/AnthropicProvider.js +16 -13
- package/dist/providers/anthropic/Streaming.d.ts +1 -1
- package/dist/providers/anthropic/Streaming.d.ts.map +1 -1
- package/dist/providers/anthropic/Streaming.js +80 -54
- package/dist/providers/deepseek/Capabilities.js +1 -1
- package/dist/providers/deepseek/DeepSeekProvider.d.ts +5 -1
- package/dist/providers/deepseek/DeepSeekProvider.d.ts.map +1 -1
- package/dist/providers/deepseek/DeepSeekProvider.js +15 -1
- package/dist/providers/deepseek/Streaming.d.ts +1 -1
- package/dist/providers/deepseek/Streaming.d.ts.map +1 -1
- package/dist/providers/deepseek/Streaming.js +80 -48
- package/dist/providers/gemini/Capabilities.d.ts.map +1 -1
- package/dist/providers/gemini/Embeddings.d.ts +1 -1
- package/dist/providers/gemini/Embeddings.d.ts.map +1 -1
- package/dist/providers/gemini/GeminiProvider.d.ts +6 -4
- package/dist/providers/gemini/GeminiProvider.d.ts.map +1 -1
- package/dist/providers/gemini/GeminiProvider.js +14 -4
- package/dist/providers/gemini/Streaming.d.ts +1 -1
- package/dist/providers/gemini/Streaming.d.ts.map +1 -1
- package/dist/providers/gemini/Streaming.js +62 -39
- package/dist/providers/gemini/index.d.ts +1 -1
- package/dist/providers/gemini/index.js +1 -1
- package/dist/providers/ollama/Capabilities.d.ts.map +1 -1
- package/dist/providers/ollama/Capabilities.js +5 -1
- package/dist/providers/ollama/OllamaProvider.d.ts +1 -0
- package/dist/providers/ollama/OllamaProvider.d.ts.map +1 -1
- package/dist/providers/ollama/OllamaProvider.js +3 -0
- package/dist/providers/ollama/index.d.ts +1 -1
- package/dist/providers/ollama/index.js +1 -1
- package/dist/providers/openai/Embedding.d.ts +1 -1
- package/dist/providers/openai/Embedding.d.ts.map +1 -1
- package/dist/providers/openai/OpenAIProvider.d.ts +6 -3
- package/dist/providers/openai/OpenAIProvider.d.ts.map +1 -1
- package/dist/providers/openai/OpenAIProvider.js +15 -1
- package/dist/providers/openai/Streaming.d.ts +1 -1
- package/dist/providers/openai/Streaming.d.ts.map +1 -1
- package/dist/providers/openai/Streaming.js +75 -43
- package/dist/providers/openai/index.d.ts +1 -1
- package/dist/providers/openai/index.js +1 -1
- package/dist/providers/openrouter/Capabilities.d.ts +13 -0
- package/dist/providers/openrouter/Capabilities.d.ts.map +1 -0
- package/dist/providers/openrouter/Capabilities.js +67 -0
- package/dist/providers/openrouter/Models.d.ts +11 -0
- package/dist/providers/openrouter/Models.d.ts.map +1 -0
- package/dist/providers/openrouter/Models.js +88 -0
- package/dist/providers/openrouter/OpenRouterProvider.d.ts +21 -0
- package/dist/providers/openrouter/OpenRouterProvider.d.ts.map +1 -0
- package/dist/providers/openrouter/OpenRouterProvider.js +24 -0
- package/dist/providers/openrouter/index.d.ts +11 -0
- package/dist/providers/openrouter/index.d.ts.map +1 -0
- package/dist/providers/openrouter/index.js +26 -0
- package/dist/providers/registry.d.ts +11 -1
- package/dist/providers/registry.d.ts.map +1 -1
- package/dist/providers/registry.js +15 -1
- package/dist/streaming/Stream.d.ts +29 -0
- package/dist/streaming/Stream.d.ts.map +1 -0
- package/dist/streaming/Stream.js +67 -0
- package/dist/utils/FileLoader.d.ts.map +1 -1
- package/dist/utils/FileLoader.js +34 -3
- package/dist/utils/logger.d.ts +13 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +24 -0
- package/package.json +2 -2
- package/dist/chat/Stream.d.ts +0 -21
- package/dist/chat/Stream.d.ts.map +0 -1
- package/dist/chat/Stream.js +0 -73
- package/dist/providers/Embedding.d.ts +0 -20
- package/dist/providers/Embedding.d.ts.map +0 -1
- package/dist/providers/Embedding.js +0 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Capabilities } from "./Capabilities.js";
|
|
2
2
|
import { handleOpenAIError } from "./Errors.js";
|
|
3
3
|
import { buildUrl } from "./utils.js";
|
|
4
|
+
import { APIError } from "../../errors/index.js";
|
|
4
5
|
export class OpenAIStreaming {
|
|
5
6
|
baseUrl;
|
|
6
7
|
apiKey;
|
|
@@ -8,7 +9,8 @@ export class OpenAIStreaming {
|
|
|
8
9
|
this.baseUrl = baseUrl;
|
|
9
10
|
this.apiKey = apiKey;
|
|
10
11
|
}
|
|
11
|
-
async *execute(request) {
|
|
12
|
+
async *execute(request, controller) {
|
|
13
|
+
const abortController = controller || new AbortController();
|
|
12
14
|
const temperature = Capabilities.normalizeTemperature(request.temperature, request.model);
|
|
13
15
|
const body = {
|
|
14
16
|
model: request.model,
|
|
@@ -24,50 +26,80 @@ export class OpenAIStreaming {
|
|
|
24
26
|
if (request.response_format) {
|
|
25
27
|
body.response_format = request.response_format;
|
|
26
28
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
29
|
+
let done = false;
|
|
30
|
+
try {
|
|
31
|
+
const response = await fetch(buildUrl(this.baseUrl, '/chat/completions'), {
|
|
32
|
+
method: "POST",
|
|
33
|
+
headers: {
|
|
34
|
+
"Authorization": `Bearer ${this.apiKey}`,
|
|
35
|
+
"Content-Type": "application/json",
|
|
36
|
+
...request.headers,
|
|
37
|
+
},
|
|
38
|
+
body: JSON.stringify(body),
|
|
39
|
+
signal: abortController.signal,
|
|
40
|
+
});
|
|
41
|
+
if (!response.ok) {
|
|
42
|
+
await handleOpenAIError(response, request.model);
|
|
43
|
+
}
|
|
44
|
+
if (!response.body) {
|
|
45
|
+
throw new Error("No response body for streaming");
|
|
46
|
+
}
|
|
47
|
+
const reader = response.body.getReader();
|
|
48
|
+
const decoder = new TextDecoder();
|
|
49
|
+
let buffer = "";
|
|
50
|
+
while (true) {
|
|
51
|
+
const { value, done: readerDone } = await reader.read();
|
|
52
|
+
if (readerDone)
|
|
53
|
+
break;
|
|
54
|
+
const chunk = decoder.decode(value, { stream: true });
|
|
55
|
+
buffer += chunk;
|
|
56
|
+
const lines = buffer.split("\n\n");
|
|
57
|
+
buffer = lines.pop() || ""; // Keep the last incomplete part in the buffer
|
|
58
|
+
for (const line of lines) {
|
|
59
|
+
let trimmed = line.trim();
|
|
60
|
+
// Handle carriage returns
|
|
61
|
+
if (trimmed.endsWith('\r')) {
|
|
62
|
+
trimmed = trimmed.substring(0, trimmed.length - 1);
|
|
63
|
+
}
|
|
64
|
+
if (!trimmed.startsWith("data: "))
|
|
65
|
+
continue;
|
|
66
|
+
const data = trimmed.replace("data: ", "").trim();
|
|
67
|
+
if (data === "[DONE]") {
|
|
68
|
+
done = true;
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
const json = JSON.parse(data);
|
|
73
|
+
// Check for errors in the data
|
|
74
|
+
if (json.error) {
|
|
75
|
+
throw new APIError("OpenAI", response.status, json.error.message || "Stream error");
|
|
76
|
+
}
|
|
77
|
+
const delta = json.choices?.[0]?.delta?.content;
|
|
78
|
+
if (delta) {
|
|
79
|
+
yield { content: delta };
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch (e) {
|
|
83
|
+
// Re-throw APIError
|
|
84
|
+
if (e instanceof APIError)
|
|
85
|
+
throw e;
|
|
86
|
+
// Ignore other parse errors
|
|
65
87
|
}
|
|
66
88
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
89
|
+
}
|
|
90
|
+
done = true;
|
|
91
|
+
}
|
|
92
|
+
catch (e) {
|
|
93
|
+
// Graceful exit on abort
|
|
94
|
+
if (e instanceof Error && e.name === 'AbortError') {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
throw e;
|
|
98
|
+
}
|
|
99
|
+
finally {
|
|
100
|
+
// Cleanup: abort if user breaks early
|
|
101
|
+
if (!done) {
|
|
102
|
+
abortController.abort();
|
|
71
103
|
}
|
|
72
104
|
}
|
|
73
105
|
}
|
|
@@ -4,7 +4,7 @@ import { OpenAIProvider } from "./OpenAIProvider.js";
|
|
|
4
4
|
let registered = false;
|
|
5
5
|
/**
|
|
6
6
|
* Idempotent registration of the OpenAI provider.
|
|
7
|
-
* Automatically called by
|
|
7
|
+
* Automatically called by NodeLLM.configure({ provider: 'openai' })
|
|
8
8
|
*/
|
|
9
9
|
export function registerOpenAIProvider() {
|
|
10
10
|
if (registered)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare class OpenRouterCapabilities {
|
|
2
|
+
private static findModel;
|
|
3
|
+
static supportsVision(model: string): boolean;
|
|
4
|
+
static supportsTools(model: string): boolean;
|
|
5
|
+
static supportsStructuredOutput(model: string): boolean;
|
|
6
|
+
static supportsEmbeddings(model: string): boolean;
|
|
7
|
+
static supportsImageGeneration(model: string): boolean;
|
|
8
|
+
static supportsTranscription(model: string): boolean;
|
|
9
|
+
static supportsModeration(model: string): boolean;
|
|
10
|
+
static supportsReasoning(model: string): boolean;
|
|
11
|
+
static getContextWindow(model: string): number | null;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=Capabilities.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Capabilities.d.ts","sourceRoot":"","sources":["../../../src/providers/openrouter/Capabilities.ts"],"names":[],"mappings":"AAEA,qBAAa,sBAAsB;IACjC,OAAO,CAAC,MAAM,CAAC,SAAS;IAIxB,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAc7C,MAAM,CAAC,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAS5C,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAUvD,MAAM,CAAC,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IASjD,MAAM,CAAC,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAItD,MAAM,CAAC,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAIpD,MAAM,CAAC,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAIjD,MAAM,CAAC,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAWhD,MAAM,CAAC,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;CAItD"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { ModelRegistry } from "../../models/ModelRegistry.js";
|
|
2
|
+
export class OpenRouterCapabilities {
|
|
3
|
+
static findModel(model) {
|
|
4
|
+
return ModelRegistry.find(model, "openrouter");
|
|
5
|
+
}
|
|
6
|
+
static supportsVision(model) {
|
|
7
|
+
const info = this.findModel(model);
|
|
8
|
+
if (info)
|
|
9
|
+
return info.capabilities.includes('vision') || info.modalities.input.includes('image');
|
|
10
|
+
// Fallback heuristics
|
|
11
|
+
return model.includes('vision') ||
|
|
12
|
+
model.includes('gpt-4o') ||
|
|
13
|
+
model.includes('claude-3') ||
|
|
14
|
+
model.includes('gemini-1.5') ||
|
|
15
|
+
model.includes('gemini-2.0') ||
|
|
16
|
+
model.includes('flash') ||
|
|
17
|
+
model.includes('gemini-pro-vision');
|
|
18
|
+
}
|
|
19
|
+
static supportsTools(model) {
|
|
20
|
+
const info = this.findModel(model);
|
|
21
|
+
if (info)
|
|
22
|
+
return info.capabilities.includes('tools') || info.capabilities.includes('function_calling');
|
|
23
|
+
// Fallback: Default to true for OpenRouter as most models support tools
|
|
24
|
+
// but this is the "honest" check we wanted.
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
static supportsStructuredOutput(model) {
|
|
28
|
+
const info = this.findModel(model);
|
|
29
|
+
if (info)
|
|
30
|
+
return info.capabilities.includes('structured_output') || info.id.includes('gpt-4');
|
|
31
|
+
// Fallback heuristics
|
|
32
|
+
return model.includes('gpt-4') ||
|
|
33
|
+
model.includes('gpt-3.5') ||
|
|
34
|
+
model.includes('claude-3');
|
|
35
|
+
}
|
|
36
|
+
static supportsEmbeddings(model) {
|
|
37
|
+
const info = this.findModel(model);
|
|
38
|
+
if (info)
|
|
39
|
+
return info.capabilities.includes('embeddings');
|
|
40
|
+
// Fallback heuristics
|
|
41
|
+
return model.includes('embedding') ||
|
|
42
|
+
model.includes('text-sdk');
|
|
43
|
+
}
|
|
44
|
+
static supportsImageGeneration(model) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
static supportsTranscription(model) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
static supportsModeration(model) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
static supportsReasoning(model) {
|
|
54
|
+
const info = this.findModel(model);
|
|
55
|
+
if (info)
|
|
56
|
+
return info.capabilities.includes('reasoning');
|
|
57
|
+
// Fallback heuristics
|
|
58
|
+
return model.includes('o1') ||
|
|
59
|
+
model.includes('o3') ||
|
|
60
|
+
model.includes('deepseek-r1') ||
|
|
61
|
+
model.includes('qwq');
|
|
62
|
+
}
|
|
63
|
+
static getContextWindow(model) {
|
|
64
|
+
const info = this.findModel(model);
|
|
65
|
+
return info?.context_window ?? null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ModelInfo } from "../Provider.js";
|
|
2
|
+
export declare class OpenRouterModels {
|
|
3
|
+
private readonly baseUrl;
|
|
4
|
+
private readonly apiKey;
|
|
5
|
+
constructor(baseUrl: string, apiKey: string);
|
|
6
|
+
execute(): Promise<ModelInfo[]>;
|
|
7
|
+
private parseModel;
|
|
8
|
+
private parseCapabilities;
|
|
9
|
+
private parsePricing;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=Models.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Models.d.ts","sourceRoot":"","sources":["../../../src/providers/openrouter/Models.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAwB3C,qBAAa,gBAAgB;IAEzB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBADN,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM;IAG3B,OAAO,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IAiBrC,OAAO,CAAC,UAAU;IA0BlB,OAAO,CAAC,iBAAiB;IAyBzB,OAAO,CAAC,YAAY;CA2BrB"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
export class OpenRouterModels {
|
|
2
|
+
baseUrl;
|
|
3
|
+
apiKey;
|
|
4
|
+
constructor(baseUrl, apiKey) {
|
|
5
|
+
this.baseUrl = baseUrl;
|
|
6
|
+
this.apiKey = apiKey;
|
|
7
|
+
}
|
|
8
|
+
async execute() {
|
|
9
|
+
const response = await fetch(`${this.baseUrl}/models`, {
|
|
10
|
+
headers: {
|
|
11
|
+
"Authorization": `Bearer ${this.apiKey}`,
|
|
12
|
+
"Content-Type": "application/json",
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
if (!response.ok) {
|
|
16
|
+
throw new Error(`OpenRouter API error: ${response.status}`);
|
|
17
|
+
}
|
|
18
|
+
const data = await response.json();
|
|
19
|
+
return data.data.map((model) => this.parseModel(model));
|
|
20
|
+
}
|
|
21
|
+
parseModel(model) {
|
|
22
|
+
const family = model.id.split('/')[0] || "";
|
|
23
|
+
return {
|
|
24
|
+
id: model.id,
|
|
25
|
+
name: model.name,
|
|
26
|
+
provider: "openrouter",
|
|
27
|
+
family: family,
|
|
28
|
+
context_window: model.context_length,
|
|
29
|
+
max_output_tokens: model.top_provider?.max_completion_tokens ?? null,
|
|
30
|
+
modalities: {
|
|
31
|
+
input: model.architecture?.input_modalities || [],
|
|
32
|
+
output: model.architecture?.output_modalities || []
|
|
33
|
+
},
|
|
34
|
+
capabilities: this.parseCapabilities(model),
|
|
35
|
+
pricing: this.parsePricing(model.pricing),
|
|
36
|
+
metadata: {
|
|
37
|
+
created_at: model.created ? new Date(model.created * 1000) : undefined,
|
|
38
|
+
description: model.description,
|
|
39
|
+
architecture: model.architecture,
|
|
40
|
+
top_provider: model.top_provider,
|
|
41
|
+
supported_parameters: model.supported_parameters
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
parseCapabilities(model) {
|
|
46
|
+
const caps = [];
|
|
47
|
+
const params = model.supported_parameters || [];
|
|
48
|
+
const inputModalities = model.architecture?.input_modalities || [];
|
|
49
|
+
if (params.includes('tools') || params.includes('function_calling')) {
|
|
50
|
+
caps.push('tools');
|
|
51
|
+
}
|
|
52
|
+
if (inputModalities.includes('image')) {
|
|
53
|
+
caps.push('vision');
|
|
54
|
+
}
|
|
55
|
+
if (model.id.includes('embedding') || model.id.includes('text-sdk')) {
|
|
56
|
+
caps.push('embeddings');
|
|
57
|
+
}
|
|
58
|
+
// Heuristics for reasoning
|
|
59
|
+
if (model.id.includes('o1') || model.id.includes('o3') || model.id.includes('deepseek-r1') || model.id.includes('qwq')) {
|
|
60
|
+
caps.push('reasoning');
|
|
61
|
+
}
|
|
62
|
+
return caps;
|
|
63
|
+
}
|
|
64
|
+
parsePricing(pricing) {
|
|
65
|
+
const result = {
|
|
66
|
+
text_tokens: {
|
|
67
|
+
standard: {}
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
const prompt = parseFloat(pricing.prompt);
|
|
71
|
+
const completion = parseFloat(pricing.completion);
|
|
72
|
+
const cachedInput = pricing.input_cache_read ? parseFloat(pricing.input_cache_read) : 0;
|
|
73
|
+
const reasoning = pricing.internal_reasoning ? parseFloat(pricing.internal_reasoning) : 0;
|
|
74
|
+
if (prompt > 0) {
|
|
75
|
+
result.text_tokens.standard.input_per_million = prompt * 1_000_000;
|
|
76
|
+
}
|
|
77
|
+
if (completion > 0) {
|
|
78
|
+
result.text_tokens.standard.output_per_million = completion * 1_000_000;
|
|
79
|
+
}
|
|
80
|
+
if (cachedInput > 0) {
|
|
81
|
+
result.text_tokens.standard.cached_input_per_million = cachedInput * 1_000_000;
|
|
82
|
+
}
|
|
83
|
+
if (reasoning > 0) {
|
|
84
|
+
result.text_tokens.standard.reasoning_output_per_million = reasoning * 1_000_000;
|
|
85
|
+
}
|
|
86
|
+
return result;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { OpenAIProvider } from "../openai/OpenAIProvider.js";
|
|
2
|
+
export interface OpenRouterProviderOptions {
|
|
3
|
+
apiKey: string;
|
|
4
|
+
baseUrl?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare class OpenRouterProvider extends OpenAIProvider {
|
|
7
|
+
capabilities: {
|
|
8
|
+
supportsVision: (model: string) => boolean;
|
|
9
|
+
supportsTools: (model: string) => boolean;
|
|
10
|
+
supportsStructuredOutput: (model: string) => boolean;
|
|
11
|
+
supportsEmbeddings: (model: string) => boolean;
|
|
12
|
+
supportsImageGeneration: (model: string) => boolean;
|
|
13
|
+
supportsTranscription: (model: string) => boolean;
|
|
14
|
+
supportsModeration: (model: string) => boolean;
|
|
15
|
+
supportsReasoning: (model: string) => boolean;
|
|
16
|
+
getContextWindow: (model: string) => number | null;
|
|
17
|
+
};
|
|
18
|
+
constructor(options: OpenRouterProviderOptions);
|
|
19
|
+
protected providerName(): string;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=OpenRouterProvider.d.ts.map
|
|
@@ -0,0 +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;AAG7D,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;oCACZ,MAAM;yCACD,MAAM;uCACR,MAAM;oCACT,MAAM;mCACP,MAAM;kCACP,MAAM;MAChC;gBAEU,OAAO,EAAE,yBAAyB;IAO9C,SAAS,CAAC,YAAY,IAAI,MAAM;CAGjC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { OpenAIProvider } from "../openai/OpenAIProvider.js";
|
|
2
|
+
import { OpenRouterCapabilities } from "./Capabilities.js";
|
|
3
|
+
export class OpenRouterProvider extends OpenAIProvider {
|
|
4
|
+
capabilities = {
|
|
5
|
+
supportsVision: (model) => OpenRouterCapabilities.supportsVision(model),
|
|
6
|
+
supportsTools: (model) => OpenRouterCapabilities.supportsTools(model),
|
|
7
|
+
supportsStructuredOutput: (model) => OpenRouterCapabilities.supportsStructuredOutput(model),
|
|
8
|
+
supportsEmbeddings: (model) => OpenRouterCapabilities.supportsEmbeddings(model),
|
|
9
|
+
supportsImageGeneration: (model) => OpenRouterCapabilities.supportsImageGeneration(model),
|
|
10
|
+
supportsTranscription: (model) => OpenRouterCapabilities.supportsTranscription(model),
|
|
11
|
+
supportsModeration: (model) => OpenRouterCapabilities.supportsModeration(model),
|
|
12
|
+
supportsReasoning: (model) => OpenRouterCapabilities.supportsReasoning(model),
|
|
13
|
+
getContextWindow: (model) => OpenRouterCapabilities.getContextWindow(model) || null,
|
|
14
|
+
};
|
|
15
|
+
constructor(options) {
|
|
16
|
+
super({
|
|
17
|
+
apiKey: options.apiKey,
|
|
18
|
+
baseUrl: options.baseUrl || "https://openrouter.ai/api/v1"
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
providerName() {
|
|
22
|
+
return "OpenRouter";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from "./OpenRouterProvider.js";
|
|
2
|
+
/**
|
|
3
|
+
* Idempotent registration of the OpenRouter provider.
|
|
4
|
+
* Automatically called by NodeLLM.configure({ provider: 'openrouter' })
|
|
5
|
+
*/
|
|
6
|
+
export declare function registerOpenRouterProvider(): void;
|
|
7
|
+
/**
|
|
8
|
+
* Alias for registerOpenRouterProvider for internal use.
|
|
9
|
+
*/
|
|
10
|
+
export declare const ensureOpenRouterRegistered: typeof registerOpenRouterProvider;
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/providers/openrouter/index.ts"],"names":[],"mappings":"AAIA,cAAc,yBAAyB,CAAC;AAIxC;;;GAGG;AACH,wBAAgB,0BAA0B,SAezC;AAED;;GAEG;AACH,eAAO,MAAM,0BAA0B,mCAA6B,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { config } from "../../config.js";
|
|
2
|
+
import { providerRegistry } from "../registry.js";
|
|
3
|
+
import { OpenRouterProvider } from "./OpenRouterProvider.js";
|
|
4
|
+
export * from "./OpenRouterProvider.js";
|
|
5
|
+
let registered = false;
|
|
6
|
+
/**
|
|
7
|
+
* Idempotent registration of the OpenRouter provider.
|
|
8
|
+
* Automatically called by NodeLLM.configure({ provider: 'openrouter' })
|
|
9
|
+
*/
|
|
10
|
+
export function registerOpenRouterProvider() {
|
|
11
|
+
if (registered)
|
|
12
|
+
return;
|
|
13
|
+
providerRegistry.register("openrouter", () => {
|
|
14
|
+
const apiKey = config.openrouterApiKey;
|
|
15
|
+
const baseUrl = config.openrouterApiBase;
|
|
16
|
+
if (!apiKey) {
|
|
17
|
+
throw new Error("openrouterApiKey is not set in config or OPENROUTER_API_KEY environment variable");
|
|
18
|
+
}
|
|
19
|
+
return new OpenRouterProvider({ apiKey, baseUrl });
|
|
20
|
+
});
|
|
21
|
+
registered = true;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Alias for registerOpenRouterProvider for internal use.
|
|
25
|
+
*/
|
|
26
|
+
export const ensureOpenRouterRegistered = registerOpenRouterProvider;
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { Provider } from "./Provider.js";
|
|
2
|
+
import { registerOpenAIProvider } from "./openai/index.js";
|
|
3
|
+
import { registerAnthropicProvider } from "./anthropic/index.js";
|
|
4
|
+
import { registerGeminiProvider } from "./gemini/index.js";
|
|
5
|
+
import { registerDeepSeekProvider } from "./deepseek/index.js";
|
|
6
|
+
import { registerOllamaProvider } from "./ollama/index.js";
|
|
7
|
+
import { registerOpenRouterProvider } from "./openrouter/index.js";
|
|
2
8
|
type ProviderFactory = () => Provider;
|
|
3
9
|
declare class ProviderRegistry {
|
|
4
10
|
private providers;
|
|
@@ -10,11 +16,15 @@ declare class ProviderRegistry {
|
|
|
10
16
|
* Resolve a provider by name
|
|
11
17
|
*/
|
|
12
18
|
resolve(name: string): Provider;
|
|
19
|
+
/**
|
|
20
|
+
* Check if a provider is registered
|
|
21
|
+
*/
|
|
22
|
+
has(name: string): boolean;
|
|
13
23
|
/**
|
|
14
24
|
* Introspection / debugging
|
|
15
25
|
*/
|
|
16
26
|
list(): string[];
|
|
17
27
|
}
|
|
18
28
|
export declare const providerRegistry: ProviderRegistry;
|
|
19
|
-
export {};
|
|
29
|
+
export { registerOpenAIProvider as ensureOpenAIRegistered, registerOpenAIProvider, registerAnthropicProvider, registerGeminiProvider, registerDeepSeekProvider, registerOllamaProvider, registerOpenRouterProvider };
|
|
20
30
|
//# 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;
|
|
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;AAEnE,KAAK,eAAe,GAAG,MAAM,QAAQ,CAAC;AAEtC,cAAM,gBAAgB;IACpB,OAAO,CAAC,SAAS,CAAsC;IAEvD;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,IAAI;IAQtD;;OAEG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ;IAU/B;;OAEG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI1B;;OAEG;IACH,IAAI,IAAI,MAAM,EAAE;CAGjB;AAED,eAAO,MAAM,gBAAgB,kBAAyB,CAAC;AAGvD,OAAO,EACL,sBAAsB,IAAI,sBAAsB,EAChD,sBAAsB,EACtB,yBAAyB,EACzB,sBAAsB,EACtB,wBAAwB,EACxB,sBAAsB,EACtB,0BAA0B,EAC3B,CAAC"}
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import { registerOpenAIProvider } from "./openai/index.js";
|
|
2
|
+
import { registerAnthropicProvider } from "./anthropic/index.js";
|
|
3
|
+
import { registerGeminiProvider } from "./gemini/index.js";
|
|
4
|
+
import { registerDeepSeekProvider } from "./deepseek/index.js";
|
|
5
|
+
import { registerOllamaProvider } from "./ollama/index.js";
|
|
6
|
+
import { registerOpenRouterProvider } from "./openrouter/index.js";
|
|
1
7
|
class ProviderRegistry {
|
|
2
8
|
providers = new Map();
|
|
3
9
|
/**
|
|
@@ -15,10 +21,16 @@ class ProviderRegistry {
|
|
|
15
21
|
resolve(name) {
|
|
16
22
|
const factory = this.providers.get(name);
|
|
17
23
|
if (!factory) {
|
|
18
|
-
throw new Error(`Unknown
|
|
24
|
+
throw new Error(`Unknown NodeLLM provider '${name}'`);
|
|
19
25
|
}
|
|
20
26
|
return factory();
|
|
21
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Check if a provider is registered
|
|
30
|
+
*/
|
|
31
|
+
has(name) {
|
|
32
|
+
return this.providers.has(name);
|
|
33
|
+
}
|
|
22
34
|
/**
|
|
23
35
|
* Introspection / debugging
|
|
24
36
|
*/
|
|
@@ -27,3 +39,5 @@ class ProviderRegistry {
|
|
|
27
39
|
}
|
|
28
40
|
}
|
|
29
41
|
export const providerRegistry = new ProviderRegistry();
|
|
42
|
+
// Exported registration functions (delegates to provider-specific index files)
|
|
43
|
+
export { registerOpenAIProvider as ensureOpenAIRegistered, registerOpenAIProvider, registerAnthropicProvider, registerGeminiProvider, registerDeepSeekProvider, registerOllamaProvider, registerOpenRouterProvider };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stream wrapper class inspired by OpenAI SDK
|
|
3
|
+
* Provides utilities for working with async iterables
|
|
4
|
+
*/
|
|
5
|
+
export declare class Stream<T> implements AsyncIterable<T> {
|
|
6
|
+
private iterator;
|
|
7
|
+
private controller?;
|
|
8
|
+
private consumed;
|
|
9
|
+
constructor(iterator: () => AsyncIterator<T>, controller?: AbortController | undefined);
|
|
10
|
+
/**
|
|
11
|
+
* Create a Stream from an async generator
|
|
12
|
+
*/
|
|
13
|
+
static fromAsyncIterable<T>(iterable: AsyncIterable<T>, controller?: AbortController): Stream<T>;
|
|
14
|
+
[Symbol.asyncIterator](): AsyncIterator<T>;
|
|
15
|
+
/**
|
|
16
|
+
* Splits the stream into two streams which can be
|
|
17
|
+
* independently read from at different speeds.
|
|
18
|
+
*/
|
|
19
|
+
tee(): [Stream<T>, Stream<T>];
|
|
20
|
+
/**
|
|
21
|
+
* Collect all chunks into an array
|
|
22
|
+
*/
|
|
23
|
+
toArray(): Promise<T[]>;
|
|
24
|
+
/**
|
|
25
|
+
* Abort the underlying stream
|
|
26
|
+
*/
|
|
27
|
+
abort(): void;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=Stream.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Stream.d.ts","sourceRoot":"","sources":["../../src/streaming/Stream.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,qBAAa,MAAM,CAAC,CAAC,CAAE,YAAW,aAAa,CAAC,CAAC,CAAC;IAI9C,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,UAAU,CAAC;IAJrB,OAAO,CAAC,QAAQ,CAAS;gBAGf,QAAQ,EAAE,MAAM,aAAa,CAAC,CAAC,CAAC,EAChC,UAAU,CAAC,EAAE,eAAe,YAAA;IAGtC;;OAEG;IACH,MAAM,CAAC,iBAAiB,CAAC,CAAC,EACxB,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,EAC1B,UAAU,CAAC,EAAE,eAAe,GAC3B,MAAM,CAAC,CAAC,CAAC;IAIZ,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC;IAQ1C;;;OAGG;IACH,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IAwB7B;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,CAAC,EAAE,CAAC;IAQ7B;;OAEG;IACH,KAAK,IAAI,IAAI;CAGd"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stream wrapper class inspired by OpenAI SDK
|
|
3
|
+
* Provides utilities for working with async iterables
|
|
4
|
+
*/
|
|
5
|
+
export class Stream {
|
|
6
|
+
iterator;
|
|
7
|
+
controller;
|
|
8
|
+
consumed = false;
|
|
9
|
+
constructor(iterator, controller) {
|
|
10
|
+
this.iterator = iterator;
|
|
11
|
+
this.controller = controller;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Create a Stream from an async generator
|
|
15
|
+
*/
|
|
16
|
+
static fromAsyncIterable(iterable, controller) {
|
|
17
|
+
return new Stream(() => iterable[Symbol.asyncIterator](), controller);
|
|
18
|
+
}
|
|
19
|
+
[Symbol.asyncIterator]() {
|
|
20
|
+
if (this.consumed) {
|
|
21
|
+
throw new Error('Cannot iterate over a consumed stream, use `.tee()` to split the stream.');
|
|
22
|
+
}
|
|
23
|
+
this.consumed = true;
|
|
24
|
+
return this.iterator();
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Splits the stream into two streams which can be
|
|
28
|
+
* independently read from at different speeds.
|
|
29
|
+
*/
|
|
30
|
+
tee() {
|
|
31
|
+
const left = [];
|
|
32
|
+
const right = [];
|
|
33
|
+
const iterator = this.iterator();
|
|
34
|
+
const teeIterator = (queue) => {
|
|
35
|
+
return {
|
|
36
|
+
next: () => {
|
|
37
|
+
if (queue.length === 0) {
|
|
38
|
+
const result = iterator.next();
|
|
39
|
+
left.push(result);
|
|
40
|
+
right.push(result);
|
|
41
|
+
}
|
|
42
|
+
return queue.shift();
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
return [
|
|
47
|
+
new Stream(() => teeIterator(left), this.controller),
|
|
48
|
+
new Stream(() => teeIterator(right), this.controller),
|
|
49
|
+
];
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Collect all chunks into an array
|
|
53
|
+
*/
|
|
54
|
+
async toArray() {
|
|
55
|
+
const result = [];
|
|
56
|
+
for await (const chunk of this) {
|
|
57
|
+
result.push(chunk);
|
|
58
|
+
}
|
|
59
|
+
return result;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Abort the underlying stream
|
|
63
|
+
*/
|
|
64
|
+
abort() {
|
|
65
|
+
this.controller?.abort();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileLoader.d.ts","sourceRoot":"","sources":["../../src/utils/FileLoader.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AA2CjD,qBAAa,UAAU;WACR,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"FileLoader.d.ts","sourceRoot":"","sources":["../../src/utils/FileLoader.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AA2CjD,qBAAa,UAAU;WACR,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;CAkF1D"}
|