@anvaka/vue-llm 0.2.0 → 0.3.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,9 +3,10 @@
3
3
  Browser-only LLM client + Vue 3 plugin, provider adapters, and lightweight components.
4
4
 
5
5
  ## Features
6
- - Provider factory with 8 built-in providers (OpenAI, Anthropic, Grok, Gemini, Ollama, Llama Server, OpenRouter, Custom) – extend with `registerProvider()`
6
+ - Provider factory with 10 built-in providers (OpenAI, Anthropic, Bedrock, Grok, Gemini, Ollama, Llama Server, OpenRouter, DeepSeek, Custom) – extend with `registerProvider()`
7
7
  - LocalStorage-based config store (custom storage adapter supported)
8
8
  - Streaming + promise requests via `llmClient.stream()`
9
+ - Normalized usage + USD cost on every response (override built-in rates per app or per model)
9
10
  - Vue plugin for dependency injection
10
11
  - `useLLM()` composable with reactive streaming state
11
12
  - Ready-to-use components: `ProviderSelector`, `LLMConfigModal`, `StoredKeysManager`
@@ -121,13 +122,73 @@ For scripts outside Vue components, use the singleton exports:
121
122
  import { llmClient, configStore, keyStore } from '@anvaka/vue-llm'
122
123
 
123
124
  // Stream directly
124
- await llmClient.stream({ messages: [...] }, chunk => console.log(chunk.fullContent))
125
+ const { content, usage, cost } = await llmClient.stream(
126
+ { messages: [...] },
127
+ chunk => console.log(chunk.fullContent)
128
+ )
125
129
 
126
130
  // Manage configs
127
131
  configStore.saveConfig('my-provider', { ... })
128
132
  configStore.setActiveProviderId('my-provider')
129
133
  ```
130
134
 
135
+ ## Usage & Cost
136
+
137
+ Every response carries a normalized `usage` object and a USD `cost` breakdown when the model's rates are known. Stream chunks expose a running `fullUsage` so consumers can render a live cost counter.
138
+
139
+ ```js
140
+ const { content, usage, cost } = await client.stream({ messages: [...] })
141
+
142
+ usage // { inputTokens, outputTokens, totalTokens,
143
+ // cachedInputTokens?, cacheCreationInputTokens?, reasoningTokens?, raw }
144
+ cost // { total, input, cachedInput, cacheCreation, output, currency, rates }
145
+ // or null when the model isn't in the rates table
146
+ ```
147
+
148
+ `runAgentLoop` aggregates usage across all iterations (each tool round-trip is one call) and emits a `usage` event per iteration:
149
+
150
+ ```js
151
+ const { messages, usage, cost } = await client.runAgentLoop({
152
+ messages: [{ role: 'user', content: '7 * 11?' }],
153
+ tools, executors,
154
+ onEvent: ev => {
155
+ if (ev.type === 'usage') console.log(`iter cost: ${ev.cost?.total}`)
156
+ }
157
+ })
158
+ ```
159
+
160
+ ### Overriding rates
161
+
162
+ Built-in rates are sourced from public pricing pages and *will* drift. Three ways to override, in priority order:
163
+
164
+ ```js
165
+ // 1. Per-instance — wins over everything
166
+ import { LLMClient } from '@anvaka/vue-llm'
167
+ const client = new LLMClient({
168
+ pricing: {
169
+ openai: { 'gpt-4o': { input: 1.50, output: 6.00, cachedInput: 0.75 } }
170
+ }
171
+ })
172
+
173
+ // 2. Global runtime override
174
+ import { registerPricing } from '@anvaka/vue-llm'
175
+ registerPricing('openai', 'gpt-4o', { input: 1.50, output: 6.00 })
176
+
177
+ // 3. Inline (without LLMClient)
178
+ import { calculateCost } from '@anvaka/vue-llm/pricing'
179
+ const cost = calculateCost(usage, { provider: 'openai', model: 'gpt-4o' })
180
+ ```
181
+
182
+ Rate keys: `input` (uncached prompt, per 1M tokens, USD), `output`, optional `cachedInput` (prompt-cache hits — defaults to `input` if omitted), optional `cacheCreation` (Anthropic-only cache-write premium).
183
+
184
+ Model lookup is exact match first, then longest-prefix match — so registering `claude-haiku-4-5` automatically covers `claude-haiku-4-5-20251001`.
185
+
186
+ ```js
187
+ // Pricing-only import (no Vue):
188
+ import { calculateCost, formatCost, registerPricing, DEFAULT_RATES } from '@anvaka/vue-llm/pricing'
189
+ formatCost(0.00012) // "$0.000120"
190
+ ```
191
+
131
192
  ## Theming
132
193
  Override any `--llm-*` CSS variable globally or per container.
133
194
  ```css
@@ -150,7 +211,7 @@ registerProvider('my-provider', MyProvider)
150
211
  // From '@anvaka/vue-llm/providers'
151
212
  import {
152
213
  BaseProvider,
153
- PROVIDERS, // { OPENAI, ANTHROPIC, GROK, GEMINI, OLLAMA, LLAMA_SERVER, OPENROUTER, CUSTOM }
214
+ PROVIDERS, // { OPENAI, ANTHROPIC, BEDROCK, GROK, GEMINI, OLLAMA, LLAMA_SERVER, OPENROUTER, DEEPSEEK, CUSTOM }
154
215
  DEFAULT_CONFIGS, // Default configs for each provider type
155
216
  createProvider, // (type, config) => Provider
156
217
  registerProvider, // (type, ProviderClass) => void
@@ -1,12 +1,25 @@
1
- import { createProviderFlexible as I } from "../providers/factory.js";
2
- class $ {
3
- constructor({ configStore: t, logger: s } = {}) {
4
- this.configStore = t, this.logger = s || console, this.config = null, this.provider = null, this.usageTracker = null;
1
+ import { createProviderFlexible as U } from "../providers/factory.js";
2
+ import { calculateCost as A } from "../pricing/calculate.js";
3
+ class P {
4
+ constructor({ configStore: t, logger: s, pricing: i } = {}) {
5
+ this.configStore = t, this.logger = s || console, this.config = null, this.provider = null, this.usageTracker = null, this.pricing = i || null;
6
+ }
7
+ // Compute USD cost for a usage object using this client's active provider /
8
+ // model + any per-instance overrides. Returns null when rates are unknown.
9
+ // Exposed so consumers can re-cost historical usage objects without
10
+ // reaching into the pricing module directly.
11
+ costFor(t, { provider: s, model: i } = {}) {
12
+ var r, e;
13
+ return A(t, {
14
+ provider: s || ((r = this.config) == null ? void 0 : r.provider),
15
+ model: i || ((e = this.config) == null ? void 0 : e.model),
16
+ pricing: this.pricing
17
+ });
5
18
  }
6
19
  async initialize(t = null) {
7
20
  if (this.config = t || this.configStore.getActiveConfig(), !this.config)
8
21
  throw new Error("LLM not configured");
9
- this.provider = I(this.config.provider, this.config), await this.provider.initialize(), this.usageTracker = this._createUsageTracker();
22
+ this.provider = U(this.config.provider, this.config), await this.provider.initialize(), this.usageTracker = this._createUsageTracker();
10
23
  }
11
24
  async ensureInitialized() {
12
25
  this.provider || await this.initialize();
@@ -35,17 +48,18 @@ class $ {
35
48
  }
36
49
  }
37
50
  _createUsageTracker() {
38
- return {
51
+ const t = {
39
52
  totalTokens: 0,
40
53
  totalCost: 0,
41
- recordUsage: (t, s) => {
42
- var i;
43
- (i = s.usage) != null && i.tokens && (this.totalTokens += s.usage.tokens);
54
+ recordUsage: (s, i) => {
55
+ var r;
56
+ ((r = i == null ? void 0 : i.usage) == null ? void 0 : r.totalTokens) != null && (t.totalTokens += i.usage.totalTokens);
44
57
  },
45
- recordPartialUsage: (t) => {
46
- t != null && t.tokens && (this.totalTokens += t.tokens);
58
+ recordPartialUsage: (s) => {
59
+ (s == null ? void 0 : s.totalTokens) != null && (t.totalTokens += s.totalTokens);
47
60
  }
48
61
  };
62
+ return t;
49
63
  }
50
64
  validateCapabilities(t) {
51
65
  var i;
@@ -75,10 +89,12 @@ class $ {
75
89
  async stream(t, s) {
76
90
  await this.ensureInitialized();
77
91
  const i = this.validateCapabilities({ ...t, stream: !0, model: t.model || this.config.model, requestId: this.generateRequestId() }), r = t.messages;
78
- let e = "";
79
- return await this.provider.streamRequest(r, i, (n) => {
80
- e = n.fullContent, s && s(n);
81
- }), e;
92
+ let e = "", n = null;
93
+ return await this.provider.streamRequest(r, i, (o) => {
94
+ e = o.fullContent, o.fullUsage && (n = o.fullUsage);
95
+ const l = o.fullUsage ? { ...o, cost: this.costFor(o.fullUsage, { provider: this.config.provider, model: i.model }) } : o;
96
+ s && s(l);
97
+ }), { content: e, usage: n, cost: this.costFor(n, { provider: this.config.provider, model: i.model }) };
82
98
  }
83
99
  // Multi-turn tool-calling loop. Each iteration:
84
100
  // 1) stream a model response (text + accumulated tool_calls)
@@ -104,47 +120,58 @@ class $ {
104
120
  temperature: n,
105
121
  model: o,
106
122
  maxTokens: l,
107
- enableThinking: u
123
+ enableThinking: p
108
124
  } = {}) {
109
125
  if (await this.ensureInitialized(), !this.provider.hasCapability("tools"))
110
126
  throw new Error("Configured provider does not support tools");
111
- const h = t.slice();
112
- let c = 0, m = null;
113
- for (; c < e; ) {
114
- c++, r && r({ type: "iter-start", iter: c });
115
- const b = this.validateCapabilities({
116
- messages: h,
127
+ const c = t.slice();
128
+ let d = 0, m = null;
129
+ const f = { inputTokens: 0, outputTokens: 0, totalTokens: 0, cachedInputTokens: 0, cacheCreationInputTokens: 0, reasoningTokens: 0 };
130
+ let b = !1;
131
+ for (; d < e; ) {
132
+ d++, r && r({ type: "iter-start", iter: d });
133
+ const x = this.validateCapabilities({
134
+ messages: c,
117
135
  tools: s,
118
136
  stream: !0,
119
137
  model: o || this.config.model,
120
138
  temperature: n ?? this.config.temperature,
121
139
  maxTokens: l ?? this.config.maxTokens ?? 4096,
122
- enableThinking: u ?? !1,
140
+ enableThinking: p ?? !1,
123
141
  requestId: this.generateRequestId()
124
- }), p = await this.provider.streamRequest(h, b, (a) => {
142
+ }), h = await this.provider.streamRequest(c, x, (a) => {
125
143
  a.content && r && r({ type: "text-delta", text: a.content }), a.toolCallDelta && r && r({ type: "tool-call-delta", delta: a.toolCallDelta });
126
- }), v = (p == null ? void 0 : p.content) || "", d = (p == null ? void 0 : p.toolCalls) || [], w = { role: "assistant", content: v };
127
- if (d.length && (w.tool_calls = d), h.push(w), r && r({ type: "assistant-message", content: v, toolCalls: d }), !d.length) {
144
+ }), q = (h == null ? void 0 : h.content) || "", y = (h == null ? void 0 : h.toolCalls) || [], C = (h == null ? void 0 : h.thinking) || "", w = (h == null ? void 0 : h.usage) || null;
145
+ if (w) {
146
+ b = !0;
147
+ for (const a of Object.keys(f))
148
+ w[a] != null && (f[a] += w[a]);
149
+ r && r({ type: "usage", usage: w, cost: this.costFor(w, { provider: this.config.provider, model: x.model }) });
150
+ }
151
+ const k = { role: "assistant", content: q };
152
+ if (y.length && (k.tool_calls = y), C && (k.thinking = C), c.push(k), r && r({ type: "assistant-message", content: q, toolCalls: y, thinking: C }), !y.length) {
128
153
  m = "no-tool-calls";
129
154
  break;
130
155
  }
131
- for (const a of d) {
156
+ for (const a of y) {
132
157
  r && r({ type: "tool-call", id: a.id, name: a.name, args: a.args });
133
- const _ = i && i[a.name];
134
- let y;
135
- if (!_)
136
- y = `Error: unknown tool '${a.name}'`;
158
+ const R = i && i[a.name];
159
+ let T;
160
+ if (!R)
161
+ T = `Error: unknown tool '${a.name}'`;
137
162
  else
138
163
  try {
139
- const f = await _(a.args || {});
140
- y = typeof f == "string" ? f : JSON.stringify(f ?? "");
141
- } catch (f) {
142
- y = `Error: ${(f == null ? void 0 : f.message) || String(f)}`;
164
+ const u = await R(a.args || {});
165
+ T = typeof u == "string" ? u : JSON.stringify(u ?? "");
166
+ } catch (u) {
167
+ T = `Error: ${(u == null ? void 0 : u.message) || String(u)}`;
143
168
  }
144
- h.push({ role: "tool", tool_call_id: a.id, content: y }), r && r({ type: "tool-result", id: a.id, name: a.name, content: y });
169
+ c.push({ role: "tool", tool_call_id: a.id, content: T }), r && r({ type: "tool-result", id: a.id, name: a.name, content: T });
145
170
  }
146
171
  }
147
- return m || (m = "max-iters"), r && r({ type: "stop", reason: m, iterations: c }), { messages: h, iterations: c, stopReason: m };
172
+ m || (m = "max-iters");
173
+ const g = b ? f : null, v = this.costFor(g, { provider: this.config.provider, model: o || this.config.model });
174
+ return r && r({ type: "stop", reason: m, iterations: d, usage: g, cost: v }), { messages: c, iterations: d, stopReason: m, usage: g, cost: v };
148
175
  }
149
176
  generateRequestId() {
150
177
  return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
@@ -185,12 +212,12 @@ class $ {
185
212
  createLLMWrapper(t, s = null, i = null) {
186
213
  const r = this;
187
214
  function e(n, o = {}) {
188
- return new S(r, t, n, o, s, i);
215
+ return new $(r, t, n, o, s, i);
189
216
  }
190
217
  return e;
191
218
  }
192
219
  }
193
- class S {
220
+ class $ {
194
221
  constructor(t, s, i, r, e, n) {
195
222
  this.client = t, this.contextNode = s, this.prompt = i, this.options = r || {}, this.originatingCode = e, this.defaultPresetName = n, this.targetNode = null, this.targetAttributes = {}, this.operationName = null, this._executed = !1, this._promise = null;
196
223
  }
@@ -229,8 +256,8 @@ class S {
229
256
  };
230
257
  try {
231
258
  if (this.targetNode) {
232
- const u = r({ ...n, stream: !0 });
233
- return await this._streamIntoTarget(e, u, s, i);
259
+ const p = r({ ...n, stream: !0 });
260
+ return await this._streamIntoTarget(e, p, s, i);
234
261
  }
235
262
  const l = r({ ...n, stream: !1 });
236
263
  return await this._promiseResponse(e, l, s, i);
@@ -248,10 +275,10 @@ class S {
248
275
  throw new Error("Provider presets require a configured ConfigStore instance");
249
276
  const s = (e = (r = this.client).getConfigByName) == null ? void 0 : e.call(r, t);
250
277
  if (!s) {
251
- const l = (((o = (n = this.client.configStore).getEnabledConfigs) == null ? void 0 : o.call(n)) || []).map((h) => h.name || `${h.provider} (${h.baseUrl})`).filter(Boolean), u = l.length ? ` Available presets: ${l.join(", ")}` : " No configured providers found.";
252
- throw new Error(`Provider preset '${t}' not found.${u}`);
278
+ const l = (((o = (n = this.client.configStore).getEnabledConfigs) == null ? void 0 : o.call(n)) || []).map((c) => c.name || `${c.provider} (${c.baseUrl})`).filter(Boolean), p = l.length ? ` Available presets: ${l.join(", ")}` : " No configured providers found.";
279
+ throw new Error(`Provider preset '${t}' not found.${p}`);
253
280
  }
254
- const i = I(s.provider, s);
281
+ const i = U(s.provider, s);
255
282
  return await i.initialize(), { provider: i, config: s, cleanup: () => {
256
283
  var l;
257
284
  return (l = i.cancelAllRequests) == null ? void 0 : l.call(i);
@@ -271,44 +298,46 @@ class S {
271
298
  }
272
299
  }
273
300
  async _streamIntoTarget(t, s, i, r) {
274
- var c, m, b, p, v, d, w, a, _, y, f, C, T, k;
301
+ var d, m, f, b, g, v, x, h, q, y, C, w, k, a, R, T;
275
302
  let e, n;
276
- typeof this.targetNode == "string" ? (n = this.targetNode, e = this.contextNode.createChild(n), (c = e.setSelected) == null || c.call(e)) : (e = this.targetNode, n = ((m = e.getText) == null ? void 0 : m.call(e)) || ""), this._applyAttributes(e);
303
+ typeof this.targetNode == "string" ? (n = this.targetNode, e = this.contextNode.createChild(n), (d = e.setSelected) == null || d.call(e)) : (e = this.targetNode, n = ((m = e.getText) == null ? void 0 : m.call(e)) || ""), this._applyAttributes(e);
277
304
  const o = r == null ? void 0 : r.name;
278
- o && e.addTag && e.addTag("provider", o), (b = e.addLLMLog) == null || b.call(e, this.operationName || "streaming-request", {
305
+ o && e.addTag && e.addTag("provider", o), (f = e.addLLMLog) == null || f.call(e, this.operationName || "streaming-request", {
279
306
  provider: r == null ? void 0 : r.provider,
280
307
  model: r == null ? void 0 : r.model,
281
308
  messages: t,
282
309
  options: s
283
- }), (p = e.setStreamingState) == null || p.call(e, !0), e._activeRequestId = s.requestId;
284
- let l = "", u = null, h = 0;
310
+ }), (b = e.setStreamingState) == null || b.call(e, !0), e._activeRequestId = s.requestId;
311
+ let l = "", p = null, c = null;
285
312
  try {
286
- return await i.streamRequest(t, s, (g) => {
287
- var x, q, R;
288
- g.content && (l = g.fullContent, (x = e.setText) == null || x.call(e, l)), (q = g.usage) != null && q.tokens && (h += g.usage.tokens), g.finishReason && (u = g.finishReason), g.done && ((R = e.setStreamingState) == null || R.call(e, !1), e._activeRequestId = null);
289
- }), (v = e.addLLMLog) == null || v.call(e, (this.operationName || "streaming-request") + "-response", { requestId: s.requestId }, { content: l, usage: { tokens: h } }), u === "length" ? ((d = e.setAttribute) == null || d.call(e, "_truncated", !0), (w = e.addLog) == null || w.call(e, "Response truncated. Increase Max Tokens.", "warn", { finish_reason: "length" })) : (a = e.setAttribute) == null || a.call(e, "_truncated", !1), await ((_ = e.persistNow) == null ? void 0 : _.call(e)), e;
290
- } catch (g) {
291
- throw (y = e.addLLMLog) == null || y.call(e, (this.operationName || "streaming-request") + "-error", { requestId: s.requestId }, null, g), (f = e.setStreamingState) == null || f.call(e, !1), e._activeRequestId = null, (C = e.setText) == null || C.call(e, `Error: ${g.message}`), (T = e.setAttribute) == null || T.call(e, "_truncated", !1), await ((k = e.persistNow) == null ? void 0 : k.call(e)), g;
313
+ await i.streamRequest(t, s, (_) => {
314
+ var I, S;
315
+ _.content && (l = _.fullContent, (I = e.setText) == null || I.call(e, l)), _.fullUsage && (c = _.fullUsage), _.finishReason && (p = _.finishReason), _.done && ((S = e.setStreamingState) == null || S.call(e, !1), e._activeRequestId = null);
316
+ });
317
+ const u = (v = (g = this.client).costFor) == null ? void 0 : v.call(g, c, { provider: r == null ? void 0 : r.provider, model: s.model });
318
+ return (x = e.addLLMLog) == null || x.call(e, (this.operationName || "streaming-request") + "-response", { requestId: s.requestId }, { content: l, usage: c, cost: u }), p === "length" ? ((h = e.setAttribute) == null || h.call(e, "_truncated", !0), (q = e.addLog) == null || q.call(e, "Response truncated. Increase Max Tokens.", "warn", { finish_reason: "length" })) : (y = e.setAttribute) == null || y.call(e, "_truncated", !1), await ((C = e.persistNow) == null ? void 0 : C.call(e)), e;
319
+ } catch (u) {
320
+ throw (w = e.addLLMLog) == null || w.call(e, (this.operationName || "streaming-request") + "-error", { requestId: s.requestId }, null, u), (k = e.setStreamingState) == null || k.call(e, !1), e._activeRequestId = null, (a = e.setText) == null || a.call(e, `Error: ${u.message}`), (R = e.setAttribute) == null || R.call(e, "_truncated", !1), await ((T = e.persistNow) == null ? void 0 : T.call(e)), u;
292
321
  }
293
322
  }
294
323
  async _promiseResponse(t, s, i, r) {
295
- var u, h, c, m, b, p, v;
324
+ var p, c, d, m, f, b, g;
296
325
  const e = i.prepareRequest(t, s), n = await i.makeRequest(e), o = i.processResponse(n);
297
- if ((o.finishReason || ((h = (u = n == null ? void 0 : n.choices) == null ? void 0 : u[0]) == null ? void 0 : h.finish_reason) || null) === "length")
326
+ if ((o.finishReason || ((c = (p = n == null ? void 0 : n.choices) == null ? void 0 : p[0]) == null ? void 0 : c.finish_reason) || null) === "length")
298
327
  try {
299
328
  this.contextNode.setAttribute("_truncated", !0);
300
- } catch (d) {
301
- (m = (c = this.client.logger) == null ? void 0 : c.warn) == null || m.call(c, "Unable to mark node as truncated", { error: d });
329
+ } catch (v) {
330
+ (m = (d = this.client.logger) == null ? void 0 : d.warn) == null || m.call(d, "Unable to mark node as truncated", { error: v });
302
331
  }
303
332
  else
304
333
  try {
305
334
  this.contextNode.setAttribute("_truncated", !1);
306
- } catch (d) {
307
- (p = (b = this.client.logger) == null ? void 0 : b.warn) == null || p.call(b, "Unable to clear truncated marker on node", { error: d });
335
+ } catch (v) {
336
+ (b = (f = this.client.logger) == null ? void 0 : f.warn) == null || b.call(f, "Unable to clear truncated marker on node", { error: v });
308
337
  }
309
- return ((v = o.content) == null ? void 0 : v.trim()) || "";
338
+ return ((g = o.content) == null ? void 0 : g.trim()) || "";
310
339
  }
311
340
  }
312
341
  export {
313
- $ as LLMClient
342
+ P as LLMClient
314
343
  };
package/dist/index.js CHANGED
@@ -1,38 +1,46 @@
1
1
  import { LLMClient as l } from "./core/LLMClient.js";
2
- import { ConfigStore as S } from "./core/configStore.js";
3
- import { KeyStore as p, maskApiKey as M } from "./core/keyStore.js";
4
- import { LocalStorageAdapter as s, MemoryStorageAdapter as x } from "./core/storageAdapter.js";
5
- import { DEFAULT_CONFIGS as g, PROVIDERS as _, createProvider as C, createProviderFlexible as O, registerProvider as u } from "./providers/factory.js";
2
+ import { ConfigStore as m } from "./core/configStore.js";
3
+ import { KeyStore as p, maskApiKey as s } from "./core/keyStore.js";
4
+ import { LocalStorageAdapter as g, MemoryStorageAdapter as x } from "./core/storageAdapter.js";
5
+ import { DEFAULT_CONFIGS as d, PROVIDERS as C, createProvider as _, createProviderFlexible as u, registerProvider as O } from "./providers/factory.js";
6
+ import { DEFAULT_RATES as E } from "./pricing/rates.js";
7
+ import { calculateCost as A, clearPricingOverrides as v, formatCost as F, lookupRates as R, registerPricing as T } from "./pricing/calculate.js";
6
8
  import { createLLM as r } from "./vue/plugin.js";
7
- import { LLMPlugin as P, LLM_CLIENT_SYMBOL as E, LLM_CONFIG_SYMBOL as v, LLM_KEYSTORE_SYMBOL as A } from "./vue/plugin.js";
8
- import { createDefaultConfig as I, useLLM as K } from "./vue/useLLM.js";
9
- import { default as k } from "./vue/components/ProviderSelector.vue.js";
10
- import { default as D } from "./vue/components/LLMConfigModal.vue.js";
11
- import { default as R } from "./vue/components/StoredKeysManager.vue.js";
12
- const e = r({}), t = e.client, L = e.configStore, a = e.keyStore;
9
+ import { LLMPlugin as D, LLM_CLIENT_SYMBOL as I, LLM_CONFIG_SYMBOL as K, LLM_KEYSTORE_SYMBOL as Y } from "./vue/plugin.js";
10
+ import { createDefaultConfig as N, useLLM as G } from "./vue/useLLM.js";
11
+ import { default as b } from "./vue/components/ProviderSelector.vue.js";
12
+ import { default as h } from "./vue/components/LLMConfigModal.vue.js";
13
+ import { default as q } from "./vue/components/StoredKeysManager.vue.js";
14
+ const e = r({}), t = e.client, a = e.configStore, i = e.keyStore;
13
15
  export {
14
- S as ConfigStore,
15
- g as DEFAULT_CONFIGS,
16
+ m as ConfigStore,
17
+ d as DEFAULT_CONFIGS,
18
+ E as DEFAULT_RATES,
16
19
  p as KeyStore,
17
20
  l as LLMClient,
18
- D as LLMConfigModal,
19
- P as LLMPlugin,
20
- E as LLM_CLIENT_SYMBOL,
21
- v as LLM_CONFIG_SYMBOL,
22
- A as LLM_KEYSTORE_SYMBOL,
23
- s as LocalStorageAdapter,
21
+ h as LLMConfigModal,
22
+ D as LLMPlugin,
23
+ I as LLM_CLIENT_SYMBOL,
24
+ K as LLM_CONFIG_SYMBOL,
25
+ Y as LLM_KEYSTORE_SYMBOL,
26
+ g as LocalStorageAdapter,
24
27
  x as MemoryStorageAdapter,
25
- _ as PROVIDERS,
26
- k as ProviderSelector,
27
- R as StoredKeysManager,
28
- L as configStore,
29
- I as createDefaultConfig,
28
+ C as PROVIDERS,
29
+ b as ProviderSelector,
30
+ q as StoredKeysManager,
31
+ A as calculateCost,
32
+ v as clearPricingOverrides,
33
+ a as configStore,
34
+ N as createDefaultConfig,
30
35
  r as createLLM,
31
- C as createProvider,
32
- O as createProviderFlexible,
33
- a as keyStore,
36
+ _ as createProvider,
37
+ u as createProviderFlexible,
38
+ F as formatCost,
39
+ i as keyStore,
34
40
  t as llmClient,
35
- M as maskApiKey,
36
- u as registerProvider,
37
- K as useLLM
41
+ R as lookupRates,
42
+ s as maskApiKey,
43
+ T as registerPricing,
44
+ O as registerProvider,
45
+ G as useLLM
38
46
  };
@@ -0,0 +1,58 @@
1
+ import { DEFAULT_RATES as R } from "./rates.js";
2
+ const c = {};
3
+ function S(t, e, n) {
4
+ if (typeof t != "string" || typeof e != "string" || !n)
5
+ throw new Error("registerPricing(provider, model, rates) requires strings and a rates object");
6
+ c[t] || (c[t] = {}), c[t][e] = { ...n };
7
+ }
8
+ function U(t, e) {
9
+ if (t == null) {
10
+ for (const n of Object.keys(c)) delete c[n];
11
+ return;
12
+ }
13
+ if (e == null) {
14
+ delete c[t];
15
+ return;
16
+ }
17
+ c[t] && delete c[t][e];
18
+ }
19
+ function b(t, e, n = null) {
20
+ return !t || !e ? null : r(n == null ? void 0 : n[t], e) || r(c[t], e) || r(R[t], e) || null;
21
+ }
22
+ function r(t, e) {
23
+ if (!t) return null;
24
+ if (t[e]) return t[e];
25
+ let n = null;
26
+ for (const u of Object.keys(t))
27
+ e.startsWith(u) && (n == null || u.length > n.length) && (n = u);
28
+ return n ? t[n] : null;
29
+ }
30
+ function $(t, { provider: e, model: n, pricing: u = null } = {}) {
31
+ if (!t) return null;
32
+ const s = b(e, n, u);
33
+ if (!s) return null;
34
+ const T = t.inputTokens ?? 0, p = t.outputTokens ?? 0, i = t.cachedInputTokens ?? 0, f = t.cacheCreationInputTokens ?? 0, _ = Math.max(0, T - i - f), o = s.input ?? 0, y = s.output ?? 0, C = s.cachedInput ?? o, I = s.cacheCreation ?? o, l = _ * o / 1e6, a = i * C / 1e6, h = f * I / 1e6, k = p * y / 1e6;
35
+ return {
36
+ total: l + a + h + k,
37
+ input: l,
38
+ cachedInput: a,
39
+ cacheCreation: h,
40
+ output: k,
41
+ currency: "USD",
42
+ rates: s
43
+ };
44
+ }
45
+ function g(t, { currency: e = "USD" } = {}) {
46
+ if (t == null || Number.isNaN(t)) return "—";
47
+ const n = e === "USD" ? "$" : "";
48
+ if (t === 0) return `${n}0.00`;
49
+ const u = Math.abs(t), s = u >= 1 ? 2 : u >= 0.01 ? 4 : 6;
50
+ return `${n}${t.toFixed(s)}`;
51
+ }
52
+ export {
53
+ $ as calculateCost,
54
+ U as clearPricingOverrides,
55
+ g as formatCost,
56
+ b as lookupRates,
57
+ S as registerPricing
58
+ };
@@ -0,0 +1,10 @@
1
+ import { DEFAULT_RATES as o } from "./rates.js";
2
+ import { calculateCost as i, clearPricingOverrides as a, formatCost as c, lookupRates as s, registerPricing as l } from "./calculate.js";
3
+ export {
4
+ o as DEFAULT_RATES,
5
+ i as calculateCost,
6
+ a as clearPricingOverrides,
7
+ c as formatCost,
8
+ s as lookupRates,
9
+ l as registerPricing
10
+ };
@@ -0,0 +1,80 @@
1
+ const t = {
2
+ openai: {
3
+ "gpt-5": { input: 0.625, output: 5, cachedInput: 0.125 },
4
+ "gpt-5-mini": { input: 0.25, output: 2, cachedInput: 0.025 },
5
+ "gpt-5-nano": { input: 0.05, output: 0.4, cachedInput: 5e-3 },
6
+ "gpt-5.4": { input: 2.5, output: 15, cachedInput: 0.25 },
7
+ "gpt-5.4-mini": { input: 0.75, output: 4.5, cachedInput: 0.075 },
8
+ "gpt-5.4-nano": { input: 0.2, output: 1.25, cachedInput: 0.02 },
9
+ "gpt-4o": { input: 2.5, output: 10, cachedInput: 1.25 },
10
+ "gpt-4o-mini": { input: 0.15, output: 0.6, cachedInput: 0.075 },
11
+ "gpt-4.1": { input: 2, output: 8, cachedInput: 0.5 },
12
+ "gpt-4.1-mini": { input: 0.2, output: 0.8, cachedInput: 0.1 },
13
+ "gpt-4.1-nano": { input: 0.05, output: 0.2, cachedInput: 0.025 },
14
+ "gpt-4-turbo": { input: 10, output: 30 },
15
+ "gpt-4": { input: 30, output: 60 },
16
+ "gpt-3.5-turbo": { input: 0.5, output: 1 },
17
+ o1: { input: 15, output: 60, cachedInput: 7.5 },
18
+ "o1-mini": { input: 0.55, output: 2.2, cachedInput: 0.55 },
19
+ o3: { input: 2, output: 8, cachedInput: 0.5 },
20
+ "o3-mini": { input: 1.1, output: 4.4, cachedInput: 0.55 },
21
+ "o4-mini": { input: 1.1, output: 4.4, cachedInput: 0.275 }
22
+ },
23
+ anthropic: {
24
+ "claude-opus-4-7": { input: 5, output: 25, cachedInput: 0.5, cacheCreation: 6.25 },
25
+ "claude-opus-4-6": { input: 5, output: 25, cachedInput: 0.5, cacheCreation: 6.25 },
26
+ "claude-sonnet-4-6": { input: 3, output: 15, cachedInput: 0.3, cacheCreation: 3.75 },
27
+ "claude-sonnet-4-5": { input: 3, output: 15, cachedInput: 0.3, cacheCreation: 3.75 },
28
+ "claude-haiku-4-5": { input: 1, output: 5, cachedInput: 0.1, cacheCreation: 1.25 },
29
+ "claude-3-5-sonnet": { input: 3, output: 15, cachedInput: 0.3, cacheCreation: 3.75 },
30
+ "claude-3-5-haiku": { input: 0.8, output: 4, cachedInput: 0.08, cacheCreation: 1 },
31
+ "claude-3-opus": { input: 15, output: 75 },
32
+ "claude-3-haiku": { input: 0.25, output: 1.25 }
33
+ },
34
+ // Bedrock charges the same per-token rates as Anthropic direct for the
35
+ // Claude inference profiles; the model IDs differ (us.anthropic.* prefix).
36
+ bedrock: {
37
+ "us.anthropic.claude-opus-4-7": { input: 5, output: 25, cachedInput: 0.5, cacheCreation: 6.25 },
38
+ "us.anthropic.claude-opus-4-6": { input: 5, output: 25, cachedInput: 0.5, cacheCreation: 6.25 },
39
+ "us.anthropic.claude-sonnet-4-6": { input: 3, output: 15, cachedInput: 0.3, cacheCreation: 3.75 },
40
+ "us.anthropic.claude-sonnet-4-5": { input: 3, output: 15, cachedInput: 0.3, cacheCreation: 3.75 },
41
+ "us.anthropic.claude-haiku-4-5": { input: 1, output: 5, cachedInput: 0.1, cacheCreation: 1.25 },
42
+ "us.anthropic.claude-opus-4-1": { input: 15, output: 75 }
43
+ },
44
+ gemini: {
45
+ "gemini-2.5-pro": { input: 1, output: 10, cachedInput: 0.125 },
46
+ "gemini-2.5-flash": { input: 0.3, output: 2.5, cachedInput: 0.03 },
47
+ "gemini-2.5-flash-lite": { input: 0.1, output: 0.4, cachedInput: 0.01 },
48
+ "gemini-2.0-flash": { input: 0.1, output: 0.4, cachedInput: 0.025 },
49
+ "gemini-2.0-flash-lite": { input: 0.075, output: 0.3 },
50
+ "gemini-1.5-pro": { input: 1.25, output: 5 },
51
+ "gemini-1.5-flash": { input: 0.075, output: 0.3 }
52
+ },
53
+ grok: {
54
+ "grok-4": { input: 3, output: 15 },
55
+ "grok-4-fast": { input: 0.2, output: 0.5 },
56
+ "grok-3": { input: 3, output: 15 },
57
+ "grok-3-fast": { input: 5, output: 25 },
58
+ "grok-3-mini": { input: 0.25, output: 0.5 },
59
+ "grok-code-fast-1": { input: 0.2, output: 1.5 },
60
+ "grok-2": { input: 2, output: 10 }
61
+ },
62
+ deepseek: {
63
+ "deepseek-chat": { input: 0.21, output: 0.79, cachedInput: 0.13 },
64
+ "deepseek-reasoner": { input: 0.55, output: 2 },
65
+ "deepseek-v4-flash": { input: 0.1, output: 0.2, cachedInput: 28e-4 },
66
+ "deepseek-v4-pro": { input: 0.435, output: 0.87, cachedInput: 36e-4 }
67
+ },
68
+ // OpenRouter prices vary per upstream model and include their own margin —
69
+ // ship empty; callers can either register their own rates or use
70
+ // OpenRouterProvider.discoverModelsWithMetadata() (which returns pricing).
71
+ openrouter: {},
72
+ // Local inference — free at the per-token layer.
73
+ ollama: {},
74
+ "llama-server": {},
75
+ // Custom OpenAI-compatible endpoints: unknown by definition.
76
+ custom: {}
77
+ };
78
+ export {
79
+ t as DEFAULT_RATES
80
+ };