@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 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, Bedrock, Grok, Gemini, Ollama, Llama Server, OpenRouter, DeepSeek, Custom) – extend with `registerProvider()`
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)
@@ -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
- // Claude inference profiles; the model IDs differ (us.anthropic.* prefix).
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 C extends g {
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 = _(t), s = e.model || this.config.model || "claude-3-sonnet-20240229", r = {
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.includes("claude-opus-4-7") || (r.temperature = e.temperature ?? 0.7);
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 = m(e.tools), e.tool_choice && (r.tool_choice = e.tool_choice)), this.promptCachingEnabled(e) && y(r, e), r;
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 _(n) {
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 m(n) {
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 y(n, t) {
162
+ function k(n, t) {
160
163
  if (n.system)
161
- n.system = k(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 = x(o.content);
173
+ o.content = A(o.content);
171
174
  }
172
175
  }
173
176
  return n;
174
177
  }
175
- function k(n) {
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 x(n) {
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
- C as AnthropicProvider,
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 g = [
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 R extends w {
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 c = `${this.config.baseUrl}/model/${encodeURIComponent(e)}/invoke`, u = n ? { signal: n } : new AbortController();
33
- n || (t = t || this.generateRequestId(), this.activeRequests.set(t, u));
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 s = await fetch(c, {
36
+ const o = await fetch(i, {
36
37
  method: "POST",
37
38
  headers: this.buildHeaders(),
38
39
  body: JSON.stringify(d),
39
- signal: u.signal || n
40
+ signal: l.signal || n
40
41
  });
41
- if (!s.ok) {
42
- const l = await s.text();
43
- throw new Error(`Bedrock API Error (${s.status}): ${l}`);
42
+ if (!o.ok) {
43
+ const h = await o.text();
44
+ throw new Error(`Bedrock API Error (${o.status}): ${h}`);
44
45
  }
45
- return s.json();
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`, c = await fetch(d, {
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 (!c.ok) {
60
- const i = await c.text();
61
- throw new Error(`Bedrock API Error (${c.status}): ${i}`);
60
+ if (!i.ok) {
61
+ const c = await i.text();
62
+ throw new Error(`Bedrock API Error (${i.status}): ${c}`);
62
63
  }
63
- const u = c.body.getReader(), s = new v(), l = new TextEncoder(), f = new ReadableStream({
64
- async pull(i) {
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: m } = await u.read();
67
+ const { done: a, value: g } = await l.read();
67
68
  if (a) {
68
- i.close();
69
+ c.close();
69
70
  return;
70
71
  }
71
- const p = s.feed(m);
72
- for (const h of p) {
73
- if (h.kind === "error") {
74
- i.error(new Error(h.message));
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
- i.enqueue(l.encode(`data: ${JSON.stringify(h.payload)}
78
+ c.enqueue(h.encode(`data: ${JSON.stringify(u.payload)}
78
79
  `));
79
80
  }
80
81
  } catch (a) {
81
- i.error(a);
82
+ c.error(a);
82
83
  }
83
84
  },
84
- cancel(i) {
85
+ cancel(c) {
85
86
  try {
86
- u.cancel(i);
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
- // Bedrock control plane needs SigV4; Bearer auth can't list models.
94
- // Return the hardcoded list so the dropdown still populates.
95
- async discoverModels() {
96
- return g.slice();
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
- return null;
119
+ const r = this.config.baseUrl || "", n = r.replace("bedrock-runtime.", "bedrock.");
120
+ return !n || n === r ? null : `${n}/inference-profiles`;
100
121
  }
101
- parseModelsResponse() {
102
- return g.slice();
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 c = 12, u = c + d, s = u, l = e - 4, f = k(this.buf.subarray(c, u)), i = this.textDecoder.decode(this.buf.subarray(s, l));
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(i);
152
+ a = JSON.parse(c);
125
153
  } catch {
126
154
  a = null;
127
155
  }
128
- const m = f[":message-type"], p = f[":exception-type"];
129
- if (m === "exception" || p) {
130
- const h = p || f[":event-type"] || "BedrockStreamException", b = (a == null ? void 0 : a.message) || i || "unknown error";
131
- n.push({ kind: "error", message: `${h}: ${b}` });
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 h = x(a.bytes), b = JSON.parse(h);
164
+ const u = x(a.bytes), b = JSON.parse(u);
137
165
  n.push({ kind: "event", payload: b });
138
- } catch (h) {
139
- n.push({ kind: "error", message: `Bedrock event-stream decode failed: ${h.message}` });
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(o) {
146
- const r = {}, n = new DataView(o.buffer, o.byteOffset, o.byteLength), t = new TextDecoder();
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 < o.length; ) {
149
- const d = o[e];
150
- if (e += 1, e + d > o.length) break;
151
- const c = t.decode(o.subarray(e, e + d));
152
- if (e += d, e >= o.length) break;
153
- const u = o[e];
154
- if (e += 1, u === 7) {
155
- if (e + 2 > o.length) break;
156
- const s = n.getUint16(e, !1);
157
- if (e += 2, e + s > o.length) break;
158
- r[c] = t.decode(o.subarray(e, e + s)), e += s;
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(o) {
165
- const r = atob(o), n = new Uint8Array(r.length);
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
- g as BEDROCK_CLAUDE_MODELS,
171
- R as BedrockProvider
198
+ p as BEDROCK_CLAUDE_MODELS,
199
+ A as BedrockProvider
172
200
  };