@anvaka/vue-llm 0.3.1 → 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 +20 -3
- package/dist/providers/AnthropicProvider.js +17 -13
- package/dist/providers/BedrockMantleProvider.js +265 -0
- package/dist/providers/BedrockProvider.js +87 -59
- 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
|
@@ -26,20 +26,37 @@ const t = {
|
|
|
26
26
|
"gpt-5-codex": { input: 1.25, output: 10, cachedInput: 0.125 }
|
|
27
27
|
},
|
|
28
28
|
anthropic: {
|
|
29
|
+
"claude-opus-4-8": { input: 5, output: 25, cachedInput: 0.5, cacheCreation: 6.25 },
|
|
29
30
|
"claude-opus-4-7": { input: 5, output: 25, cachedInput: 0.5, cacheCreation: 6.25 },
|
|
30
31
|
"claude-opus-4-6": { input: 5, output: 25, cachedInput: 0.5, cacheCreation: 6.25 },
|
|
31
32
|
"claude-sonnet-4-6": { input: 3, output: 15, cachedInput: 0.3, cacheCreation: 3.75 },
|
|
32
33
|
"claude-sonnet-4-5": { input: 3, output: 15, cachedInput: 0.3, cacheCreation: 3.75 },
|
|
33
34
|
"claude-haiku-4-5": { input: 1, output: 5, cachedInput: 0.1, cacheCreation: 1.25 }
|
|
34
35
|
},
|
|
35
|
-
// Bedrock charges the same per-token rates as Anthropic direct for the
|
|
36
|
-
//
|
|
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.
|
|
37
42
|
bedrock: {
|
|
43
|
+
"us.anthropic.claude-opus-4-8": { input: 5, output: 25, cachedInput: 0.5, cacheCreation: 6.25 },
|
|
38
44
|
"us.anthropic.claude-opus-4-7": { input: 5, output: 25, cachedInput: 0.5, cacheCreation: 6.25 },
|
|
39
45
|
"us.anthropic.claude-opus-4-6": { input: 5, output: 25, cachedInput: 0.5, cacheCreation: 6.25 },
|
|
40
46
|
"us.anthropic.claude-sonnet-4-6": { input: 3, output: 15, cachedInput: 0.3, cacheCreation: 3.75 },
|
|
41
47
|
"us.anthropic.claude-sonnet-4-5": { input: 3, output: 15, cachedInput: 0.3, cacheCreation: 3.75 },
|
|
42
|
-
"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 }
|
|
43
60
|
},
|
|
44
61
|
gemini: {
|
|
45
62
|
// Gemini 3.x — current lineup (May 2026)
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { BaseProvider as g } from "./BaseProvider.js";
|
|
2
|
-
class
|
|
2
|
+
class b extends g {
|
|
3
3
|
async detectCapabilities() {
|
|
4
4
|
var t, e, o, s;
|
|
5
5
|
((t = this.config.model) != null && t.includes("claude-3") || (e = this.config.model) != null && e.includes("claude-sonnet") || (o = this.config.model) != null && o.includes("claude-opus") || (s = this.config.model) != null && s.includes("claude-haiku")) && (this.capabilities.add("vision"), this.capabilities.add("tools"));
|
|
6
6
|
}
|
|
7
7
|
prepareRequest(t, e) {
|
|
8
|
-
const o =
|
|
8
|
+
const o = m(t), s = e.model || this.config.model || "claude-3-sonnet-20240229", r = {
|
|
9
9
|
model: s,
|
|
10
10
|
max_tokens: e.maxTokens || 1e3,
|
|
11
11
|
messages: o,
|
|
12
12
|
stream: e.stream || !1
|
|
13
13
|
};
|
|
14
|
-
s
|
|
14
|
+
_(s) || (r.temperature = e.temperature ?? 0.7);
|
|
15
15
|
const c = t.find((i) => i.role === "system");
|
|
16
|
-
return c && (r.system = c.content), e.tools && this.capabilities.has("tools") && (r.tools =
|
|
16
|
+
return c && (r.system = c.content), e.tools && this.capabilities.has("tools") && (r.tools = y(e.tools), e.tool_choice && (r.tool_choice = e.tool_choice)), this.promptCachingEnabled(e) && k(r, e), r;
|
|
17
17
|
}
|
|
18
18
|
// Prompt caching is on by default for the Claude/Anthropic-family path
|
|
19
19
|
// (Bedrock inherits this method) because it's strictly cheaper and degrades
|
|
@@ -106,6 +106,9 @@ class C extends g {
|
|
|
106
106
|
return ((e = t.data) == null ? void 0 : e.map((o) => o.id)) || [];
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
|
+
function _(n) {
|
|
110
|
+
return /claude-opus-4-[789]/.test(n);
|
|
111
|
+
}
|
|
109
112
|
function h(n) {
|
|
110
113
|
if (!n) return null;
|
|
111
114
|
const t = String(n).toLowerCase();
|
|
@@ -116,7 +119,7 @@ function f(n) {
|
|
|
116
119
|
const t = n.input_tokens ?? 0, e = n.cache_read_input_tokens ?? 0, o = n.cache_creation_input_tokens ?? 0, r = n.input_tokens != null || n.cache_read_input_tokens != null || n.cache_creation_input_tokens != null ? t + e + o : 0, c = n.output_tokens ?? 0, i = { inputTokens: r, outputTokens: c, totalTokens: r + c, raw: n };
|
|
117
120
|
return n.cache_read_input_tokens != null && (i.cachedInputTokens = e), n.cache_creation_input_tokens != null && (i.cacheCreationInputTokens = o), i;
|
|
118
121
|
}
|
|
119
|
-
function
|
|
122
|
+
function m(n) {
|
|
120
123
|
const t = [];
|
|
121
124
|
for (const e of n)
|
|
122
125
|
if (e.role !== "system") {
|
|
@@ -148,7 +151,7 @@ function _(n) {
|
|
|
148
151
|
}
|
|
149
152
|
return t;
|
|
150
153
|
}
|
|
151
|
-
function
|
|
154
|
+
function y(n) {
|
|
152
155
|
return n.map((t) => t.type === "function" && t.function ? {
|
|
153
156
|
name: t.function.name,
|
|
154
157
|
description: t.function.description,
|
|
@@ -156,9 +159,9 @@ function m(n) {
|
|
|
156
159
|
} : t);
|
|
157
160
|
}
|
|
158
161
|
const l = { type: "ephemeral" };
|
|
159
|
-
function
|
|
162
|
+
function k(n, t) {
|
|
160
163
|
if (n.system)
|
|
161
|
-
n.system =
|
|
164
|
+
n.system = x(n.system);
|
|
162
165
|
else if (Array.isArray(n.tools) && n.tools.length) {
|
|
163
166
|
const e = n.tools.length - 1;
|
|
164
167
|
n.tools[e] = { ...n.tools[e], cache_control: l };
|
|
@@ -167,12 +170,12 @@ function y(n, t) {
|
|
|
167
170
|
const e = n.messages;
|
|
168
171
|
if (Array.isArray(e) && e.length) {
|
|
169
172
|
const o = e[e.length - 1];
|
|
170
|
-
o.content =
|
|
173
|
+
o.content = A(o.content);
|
|
171
174
|
}
|
|
172
175
|
}
|
|
173
176
|
return n;
|
|
174
177
|
}
|
|
175
|
-
function
|
|
178
|
+
function x(n) {
|
|
176
179
|
let t;
|
|
177
180
|
if (typeof n == "string")
|
|
178
181
|
t = [{ type: "text", text: n }];
|
|
@@ -182,7 +185,7 @@ function k(n) {
|
|
|
182
185
|
return n;
|
|
183
186
|
return t[t.length - 1] = { ...t[t.length - 1], cache_control: l }, t;
|
|
184
187
|
}
|
|
185
|
-
function
|
|
188
|
+
function A(n) {
|
|
186
189
|
if (typeof n == "string")
|
|
187
190
|
return [{ type: "text", text: n, cache_control: l }];
|
|
188
191
|
if (Array.isArray(n) && n.length) {
|
|
@@ -192,6 +195,7 @@ function x(n) {
|
|
|
192
195
|
return n;
|
|
193
196
|
}
|
|
194
197
|
export {
|
|
195
|
-
|
|
196
|
-
f as normalizeAnthropicUsage
|
|
198
|
+
b as AnthropicProvider,
|
|
199
|
+
f as normalizeAnthropicUsage,
|
|
200
|
+
_ as samplingParamsRemoved
|
|
197
201
|
};
|
|
@@ -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
|
+
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { BaseProvider as y } from "./BaseProvider.js";
|
|
2
2
|
import { AnthropicProvider as w } from "./AnthropicProvider.js";
|
|
3
|
-
const
|
|
3
|
+
const p = [
|
|
4
|
+
"us.anthropic.claude-opus-4-8",
|
|
4
5
|
"us.anthropic.claude-opus-4-7",
|
|
5
6
|
"us.anthropic.claude-sonnet-4-6",
|
|
6
7
|
"us.anthropic.claude-haiku-4-5-20251001-v1:0",
|
|
@@ -9,7 +10,7 @@ const g = [
|
|
|
9
10
|
"us.anthropic.claude-sonnet-4-5-20250929-v1:0",
|
|
10
11
|
"us.anthropic.claude-opus-4-1-20250805-v1:0"
|
|
11
12
|
];
|
|
12
|
-
class
|
|
13
|
+
class A extends w {
|
|
13
14
|
prepareRequest(r, n) {
|
|
14
15
|
const t = super.prepareRequest(r, n);
|
|
15
16
|
return delete t.stream, t.anthropic_version = "bedrock-2023-05-31", t;
|
|
@@ -29,20 +30,20 @@ class R extends w {
|
|
|
29
30
|
async makeRequest(r, n, t) {
|
|
30
31
|
const e = r.model, d = { ...r };
|
|
31
32
|
delete d.model;
|
|
32
|
-
const
|
|
33
|
-
n || (t = t || this.generateRequestId(), this.activeRequests.set(t,
|
|
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));
|
|
34
35
|
try {
|
|
35
|
-
const
|
|
36
|
+
const o = await fetch(i, {
|
|
36
37
|
method: "POST",
|
|
37
38
|
headers: this.buildHeaders(),
|
|
38
39
|
body: JSON.stringify(d),
|
|
39
|
-
signal:
|
|
40
|
+
signal: l.signal || n
|
|
40
41
|
});
|
|
41
|
-
if (!
|
|
42
|
-
const
|
|
43
|
-
throw new Error(`Bedrock API Error (${
|
|
42
|
+
if (!o.ok) {
|
|
43
|
+
const h = await o.text();
|
|
44
|
+
throw new Error(`Bedrock API Error (${o.status}): ${h}`);
|
|
44
45
|
}
|
|
45
|
-
return
|
|
46
|
+
return o.json();
|
|
46
47
|
} finally {
|
|
47
48
|
!n && t && this.activeRequests.delete(t);
|
|
48
49
|
}
|
|
@@ -50,56 +51,83 @@ class R extends w {
|
|
|
50
51
|
async makeStreamingRequest(r, n) {
|
|
51
52
|
const t = r.model, e = { ...r };
|
|
52
53
|
delete e.model;
|
|
53
|
-
const d = `${this.config.baseUrl}/model/${encodeURIComponent(t)}/invoke-with-response-stream`,
|
|
54
|
+
const d = `${this.config.baseUrl}/model/${encodeURIComponent(t)}/invoke-with-response-stream`, i = await fetch(d, {
|
|
54
55
|
method: "POST",
|
|
55
56
|
headers: this.buildHeaders(),
|
|
56
57
|
body: JSON.stringify(e),
|
|
57
58
|
signal: n
|
|
58
59
|
});
|
|
59
|
-
if (!
|
|
60
|
-
const
|
|
61
|
-
throw new Error(`Bedrock API Error (${
|
|
60
|
+
if (!i.ok) {
|
|
61
|
+
const c = await i.text();
|
|
62
|
+
throw new Error(`Bedrock API Error (${i.status}): ${c}`);
|
|
62
63
|
}
|
|
63
|
-
const
|
|
64
|
-
async pull(
|
|
64
|
+
const l = i.body.getReader(), o = new v(), h = new TextEncoder(), f = new ReadableStream({
|
|
65
|
+
async pull(c) {
|
|
65
66
|
try {
|
|
66
|
-
const { done: a, value:
|
|
67
|
+
const { done: a, value: g } = await l.read();
|
|
67
68
|
if (a) {
|
|
68
|
-
|
|
69
|
+
c.close();
|
|
69
70
|
return;
|
|
70
71
|
}
|
|
71
|
-
const
|
|
72
|
-
for (const
|
|
73
|
-
if (
|
|
74
|
-
|
|
72
|
+
const m = o.feed(g);
|
|
73
|
+
for (const u of m) {
|
|
74
|
+
if (u.kind === "error") {
|
|
75
|
+
c.error(new Error(u.message));
|
|
75
76
|
return;
|
|
76
77
|
}
|
|
77
|
-
|
|
78
|
+
c.enqueue(h.encode(`data: ${JSON.stringify(u.payload)}
|
|
78
79
|
`));
|
|
79
80
|
}
|
|
80
81
|
} catch (a) {
|
|
81
|
-
|
|
82
|
+
c.error(a);
|
|
82
83
|
}
|
|
83
84
|
},
|
|
84
|
-
cancel(
|
|
85
|
+
cancel(c) {
|
|
85
86
|
try {
|
|
86
|
-
|
|
87
|
+
l.cancel(c);
|
|
87
88
|
} catch {
|
|
88
89
|
}
|
|
89
90
|
}
|
|
90
91
|
});
|
|
91
92
|
return new Response(f, { status: 200, headers: { "Content-Type": "text/event-stream" } });
|
|
92
93
|
}
|
|
93
|
-
//
|
|
94
|
-
//
|
|
95
|
-
|
|
96
|
-
|
|
94
|
+
// Live discovery is opt-in via config.liveModelDiscovery. A Bedrock API key
|
|
95
|
+
// authenticates against the control plane with Bearer auth (no SigV4), and
|
|
96
|
+
// the control-plane host returns permissive CORS headers, so a browser fetch
|
|
97
|
+
// works. When disabled (default) or on any failure, fall back to the static
|
|
98
|
+
// list — existing apps keep working with zero surprise network calls.
|
|
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())
|
|
105
|
+
return p.slice();
|
|
106
|
+
try {
|
|
107
|
+
const t = await super.discoverModels(r);
|
|
108
|
+
return t.length ? t : p.slice();
|
|
109
|
+
} catch {
|
|
110
|
+
return p.slice();
|
|
111
|
+
}
|
|
97
112
|
}
|
|
113
|
+
// The control plane lives at the same host with the `-runtime` segment
|
|
114
|
+
// stripped: bedrock-runtime.{region}.amazonaws.com (invoke) ->
|
|
115
|
+
// bedrock.{region}.amazonaws.com (ListInferenceProfiles). Returns null when
|
|
116
|
+
// no distinct control-plane host can be derived (custom/relative baseUrl),
|
|
117
|
+
// which disables live discovery and leaves the static fallback in place.
|
|
98
118
|
getModelsEndpoint() {
|
|
99
|
-
|
|
119
|
+
const r = this.config.baseUrl || "", n = r.replace("bedrock-runtime.", "bedrock.");
|
|
120
|
+
return !n || n === r ? null : `${n}/inference-profiles`;
|
|
100
121
|
}
|
|
101
|
-
|
|
102
|
-
|
|
122
|
+
// ListInferenceProfiles returns
|
|
123
|
+
// { inferenceProfileSummaries: [{ inferenceProfileId, ... }] }
|
|
124
|
+
// Keep only Anthropic Claude profiles (us.* and global.*) — those ids are
|
|
125
|
+
// exactly what InvokeModel expects in the URL path.
|
|
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();
|
|
103
131
|
}
|
|
104
132
|
}
|
|
105
133
|
class v {
|
|
@@ -117,56 +145,56 @@ class v {
|
|
|
117
145
|
if (e < 16 || e > 16 * 1024 * 1024)
|
|
118
146
|
throw new Error(`Bedrock event stream: invalid frame length ${e}`);
|
|
119
147
|
if (this.buf.length < e) break;
|
|
120
|
-
const
|
|
148
|
+
const i = 12, l = i + d, o = l, h = e - 4, f = k(this.buf.subarray(i, l)), c = this.textDecoder.decode(this.buf.subarray(o, h));
|
|
121
149
|
this.buf = this.buf.subarray(e);
|
|
122
150
|
let a;
|
|
123
151
|
try {
|
|
124
|
-
a = JSON.parse(
|
|
152
|
+
a = JSON.parse(c);
|
|
125
153
|
} catch {
|
|
126
154
|
a = null;
|
|
127
155
|
}
|
|
128
|
-
const
|
|
129
|
-
if (
|
|
130
|
-
const
|
|
131
|
-
n.push({ kind: "error", message: `${
|
|
156
|
+
const g = f[":message-type"], m = f[":exception-type"];
|
|
157
|
+
if (g === "exception" || m) {
|
|
158
|
+
const u = m || f[":event-type"] || "BedrockStreamException", b = (a == null ? void 0 : a.message) || c || "unknown error";
|
|
159
|
+
n.push({ kind: "error", message: `${u}: ${b}` });
|
|
132
160
|
continue;
|
|
133
161
|
}
|
|
134
162
|
if (a && typeof a.bytes == "string")
|
|
135
163
|
try {
|
|
136
|
-
const
|
|
164
|
+
const u = x(a.bytes), b = JSON.parse(u);
|
|
137
165
|
n.push({ kind: "event", payload: b });
|
|
138
|
-
} catch (
|
|
139
|
-
n.push({ kind: "error", message: `Bedrock event-stream decode failed: ${
|
|
166
|
+
} catch (u) {
|
|
167
|
+
n.push({ kind: "error", message: `Bedrock event-stream decode failed: ${u.message}` });
|
|
140
168
|
}
|
|
141
169
|
}
|
|
142
170
|
return n;
|
|
143
171
|
}
|
|
144
172
|
}
|
|
145
|
-
function k(
|
|
146
|
-
const r = {}, n = new DataView(
|
|
173
|
+
function k(s) {
|
|
174
|
+
const r = {}, n = new DataView(s.buffer, s.byteOffset, s.byteLength), t = new TextDecoder();
|
|
147
175
|
let e = 0;
|
|
148
|
-
for (; e <
|
|
149
|
-
const d =
|
|
150
|
-
if (e += 1, e + d >
|
|
151
|
-
const
|
|
152
|
-
if (e += d, e >=
|
|
153
|
-
const
|
|
154
|
-
if (e += 1,
|
|
155
|
-
if (e + 2 >
|
|
156
|
-
const
|
|
157
|
-
if (e += 2, e +
|
|
158
|
-
r[
|
|
176
|
+
for (; e < s.length; ) {
|
|
177
|
+
const d = s[e];
|
|
178
|
+
if (e += 1, e + d > s.length) break;
|
|
179
|
+
const i = t.decode(s.subarray(e, e + d));
|
|
180
|
+
if (e += d, e >= s.length) break;
|
|
181
|
+
const l = s[e];
|
|
182
|
+
if (e += 1, l === 7) {
|
|
183
|
+
if (e + 2 > s.length) break;
|
|
184
|
+
const o = n.getUint16(e, !1);
|
|
185
|
+
if (e += 2, e + o > s.length) break;
|
|
186
|
+
r[i] = t.decode(s.subarray(e, e + o)), e += o;
|
|
159
187
|
} else
|
|
160
188
|
break;
|
|
161
189
|
}
|
|
162
190
|
return r;
|
|
163
191
|
}
|
|
164
|
-
function x(
|
|
165
|
-
const r = atob(
|
|
192
|
+
function x(s) {
|
|
193
|
+
const r = atob(s), n = new Uint8Array(r.length);
|
|
166
194
|
for (let t = 0; t < r.length; t++) n[t] = r.charCodeAt(t);
|
|
167
195
|
return new TextDecoder().decode(n);
|
|
168
196
|
}
|
|
169
197
|
export {
|
|
170
|
-
|
|
171
|
-
|
|
198
|
+
p as BEDROCK_CLAUDE_MODELS,
|
|
199
|
+
A as BedrockProvider
|
|
172
200
|
};
|