@anvaka/vue-llm 0.3.2 → 0.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/README.md +2 -1
- package/dist/pricing/rates.js +18 -3
- package/dist/providers/BedrockMantleProvider.js +265 -0
- package/dist/providers/BedrockProvider.js +45 -41
- package/dist/providers/factory.js +84 -50
- package/dist/providers/index.js +12 -9
- package/dist/vue/components/LLMConfigModal.vue.js +357 -297
- package/dist/vue/useLLM.js +10 -8
- package/dist/vue-llm.css +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
Browser-only LLM client + Vue 3 plugin, provider adapters, and lightweight components.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
|
-
- Provider factory with 10 built-in providers (OpenAI, Anthropic,
|
|
6
|
+
- Provider factory with 10 built-in providers (OpenAI, Anthropic, AWS, Grok, Gemini, Ollama, Llama Server, OpenRouter, DeepSeek, Custom) – extend with `registerProvider()`
|
|
7
|
+
- **AWS** runs on the Bedrock Mantle endpoint and exposes the entire catalog (Claude, GPT-OSS, Qwen, Mistral, Gemma, …) from one model list — each request is routed to the right API surface automatically (Claude → Anthropic Messages, everything else → OpenAI Chat Completions)
|
|
7
8
|
- LocalStorage-based config store (custom storage adapter supported)
|
|
8
9
|
- Streaming + promise requests via `llmClient.stream()`
|
|
9
10
|
- Normalized usage + USD cost on every response (override built-in rates per app or per model)
|
package/dist/pricing/rates.js
CHANGED
|
@@ -33,15 +33,30 @@ const t = {
|
|
|
33
33
|
"claude-sonnet-4-5": { input: 3, output: 15, cachedInput: 0.3, cacheCreation: 3.75 },
|
|
34
34
|
"claude-haiku-4-5": { input: 1, output: 5, cachedInput: 0.1, cacheCreation: 1.25 }
|
|
35
35
|
},
|
|
36
|
-
// Bedrock charges the same per-token rates as Anthropic direct for the
|
|
37
|
-
//
|
|
36
|
+
// Bedrock charges the same per-token rates as Anthropic direct for the Claude
|
|
37
|
+
// models; only the id form differs by backend. bedrock-runtime uses the
|
|
38
|
+
// cross-region inference profiles (us.anthropic.*); bedrock-mantle uses the
|
|
39
|
+
// Anthropic-native ids (anthropic.claude-*-v1). Both backends share this
|
|
40
|
+
// provider type ('bedrock'), so both id forms live here. Keys omit any -v1
|
|
41
|
+
// suffix so lookupRates' prefix match catches versioned ids.
|
|
38
42
|
bedrock: {
|
|
39
43
|
"us.anthropic.claude-opus-4-8": { input: 5, output: 25, cachedInput: 0.5, cacheCreation: 6.25 },
|
|
40
44
|
"us.anthropic.claude-opus-4-7": { input: 5, output: 25, cachedInput: 0.5, cacheCreation: 6.25 },
|
|
41
45
|
"us.anthropic.claude-opus-4-6": { input: 5, output: 25, cachedInput: 0.5, cacheCreation: 6.25 },
|
|
42
46
|
"us.anthropic.claude-sonnet-4-6": { input: 3, output: 15, cachedInput: 0.3, cacheCreation: 3.75 },
|
|
43
47
|
"us.anthropic.claude-sonnet-4-5": { input: 3, output: 15, cachedInput: 0.3, cacheCreation: 3.75 },
|
|
44
|
-
"us.anthropic.claude-haiku-4-5": { input: 1, output: 5, cachedInput: 0.1, cacheCreation: 1.25 }
|
|
48
|
+
"us.anthropic.claude-haiku-4-5": { input: 1, output: 5, cachedInput: 0.1, cacheCreation: 1.25 },
|
|
49
|
+
// bedrock-mantle ids
|
|
50
|
+
"anthropic.claude-opus-4-8": { input: 5, output: 25, cachedInput: 0.5, cacheCreation: 6.25 },
|
|
51
|
+
"anthropic.claude-opus-4-7": { input: 5, output: 25, cachedInput: 0.5, cacheCreation: 6.25 },
|
|
52
|
+
"anthropic.claude-opus-4-6": { input: 5, output: 25, cachedInput: 0.5, cacheCreation: 6.25 },
|
|
53
|
+
"anthropic.claude-sonnet-5": { input: 3, output: 15, cachedInput: 0.3, cacheCreation: 3.75 },
|
|
54
|
+
"anthropic.claude-sonnet-4-6": { input: 3, output: 15, cachedInput: 0.3, cacheCreation: 3.75 },
|
|
55
|
+
"anthropic.claude-sonnet-4-5": { input: 3, output: 15, cachedInput: 0.3, cacheCreation: 3.75 },
|
|
56
|
+
"anthropic.claude-haiku-4-5": { input: 1, output: 5, cachedInput: 0.1, cacheCreation: 1.25 },
|
|
57
|
+
// OpenAI frontier via /openai/v1/responses (usage billed on input/output tokens)
|
|
58
|
+
"openai.gpt-5.5": { input: 5, output: 30, cachedInput: 0.5 },
|
|
59
|
+
"openai.gpt-5.4": { input: 5, output: 30, cachedInput: 0.5 }
|
|
45
60
|
},
|
|
46
61
|
gemini: {
|
|
47
62
|
// Gemini 3.x — current lineup (May 2026)
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import { BaseProvider as d } from "./BaseProvider.js";
|
|
2
|
+
import { OpenAIProvider as h } from "./OpenAIProvider.js";
|
|
3
|
+
import { AnthropicProvider as f } from "./AnthropicProvider.js";
|
|
4
|
+
const l = [
|
|
5
|
+
"anthropic.claude-opus-4-8",
|
|
6
|
+
"anthropic.claude-opus-4-7",
|
|
7
|
+
"anthropic.claude-sonnet-5",
|
|
8
|
+
"anthropic.claude-haiku-4-5"
|
|
9
|
+
];
|
|
10
|
+
function _(s) {
|
|
11
|
+
return typeof s == "string" && s.includes("anthropic.claude");
|
|
12
|
+
}
|
|
13
|
+
function m(s) {
|
|
14
|
+
return typeof s == "string" && (/openai\.gpt-5/.test(s) || /codex/.test(s));
|
|
15
|
+
}
|
|
16
|
+
function g(s) {
|
|
17
|
+
return /does not support the '[^']*' API/i.test((s == null ? void 0 : s.message) || "");
|
|
18
|
+
}
|
|
19
|
+
function y(s) {
|
|
20
|
+
const e = (s == null ? void 0 : s.message) || "";
|
|
21
|
+
if (/not available for this account|Berm is not enabled|access_denied|permission_(error|denied)/i.test(e)) {
|
|
22
|
+
const t = new Error(`This model isn't enabled for your AWS account — request access in the Amazon Bedrock console, then try again. (${e})`);
|
|
23
|
+
return t.cause = s, t;
|
|
24
|
+
}
|
|
25
|
+
return s;
|
|
26
|
+
}
|
|
27
|
+
class b extends f {
|
|
28
|
+
getApiPath() {
|
|
29
|
+
return "/anthropic/v1/messages";
|
|
30
|
+
}
|
|
31
|
+
buildHeaders() {
|
|
32
|
+
const e = super.buildHeaders();
|
|
33
|
+
return delete e["anthropic-dangerous-direct-browser-access"], e;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
class R extends h {
|
|
37
|
+
// /v1/chat/completions + Bearer + /v1/models are inherited. Only discovery
|
|
38
|
+
// differs: list EVERY available model so the single dropdown offers the whole
|
|
39
|
+
// catalog (the router, not this class, decides each model's transport).
|
|
40
|
+
async detectCapabilities() {
|
|
41
|
+
await super.detectCapabilities(), this.capabilities.add("tools");
|
|
42
|
+
}
|
|
43
|
+
parseModelsResponse(e) {
|
|
44
|
+
return ((e == null ? void 0 : e.data) || []).filter((t) => t && typeof t.id == "string" && (t.status == null || t.status === "available")).map((t) => t.id).sort();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const k = 16;
|
|
48
|
+
class p extends d {
|
|
49
|
+
constructor(e, t) {
|
|
50
|
+
super(e), this._apiPath = t || "/v1/responses";
|
|
51
|
+
}
|
|
52
|
+
getApiPath() {
|
|
53
|
+
return this._apiPath;
|
|
54
|
+
}
|
|
55
|
+
requiresAuth() {
|
|
56
|
+
return !!this.config.apiKey;
|
|
57
|
+
}
|
|
58
|
+
// Bearer (BaseProvider default)
|
|
59
|
+
async detectCapabilities() {
|
|
60
|
+
const e = (this.config.model || "").toLowerCase();
|
|
61
|
+
this.capabilities.add("tools"), (e.includes("gpt-5") || e.includes("codex") || /(^|\.)o[13]/.test(e)) && this.capabilities.add("thinking");
|
|
62
|
+
}
|
|
63
|
+
prepareRequest(e, t) {
|
|
64
|
+
const r = {
|
|
65
|
+
model: t.model || this.config.model,
|
|
66
|
+
input: v(e),
|
|
67
|
+
// The Responses API rejects max_output_tokens < 16 (Chat Completions and
|
|
68
|
+
// Messages accept less), so clamp — e.g. testConnection sends 10.
|
|
69
|
+
max_output_tokens: Math.max(k, t.maxTokens || 1e3),
|
|
70
|
+
stream: t.stream || !1
|
|
71
|
+
}, i = x(t.tools);
|
|
72
|
+
return i && (r.tools = i), r;
|
|
73
|
+
}
|
|
74
|
+
processResponse(e) {
|
|
75
|
+
let t = "", n = "";
|
|
76
|
+
const r = [];
|
|
77
|
+
for (const o of e.output || [])
|
|
78
|
+
if (o.type === "message")
|
|
79
|
+
for (const a of o.content || []) a.type === "output_text" && (t += a.text || "");
|
|
80
|
+
else if (o.type === "reasoning")
|
|
81
|
+
for (const a of o.content || []) a.type === "reasoning_text" && (n += a.text || "");
|
|
82
|
+
else o.type === "function_call" && r.push({ id: o.call_id || o.id, name: o.name, args: A(o.arguments) });
|
|
83
|
+
const i = {
|
|
84
|
+
content: t,
|
|
85
|
+
usage: u(e.usage),
|
|
86
|
+
finishReason: c(e.status)
|
|
87
|
+
};
|
|
88
|
+
return n && (i.thinking = n), r.length && (i.toolCalls = r), i;
|
|
89
|
+
}
|
|
90
|
+
// Responses streams plain `data: {json}` SSE (no [DONE]); events carry a
|
|
91
|
+
// `type`. AnthropicProvider/OpenAIProvider use the same `data: ` line format.
|
|
92
|
+
parseStreamingLine(e) {
|
|
93
|
+
if (!e.startsWith("data: ")) return null;
|
|
94
|
+
const t = e.slice(6).trim();
|
|
95
|
+
if (t === "[DONE]") return { done: !0 };
|
|
96
|
+
try {
|
|
97
|
+
return JSON.parse(t);
|
|
98
|
+
} catch {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
extractStreamingContent(e) {
|
|
103
|
+
var t, n, r, i;
|
|
104
|
+
if (e.done) return { done: !0 };
|
|
105
|
+
switch (e.type) {
|
|
106
|
+
case "response.output_text.delta":
|
|
107
|
+
return { content: e.delta || "", done: !1 };
|
|
108
|
+
case "response.reasoning_text.delta":
|
|
109
|
+
return { thinking: e.delta || "", done: !1 };
|
|
110
|
+
// A function_call item opens with output_item.added (carrying the call_id
|
|
111
|
+
// and name), then streams its JSON arguments as function_call_arguments.delta
|
|
112
|
+
// fragments. We seed id/name on `added` and stitch arg text on each delta;
|
|
113
|
+
// BaseProvider's accumulator keys them by output_index and parses at done.
|
|
114
|
+
case "response.output_item.added": {
|
|
115
|
+
const o = e.item;
|
|
116
|
+
return o && o.type === "function_call" ? { content: "", done: !1, toolCallDelta: { index: e.output_index ?? 0, id: o.call_id, name: o.name, argsTextDelta: "" } } : null;
|
|
117
|
+
}
|
|
118
|
+
case "response.function_call_arguments.delta":
|
|
119
|
+
return { content: "", done: !1, toolCallDelta: { index: e.output_index ?? 0, argsTextDelta: e.delta || "" } };
|
|
120
|
+
case "response.completed":
|
|
121
|
+
return { done: !0, usage: u((t = e.response) == null ? void 0 : t.usage), finishReason: c((n = e.response) == null ? void 0 : n.status) };
|
|
122
|
+
case "response.incomplete":
|
|
123
|
+
case "response.failed":
|
|
124
|
+
return { done: !0, usage: u((r = e.response) == null ? void 0 : r.usage), finishReason: c((i = e.response) == null ? void 0 : i.status) };
|
|
125
|
+
default:
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
class S extends d {
|
|
131
|
+
constructor(e) {
|
|
132
|
+
super(e), this._claude = new b(e), this._chat = new R(e), this._responses = new p(e, "/v1/responses"), this._openaiResponses = new p(e, "/openai/v1/responses"), this._resolved = /* @__PURE__ */ new Map();
|
|
133
|
+
}
|
|
134
|
+
_subs() {
|
|
135
|
+
return [this._claude, this._chat, this._responses, this._openaiResponses];
|
|
136
|
+
}
|
|
137
|
+
// Ordered candidate transports for a model; first that isn't rejected with
|
|
138
|
+
// "does not support" wins and is cached.
|
|
139
|
+
_order(e) {
|
|
140
|
+
const t = e || this.config.model || "";
|
|
141
|
+
if (_(t)) return [this._claude];
|
|
142
|
+
const n = this._resolved.get(t);
|
|
143
|
+
return n ? [n] : m(t) ? [this._openaiResponses, this._responses, this._chat] : [this._chat, this._responses, this._openaiResponses];
|
|
144
|
+
}
|
|
145
|
+
_active() {
|
|
146
|
+
return this._order(this.config.model)[0];
|
|
147
|
+
}
|
|
148
|
+
async initialize() {
|
|
149
|
+
const e = this._active();
|
|
150
|
+
await e.initialize(), this.capabilities = e.capabilities;
|
|
151
|
+
}
|
|
152
|
+
async detectCapabilities() {
|
|
153
|
+
const e = this._active();
|
|
154
|
+
await e.detectCapabilities(), this.capabilities = e.capabilities;
|
|
155
|
+
}
|
|
156
|
+
hasCapability(e) {
|
|
157
|
+
return this._active().hasCapability(e);
|
|
158
|
+
}
|
|
159
|
+
// Defer real preparation to makeRequest/streamRequest so we can fall back
|
|
160
|
+
// across API surfaces (a model's supported API isn't advertised anywhere).
|
|
161
|
+
prepareRequest(e, t) {
|
|
162
|
+
return { __mantle: { messages: e, options: t } };
|
|
163
|
+
}
|
|
164
|
+
async makeRequest(e, t, n) {
|
|
165
|
+
if (e && e.__mantle) {
|
|
166
|
+
const { messages: r, options: i } = e.__mantle;
|
|
167
|
+
return this._runWithFallback((o) => o.makeRequest(o.prepareRequest(r, i), t, n), i == null ? void 0 : i.model);
|
|
168
|
+
}
|
|
169
|
+
return this._subForShape(e).makeRequest(e, t, n);
|
|
170
|
+
}
|
|
171
|
+
async streamRequest(e, t, n) {
|
|
172
|
+
return this._runWithFallback((r) => r.streamRequest(e, t, n), t == null ? void 0 : t.model);
|
|
173
|
+
}
|
|
174
|
+
async _runWithFallback(e, t) {
|
|
175
|
+
const n = t || this.config.model;
|
|
176
|
+
let r;
|
|
177
|
+
for (const i of this._order(n))
|
|
178
|
+
try {
|
|
179
|
+
const o = await e(i);
|
|
180
|
+
return this._resolved.set(n, i), o;
|
|
181
|
+
} catch (o) {
|
|
182
|
+
if (g(o)) {
|
|
183
|
+
r = o;
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
throw y(o);
|
|
187
|
+
}
|
|
188
|
+
throw r || new Error(`No compatible Mantle API for model '${n}'`);
|
|
189
|
+
}
|
|
190
|
+
processResponse(e) {
|
|
191
|
+
return this._subForShape(e).processResponse(e);
|
|
192
|
+
}
|
|
193
|
+
_subForShape(e) {
|
|
194
|
+
return e != null && e.choices ? this._chat : e != null && e.output || e != null && e.status ? this._responses : Array.isArray(e == null ? void 0 : e.content) || e != null && e.stop_reason ? this._claude : this._active();
|
|
195
|
+
}
|
|
196
|
+
async discoverModels(e = 1e4, t) {
|
|
197
|
+
try {
|
|
198
|
+
const n = await this._chat.discoverModels(e, t);
|
|
199
|
+
return n.length ? n : l.slice();
|
|
200
|
+
} catch {
|
|
201
|
+
return l.slice();
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
cancelAllRequests() {
|
|
205
|
+
for (const e of this._subs()) e.cancelAllRequests();
|
|
206
|
+
}
|
|
207
|
+
cancelRequest(e) {
|
|
208
|
+
for (const t of this._subs()) t.cancelRequest(e);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
function v(s) {
|
|
212
|
+
const e = [];
|
|
213
|
+
for (const t of s) {
|
|
214
|
+
if (t.role === "tool") {
|
|
215
|
+
e.push({ type: "function_call_output", call_id: t.tool_call_id, output: String(t.content ?? "") });
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
if (t.role === "assistant" && Array.isArray(t.tool_calls) && t.tool_calls.length) {
|
|
219
|
+
t.content && e.push({ role: "assistant", content: t.content });
|
|
220
|
+
for (const n of t.tool_calls)
|
|
221
|
+
e.push({
|
|
222
|
+
type: "function_call",
|
|
223
|
+
call_id: n.id,
|
|
224
|
+
name: n.name,
|
|
225
|
+
arguments: typeof n.args == "string" ? n.args : JSON.stringify(n.args || {})
|
|
226
|
+
});
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
e.push({ role: t.role, content: t.content });
|
|
230
|
+
}
|
|
231
|
+
return e;
|
|
232
|
+
}
|
|
233
|
+
function x(s) {
|
|
234
|
+
if (!(!Array.isArray(s) || !s.length))
|
|
235
|
+
return s.map((e) => {
|
|
236
|
+
if (e && e.type === "function" && e.function) {
|
|
237
|
+
const t = e.function;
|
|
238
|
+
return { type: "function", name: t.name, description: t.description, parameters: t.parameters };
|
|
239
|
+
}
|
|
240
|
+
return e;
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
function c(s) {
|
|
244
|
+
return s === "completed" ? "stop" : s === "incomplete" ? "length" : s || null;
|
|
245
|
+
}
|
|
246
|
+
function u(s) {
|
|
247
|
+
var o, a;
|
|
248
|
+
if (!s) return null;
|
|
249
|
+
const e = s.input_tokens ?? 0, t = s.output_tokens ?? 0, n = { inputTokens: e, outputTokens: t, totalTokens: s.total_tokens ?? e + t, raw: s }, r = (o = s.input_tokens_details) == null ? void 0 : o.cached_tokens, i = (a = s.output_tokens_details) == null ? void 0 : a.reasoning_tokens;
|
|
250
|
+
return r != null && (n.cachedInputTokens = r), i != null && (n.reasoningTokens = i), n;
|
|
251
|
+
}
|
|
252
|
+
function A(s) {
|
|
253
|
+
if (!s) return {};
|
|
254
|
+
try {
|
|
255
|
+
return JSON.parse(s);
|
|
256
|
+
} catch {
|
|
257
|
+
return { __parseError: !0, raw: s };
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
export {
|
|
261
|
+
l as BEDROCK_MANTLE_CLAUDE_MODELS,
|
|
262
|
+
S as BedrockMantleProvider,
|
|
263
|
+
_ as isMantleClaudeModel,
|
|
264
|
+
u as normalizeResponsesUsage
|
|
265
|
+
};
|
|
@@ -11,9 +11,9 @@ const p = [
|
|
|
11
11
|
"us.anthropic.claude-opus-4-1-20250805-v1:0"
|
|
12
12
|
];
|
|
13
13
|
class A extends w {
|
|
14
|
-
prepareRequest(
|
|
15
|
-
const
|
|
16
|
-
return delete
|
|
14
|
+
prepareRequest(r, n) {
|
|
15
|
+
const t = super.prepareRequest(r, n);
|
|
16
|
+
return delete t.stream, t.anthropic_version = "bedrock-2023-05-31", t;
|
|
17
17
|
}
|
|
18
18
|
buildHeaders() {
|
|
19
19
|
return y.prototype.buildHeaders.call(this);
|
|
@@ -27,17 +27,17 @@ class A extends w {
|
|
|
27
27
|
requiresAuth() {
|
|
28
28
|
return !!this.config.apiKey;
|
|
29
29
|
}
|
|
30
|
-
async makeRequest(n, t
|
|
31
|
-
const e =
|
|
30
|
+
async makeRequest(r, n, t) {
|
|
31
|
+
const e = r.model, d = { ...r };
|
|
32
32
|
delete d.model;
|
|
33
|
-
const i = `${this.config.baseUrl}/model/${encodeURIComponent(e)}/invoke`, l =
|
|
34
|
-
|
|
33
|
+
const i = `${this.config.baseUrl}/model/${encodeURIComponent(e)}/invoke`, l = n ? { signal: n } : new AbortController();
|
|
34
|
+
n || (t = t || this.generateRequestId(), this.activeRequests.set(t, l));
|
|
35
35
|
try {
|
|
36
36
|
const o = await fetch(i, {
|
|
37
37
|
method: "POST",
|
|
38
38
|
headers: this.buildHeaders(),
|
|
39
39
|
body: JSON.stringify(d),
|
|
40
|
-
signal: l.signal ||
|
|
40
|
+
signal: l.signal || n
|
|
41
41
|
});
|
|
42
42
|
if (!o.ok) {
|
|
43
43
|
const h = await o.text();
|
|
@@ -45,17 +45,17 @@ class A extends w {
|
|
|
45
45
|
}
|
|
46
46
|
return o.json();
|
|
47
47
|
} finally {
|
|
48
|
-
!
|
|
48
|
+
!n && t && this.activeRequests.delete(t);
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
|
-
async makeStreamingRequest(
|
|
52
|
-
const
|
|
51
|
+
async makeStreamingRequest(r, n) {
|
|
52
|
+
const t = r.model, e = { ...r };
|
|
53
53
|
delete e.model;
|
|
54
|
-
const d = `${this.config.baseUrl}/model/${encodeURIComponent(
|
|
54
|
+
const d = `${this.config.baseUrl}/model/${encodeURIComponent(t)}/invoke-with-response-stream`, i = await fetch(d, {
|
|
55
55
|
method: "POST",
|
|
56
56
|
headers: this.buildHeaders(),
|
|
57
57
|
body: JSON.stringify(e),
|
|
58
|
-
signal:
|
|
58
|
+
signal: n
|
|
59
59
|
});
|
|
60
60
|
if (!i.ok) {
|
|
61
61
|
const c = await i.text();
|
|
@@ -96,11 +96,15 @@ class A extends w {
|
|
|
96
96
|
// the control-plane host returns permissive CORS headers, so a browser fetch
|
|
97
97
|
// works. When disabled (default) or on any failure, fall back to the static
|
|
98
98
|
// list — existing apps keep working with zero surprise network calls.
|
|
99
|
-
|
|
100
|
-
|
|
99
|
+
// `force` is set when the user explicitly clicks Refresh in the UI; it
|
|
100
|
+
// bypasses the opt-in `liveModelDiscovery` gate so the button actually queries
|
|
101
|
+
// the control plane. Automatic loads (provider change / edit) leave `force`
|
|
102
|
+
// unset and keep the "no surprise network calls" default.
|
|
103
|
+
async discoverModels(r = 1e4, { force: n = !1 } = {}) {
|
|
104
|
+
if (!n && !this.config.liveModelDiscovery || !this.getModelsEndpoint())
|
|
101
105
|
return p.slice();
|
|
102
106
|
try {
|
|
103
|
-
const t = await super.discoverModels(
|
|
107
|
+
const t = await super.discoverModels(r);
|
|
104
108
|
return t.length ? t : p.slice();
|
|
105
109
|
} catch {
|
|
106
110
|
return p.slice();
|
|
@@ -112,32 +116,32 @@ class A extends w {
|
|
|
112
116
|
// no distinct control-plane host can be derived (custom/relative baseUrl),
|
|
113
117
|
// which disables live discovery and leaves the static fallback in place.
|
|
114
118
|
getModelsEndpoint() {
|
|
115
|
-
const
|
|
116
|
-
return !
|
|
119
|
+
const r = this.config.baseUrl || "", n = r.replace("bedrock-runtime.", "bedrock.");
|
|
120
|
+
return !n || n === r ? null : `${n}/inference-profiles`;
|
|
117
121
|
}
|
|
118
122
|
// ListInferenceProfiles returns
|
|
119
123
|
// { inferenceProfileSummaries: [{ inferenceProfileId, ... }] }
|
|
120
124
|
// Keep only Anthropic Claude profiles (us.* and global.*) — those ids are
|
|
121
125
|
// exactly what InvokeModel expects in the URL path.
|
|
122
|
-
parseModelsResponse(
|
|
123
|
-
const
|
|
124
|
-
if (!Array.isArray(
|
|
125
|
-
const
|
|
126
|
-
return
|
|
126
|
+
parseModelsResponse(r) {
|
|
127
|
+
const n = r == null ? void 0 : r.inferenceProfileSummaries;
|
|
128
|
+
if (!Array.isArray(n)) return p.slice();
|
|
129
|
+
const t = n.map((e) => e == null ? void 0 : e.inferenceProfileId).filter((e) => typeof e == "string" && e.includes("anthropic.claude"));
|
|
130
|
+
return t.length ? t : p.slice();
|
|
127
131
|
}
|
|
128
132
|
}
|
|
129
133
|
class v {
|
|
130
134
|
constructor() {
|
|
131
135
|
this.buf = new Uint8Array(0), this.textDecoder = new TextDecoder();
|
|
132
136
|
}
|
|
133
|
-
feed(
|
|
134
|
-
if (
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
+
feed(r) {
|
|
138
|
+
if (r && r.length) {
|
|
139
|
+
const t = new Uint8Array(this.buf.length + r.length);
|
|
140
|
+
t.set(this.buf, 0), t.set(r, this.buf.length), this.buf = t;
|
|
137
141
|
}
|
|
138
|
-
const
|
|
142
|
+
const n = [];
|
|
139
143
|
for (; this.buf.length >= 12; ) {
|
|
140
|
-
const
|
|
144
|
+
const t = new DataView(this.buf.buffer, this.buf.byteOffset, this.buf.byteLength), e = t.getUint32(0, !1), d = t.getUint32(4, !1);
|
|
141
145
|
if (e < 16 || e > 16 * 1024 * 1024)
|
|
142
146
|
throw new Error(`Bedrock event stream: invalid frame length ${e}`);
|
|
143
147
|
if (this.buf.length < e) break;
|
|
@@ -152,43 +156,43 @@ class v {
|
|
|
152
156
|
const g = f[":message-type"], m = f[":exception-type"];
|
|
153
157
|
if (g === "exception" || m) {
|
|
154
158
|
const u = m || f[":event-type"] || "BedrockStreamException", b = (a == null ? void 0 : a.message) || c || "unknown error";
|
|
155
|
-
|
|
159
|
+
n.push({ kind: "error", message: `${u}: ${b}` });
|
|
156
160
|
continue;
|
|
157
161
|
}
|
|
158
162
|
if (a && typeof a.bytes == "string")
|
|
159
163
|
try {
|
|
160
164
|
const u = x(a.bytes), b = JSON.parse(u);
|
|
161
|
-
|
|
165
|
+
n.push({ kind: "event", payload: b });
|
|
162
166
|
} catch (u) {
|
|
163
|
-
|
|
167
|
+
n.push({ kind: "error", message: `Bedrock event-stream decode failed: ${u.message}` });
|
|
164
168
|
}
|
|
165
169
|
}
|
|
166
|
-
return
|
|
170
|
+
return n;
|
|
167
171
|
}
|
|
168
172
|
}
|
|
169
173
|
function k(s) {
|
|
170
|
-
const
|
|
174
|
+
const r = {}, n = new DataView(s.buffer, s.byteOffset, s.byteLength), t = new TextDecoder();
|
|
171
175
|
let e = 0;
|
|
172
176
|
for (; e < s.length; ) {
|
|
173
177
|
const d = s[e];
|
|
174
178
|
if (e += 1, e + d > s.length) break;
|
|
175
|
-
const i =
|
|
179
|
+
const i = t.decode(s.subarray(e, e + d));
|
|
176
180
|
if (e += d, e >= s.length) break;
|
|
177
181
|
const l = s[e];
|
|
178
182
|
if (e += 1, l === 7) {
|
|
179
183
|
if (e + 2 > s.length) break;
|
|
180
|
-
const o =
|
|
184
|
+
const o = n.getUint16(e, !1);
|
|
181
185
|
if (e += 2, e + o > s.length) break;
|
|
182
|
-
|
|
186
|
+
r[i] = t.decode(s.subarray(e, e + o)), e += o;
|
|
183
187
|
} else
|
|
184
188
|
break;
|
|
185
189
|
}
|
|
186
|
-
return
|
|
190
|
+
return r;
|
|
187
191
|
}
|
|
188
192
|
function x(s) {
|
|
189
|
-
const
|
|
190
|
-
for (let
|
|
191
|
-
return new TextDecoder().decode(
|
|
193
|
+
const r = atob(s), n = new Uint8Array(r.length);
|
|
194
|
+
for (let t = 0; t < r.length; t++) n[t] = r.charCodeAt(t);
|
|
195
|
+
return new TextDecoder().decode(n);
|
|
192
196
|
}
|
|
193
197
|
export {
|
|
194
198
|
p as BEDROCK_CLAUDE_MODELS,
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import { OpenAIProvider as
|
|
2
|
-
import { AnthropicProvider as
|
|
3
|
-
import { BedrockProvider as
|
|
1
|
+
import { OpenAIProvider as o } from "./OpenAIProvider.js";
|
|
2
|
+
import { AnthropicProvider as n } from "./AnthropicProvider.js";
|
|
3
|
+
import { BedrockProvider as i } from "./BedrockProvider.js";
|
|
4
|
+
import { BedrockMantleProvider as p } from "./BedrockMantleProvider.js";
|
|
4
5
|
import { GrokProvider as m } from "./GrokProvider.js";
|
|
5
|
-
import { GeminiProvider as
|
|
6
|
-
import { OllamaProvider as
|
|
6
|
+
import { GeminiProvider as u } from "./GeminiProvider.js";
|
|
7
|
+
import { OllamaProvider as l } from "./OllamaProvider.js";
|
|
7
8
|
import { LlamaServerProvider as E } from "./LlamaServerProvider.js";
|
|
8
|
-
import { OpenRouterProvider as
|
|
9
|
-
import { DeepSeekProvider as
|
|
10
|
-
import { CustomProvider as
|
|
11
|
-
const
|
|
9
|
+
import { OpenRouterProvider as A } from "./OpenRouterProvider.js";
|
|
10
|
+
import { DeepSeekProvider as O } from "./DeepSeekProvider.js";
|
|
11
|
+
import { CustomProvider as c } from "./CustomProvider.js";
|
|
12
|
+
const r = {
|
|
12
13
|
OPENAI: "openai",
|
|
13
14
|
ANTHROPIC: "anthropic",
|
|
14
15
|
BEDROCK: "bedrock",
|
|
@@ -20,60 +21,93 @@ const e = {
|
|
|
20
21
|
DEEPSEEK: "deepseek",
|
|
21
22
|
CUSTOM: "custom"
|
|
22
23
|
}, S = {
|
|
23
|
-
[
|
|
24
|
-
[
|
|
25
|
-
[
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
24
|
+
[r.OPENAI]: { name: "OpenAI", baseUrl: "https://api.openai.com", requiresApiKey: !0 },
|
|
25
|
+
[r.ANTHROPIC]: { name: "Anthropic", baseUrl: "https://api.anthropic.com", requiresApiKey: !0 },
|
|
26
|
+
[r.BEDROCK]: {
|
|
27
|
+
name: "AWS",
|
|
28
|
+
// Two backends, chosen by a UI toggle. Mantle (default) serves the whole
|
|
29
|
+
// catalog behind one model list via BedrockMantleProvider's auto-routing
|
|
30
|
+
// (Claude -> Anthropic Messages, the rest -> OpenAI Chat Completions).
|
|
31
|
+
// Bedrock is the classic Claude-only bedrock-runtime transport. `baseUrl`
|
|
32
|
+
// is derived from (backend, region) via the templates below, and
|
|
33
|
+
// `createProvider` picks the class from `config.backend`. The region list
|
|
34
|
+
// is valid on both.
|
|
35
|
+
baseUrl: "https://bedrock-mantle.us-east-1.api.aws",
|
|
36
|
+
baseUrlTemplate: "https://bedrock-mantle.{region}.api.aws",
|
|
37
|
+
defaultBackend: "mantle",
|
|
38
|
+
backends: {
|
|
39
|
+
mantle: { label: "Mantle", baseUrlTemplate: "https://bedrock-mantle.{region}.api.aws" },
|
|
40
|
+
runtime: { label: "Bedrock", baseUrlTemplate: "https://bedrock-runtime.{region}.amazonaws.com" }
|
|
41
|
+
},
|
|
42
|
+
regions: [
|
|
43
|
+
"us-east-1",
|
|
44
|
+
"us-east-2",
|
|
45
|
+
"us-west-2",
|
|
46
|
+
"eu-central-1",
|
|
47
|
+
"eu-west-1",
|
|
48
|
+
"eu-west-2",
|
|
49
|
+
"eu-north-1",
|
|
50
|
+
"eu-south-1",
|
|
51
|
+
"ap-northeast-1",
|
|
52
|
+
"ap-south-1",
|
|
53
|
+
"ap-southeast-2",
|
|
54
|
+
"ap-southeast-3",
|
|
55
|
+
"sa-east-1"
|
|
56
|
+
],
|
|
57
|
+
defaultRegion: "us-east-1",
|
|
58
|
+
requiresApiKey: !0
|
|
59
|
+
},
|
|
60
|
+
[r.GROK]: { name: "Grok", baseUrl: "https://api.x.ai", requiresApiKey: !0 },
|
|
61
|
+
[r.GEMINI]: { name: "Google Gemini", baseUrl: "https://generativelanguage.googleapis.com", requiresApiKey: !0 },
|
|
62
|
+
[r.OLLAMA]: { name: "Ollama (Native)", baseUrl: "http://localhost:11434", requiresApiKey: !1 },
|
|
63
|
+
[r.LLAMA_SERVER]: { name: "Local Llama Server", baseUrl: "http://localhost:8080", requiresApiKey: !1 },
|
|
64
|
+
[r.OPENROUTER]: { name: "OpenRouter", baseUrl: "https://openrouter.ai/api", requiresApiKey: !0 },
|
|
65
|
+
[r.DEEPSEEK]: { name: "DeepSeek", baseUrl: "https://api.deepseek.com", requiresApiKey: !0 },
|
|
66
|
+
[r.CUSTOM]: { name: "Custom OpenAI Compatible", baseUrl: "", requiresApiKey: !1 }
|
|
33
67
|
};
|
|
34
|
-
function
|
|
68
|
+
function d(t, e) {
|
|
35
69
|
switch (t) {
|
|
36
|
-
case
|
|
37
|
-
return new
|
|
38
|
-
case
|
|
39
|
-
return new
|
|
40
|
-
case
|
|
41
|
-
return new
|
|
42
|
-
case
|
|
43
|
-
return new m(
|
|
44
|
-
case
|
|
45
|
-
return new
|
|
46
|
-
case
|
|
47
|
-
return new
|
|
48
|
-
case
|
|
49
|
-
return new E(
|
|
50
|
-
case
|
|
51
|
-
return new
|
|
52
|
-
case
|
|
53
|
-
return new
|
|
54
|
-
case
|
|
55
|
-
return new
|
|
70
|
+
case r.OPENAI:
|
|
71
|
+
return new o(e);
|
|
72
|
+
case r.ANTHROPIC:
|
|
73
|
+
return new n(e);
|
|
74
|
+
case r.BEDROCK:
|
|
75
|
+
return (e == null ? void 0 : e.backend) === "runtime" ? new i(e) : new p(e);
|
|
76
|
+
case r.GROK:
|
|
77
|
+
return new m(e);
|
|
78
|
+
case r.GEMINI:
|
|
79
|
+
return new u(e);
|
|
80
|
+
case r.OLLAMA:
|
|
81
|
+
return new l(e);
|
|
82
|
+
case r.LLAMA_SERVER:
|
|
83
|
+
return new E(e);
|
|
84
|
+
case r.OPENROUTER:
|
|
85
|
+
return new A(e);
|
|
86
|
+
case r.DEEPSEEK:
|
|
87
|
+
return new O(e);
|
|
88
|
+
case r.CUSTOM:
|
|
89
|
+
return new c(e);
|
|
56
90
|
default:
|
|
57
91
|
throw new Error(`Unknown provider type: ${t}`);
|
|
58
92
|
}
|
|
59
93
|
}
|
|
60
94
|
const a = /* @__PURE__ */ new Map();
|
|
61
|
-
function
|
|
62
|
-
if (typeof t != "string" || !
|
|
95
|
+
function M(t, e) {
|
|
96
|
+
if (typeof t != "string" || !e)
|
|
63
97
|
throw new Error("registerProvider requires a type string and a class reference");
|
|
64
|
-
a.set(t,
|
|
98
|
+
a.set(t, e);
|
|
65
99
|
}
|
|
66
|
-
function N(t,
|
|
100
|
+
function N(t, e) {
|
|
67
101
|
if (a.has(t)) {
|
|
68
|
-
const
|
|
69
|
-
return new
|
|
102
|
+
const s = a.get(t);
|
|
103
|
+
return new s(e);
|
|
70
104
|
}
|
|
71
|
-
return
|
|
105
|
+
return d(t, e);
|
|
72
106
|
}
|
|
73
107
|
export {
|
|
74
108
|
S as DEFAULT_CONFIGS,
|
|
75
|
-
|
|
76
|
-
|
|
109
|
+
r as PROVIDERS,
|
|
110
|
+
d as createProvider,
|
|
77
111
|
N as createProviderFlexible,
|
|
78
|
-
|
|
112
|
+
M as registerProvider
|
|
79
113
|
};
|