@anvaka/vue-llm 0.1.2 → 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 A {
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();
@@ -28,24 +41,25 @@ class A {
28
41
  temperature: t.temperature ?? 0.1,
29
42
  maxTokens: 10,
30
43
  stream: !1
31
- }), o = this.provider.prepareRequest(e, n), a = await this.provider.makeRequest(o);
32
- return (r = this.provider.processResponse(a).content) == null ? void 0 : r.trim();
44
+ }), o = this.provider.prepareRequest(e, n), l = await this.provider.makeRequest(o);
45
+ return (r = this.provider.processResponse(l).content) == null ? void 0 : r.trim();
33
46
  } finally {
34
47
  this.config = s, this.provider = i;
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,89 @@ class A {
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 }) };
98
+ }
99
+ // Multi-turn tool-calling loop. Each iteration:
100
+ // 1) stream a model response (text + accumulated tool_calls)
101
+ // 2) append the assistant message to the conversation
102
+ // 3) if no tool calls → stop (the model is done)
103
+ // 4) otherwise execute each tool via `executors[name](args)` and append
104
+ // a tool message per call, then loop
105
+ //
106
+ // `tools` is OpenAI-style ([{type:'function', function:{name, description, parameters}}]).
107
+ // Providers translate to native format. The caller passes `executors` as a
108
+ // `{ name -> async (args) => result }` map. Results are stringified into the
109
+ // tool message content (or used verbatim if the executor already returns a
110
+ // string).
111
+ //
112
+ // `onEvent` (optional) fires for: iter-start, text-delta, tool-call-delta,
113
+ // assistant-message, tool-call, tool-result, stop. Use it to drive a trace UI.
114
+ async runAgentLoop({
115
+ messages: t,
116
+ tools: s,
117
+ executors: i,
118
+ onEvent: r,
119
+ maxIters: e = 10,
120
+ temperature: n,
121
+ model: o,
122
+ maxTokens: l,
123
+ enableThinking: p
124
+ } = {}) {
125
+ if (await this.ensureInitialized(), !this.provider.hasCapability("tools"))
126
+ throw new Error("Configured provider does not support tools");
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,
135
+ tools: s,
136
+ stream: !0,
137
+ model: o || this.config.model,
138
+ temperature: n ?? this.config.temperature,
139
+ maxTokens: l ?? this.config.maxTokens ?? 4096,
140
+ enableThinking: p ?? !1,
141
+ requestId: this.generateRequestId()
142
+ }), h = await this.provider.streamRequest(c, x, (a) => {
143
+ a.content && r && r({ type: "text-delta", text: a.content }), a.toolCallDelta && r && r({ type: "tool-call-delta", delta: a.toolCallDelta });
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) {
153
+ m = "no-tool-calls";
154
+ break;
155
+ }
156
+ for (const a of y) {
157
+ r && r({ type: "tool-call", id: a.id, name: a.name, args: a.args });
158
+ const R = i && i[a.name];
159
+ let T;
160
+ if (!R)
161
+ T = `Error: unknown tool '${a.name}'`;
162
+ else
163
+ try {
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)}`;
168
+ }
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 });
170
+ }
171
+ }
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 };
82
175
  }
83
176
  generateRequestId() {
84
177
  return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
@@ -119,12 +212,12 @@ class A {
119
212
  createLLMWrapper(t, s = null, i = null) {
120
213
  const r = this;
121
214
  function e(n, o = {}) {
122
- return new S(r, t, n, o, s, i);
215
+ return new $(r, t, n, o, s, i);
123
216
  }
124
217
  return e;
125
218
  }
126
219
  }
127
- class S {
220
+ class $ {
128
221
  constructor(t, s, i, r, e, n) {
129
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;
130
223
  }
@@ -163,11 +256,11 @@ class S {
163
256
  };
164
257
  try {
165
258
  if (this.targetNode) {
166
- const h = r({ ...n, stream: !0 });
167
- return await this._streamIntoTarget(e, h, s, i);
259
+ const p = r({ ...n, stream: !0 });
260
+ return await this._streamIntoTarget(e, p, s, i);
168
261
  }
169
- const a = r({ ...n, stream: !1 });
170
- return await this._promiseResponse(e, a, s, i);
262
+ const l = r({ ...n, stream: !1 });
263
+ return await this._promiseResponse(e, l, s, i);
171
264
  } finally {
172
265
  (o = t.cleanup) == null || o.call(t);
173
266
  }
@@ -182,13 +275,13 @@ class S {
182
275
  throw new Error("Provider presets require a configured ConfigStore instance");
183
276
  const s = (e = (r = this.client).getConfigByName) == null ? void 0 : e.call(r, t);
184
277
  if (!s) {
185
- const a = (((o = (n = this.client.configStore).getEnabledConfigs) == null ? void 0 : o.call(n)) || []).map((u) => u.name || `${u.provider} (${u.baseUrl})`).filter(Boolean), h = a.length ? ` Available presets: ${a.join(", ")}` : " No configured providers found.";
186
- throw new Error(`Provider preset '${t}' not found.${h}`);
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}`);
187
280
  }
188
- const i = I(s.provider, s);
281
+ const i = U(s.provider, s);
189
282
  return await i.initialize(), { provider: i, config: s, cleanup: () => {
190
- var a;
191
- return (a = i.cancelAllRequests) == null ? void 0 : a.call(i);
283
+ var l;
284
+ return (l = i.cancelAllRequests) == null ? void 0 : l.call(i);
192
285
  } };
193
286
  }
194
287
  _resolvePresetName() {
@@ -205,44 +298,46 @@ class S {
205
298
  }
206
299
  }
207
300
  async _streamIntoTarget(t, s, i, r) {
208
- var c, m, p, f, g, d, v, b, _, w, y, k, T, q;
301
+ var d, m, f, b, g, v, x, h, q, y, C, w, k, a, R, T;
209
302
  let e, n;
210
- 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);
211
304
  const o = r == null ? void 0 : r.name;
212
- o && e.addTag && e.addTag("provider", o), (p = e.addLLMLog) == null || p.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", {
213
306
  provider: r == null ? void 0 : r.provider,
214
307
  model: r == null ? void 0 : r.model,
215
308
  messages: t,
216
309
  options: s
217
- }), (f = e.setStreamingState) == null || f.call(e, !0), e._activeRequestId = s.requestId;
218
- let a = "", h = null, u = 0;
310
+ }), (b = e.setStreamingState) == null || b.call(e, !0), e._activeRequestId = s.requestId;
311
+ let l = "", p = null, c = null;
219
312
  try {
220
- return await i.streamRequest(t, s, (l) => {
221
- var x, C, R;
222
- l.content && (a = l.fullContent, (x = e.setText) == null || x.call(e, a)), (C = l.usage) != null && C.tokens && (u += l.usage.tokens), l.finishReason && (h = l.finishReason), l.done && ((R = e.setStreamingState) == null || R.call(e, !1), e._activeRequestId = null);
223
- }), (g = e.addLLMLog) == null || g.call(e, (this.operationName || "streaming-request") + "-response", { requestId: s.requestId }, { content: a, usage: { tokens: u } }), h === "length" ? ((d = e.setAttribute) == null || d.call(e, "_truncated", !0), (v = e.addLog) == null || v.call(e, "Response truncated. Increase Max Tokens.", "warn", { finish_reason: "length" })) : (b = e.setAttribute) == null || b.call(e, "_truncated", !1), await ((_ = e.persistNow) == null ? void 0 : _.call(e)), e;
224
- } catch (l) {
225
- throw (w = e.addLLMLog) == null || w.call(e, (this.operationName || "streaming-request") + "-error", { requestId: s.requestId }, null, l), (y = e.setStreamingState) == null || y.call(e, !1), e._activeRequestId = null, (k = e.setText) == null || k.call(e, `Error: ${l.message}`), (T = e.setAttribute) == null || T.call(e, "_truncated", !1), await ((q = e.persistNow) == null ? void 0 : q.call(e)), l;
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;
226
321
  }
227
322
  }
228
323
  async _promiseResponse(t, s, i, r) {
229
- var h, u, c, m, p, f, g;
324
+ var p, c, d, m, f, b, g;
230
325
  const e = i.prepareRequest(t, s), n = await i.makeRequest(e), o = i.processResponse(n);
231
- if ((o.finishReason || ((u = (h = n == null ? void 0 : n.choices) == null ? void 0 : h[0]) == null ? void 0 : u.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")
232
327
  try {
233
328
  this.contextNode.setAttribute("_truncated", !0);
234
- } catch (d) {
235
- (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 });
236
331
  }
237
332
  else
238
333
  try {
239
334
  this.contextNode.setAttribute("_truncated", !1);
240
- } catch (d) {
241
- (f = (p = this.client.logger) == null ? void 0 : p.warn) == null || f.call(p, "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 });
242
337
  }
243
338
  return ((g = o.content) == null ? void 0 : g.trim()) || "";
244
339
  }
245
340
  }
246
341
  export {
247
- A as LLMClient
342
+ P as LLMClient
248
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
+ };