@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 +64 -3
- package/dist/core/LLMClient.js +92 -63
- package/dist/index.js +37 -29
- package/dist/pricing/calculate.js +58 -0
- package/dist/pricing/index.js +10 -0
- package/dist/pricing/rates.js +80 -0
- package/dist/providers/AnthropicProvider.js +35 -26
- package/dist/providers/BaseProvider.js +84 -74
- package/dist/providers/CustomProvider.js +69 -28
- package/dist/providers/DeepSeekProvider.js +115 -0
- package/dist/providers/GeminiProvider.js +143 -111
- package/dist/providers/GrokProvider.js +75 -38
- package/dist/providers/LlamaServerProvider.js +72 -31
- package/dist/providers/OllamaProvider.js +117 -41
- package/dist/providers/OpenAIProvider.js +64 -57
- package/dist/providers/OpenRouterProvider.js +40 -40
- package/dist/providers/factory.js +12 -7
- package/dist/providers/index.js +10 -8
- package/package.json +7 -2
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
|
|
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
|
-
|
|
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
|
package/dist/core/LLMClient.js
CHANGED
|
@@ -1,12 +1,25 @@
|
|
|
1
|
-
import { createProviderFlexible as
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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 =
|
|
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
|
-
|
|
51
|
+
const t = {
|
|
39
52
|
totalTokens: 0,
|
|
40
53
|
totalCost: 0,
|
|
41
|
-
recordUsage: (
|
|
42
|
-
var
|
|
43
|
-
(
|
|
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: (
|
|
46
|
-
|
|
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, (
|
|
80
|
-
e =
|
|
81
|
-
|
|
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:
|
|
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
|
|
112
|
-
let
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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:
|
|
140
|
+
enableThinking: p ?? !1,
|
|
123
141
|
requestId: this.generateRequestId()
|
|
124
|
-
}),
|
|
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
|
-
}),
|
|
127
|
-
if (
|
|
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
|
|
156
|
+
for (const a of y) {
|
|
132
157
|
r && r({ type: "tool-call", id: a.id, name: a.name, args: a.args });
|
|
133
|
-
const
|
|
134
|
-
let
|
|
135
|
-
if (!
|
|
136
|
-
|
|
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
|
|
140
|
-
|
|
141
|
-
} catch (
|
|
142
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
215
|
+
return new $(r, t, n, o, s, i);
|
|
189
216
|
}
|
|
190
217
|
return e;
|
|
191
218
|
}
|
|
192
219
|
}
|
|
193
|
-
class
|
|
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
|
|
233
|
-
return await this._streamIntoTarget(e,
|
|
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((
|
|
252
|
-
throw new Error(`Provider preset '${t}' not found.${
|
|
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 =
|
|
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
|
|
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), (
|
|
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), (
|
|
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
|
-
}), (
|
|
284
|
-
let l = "",
|
|
310
|
+
}), (b = e.setStreamingState) == null || b.call(e, !0), e._activeRequestId = s.requestId;
|
|
311
|
+
let l = "", p = null, c = null;
|
|
285
312
|
try {
|
|
286
|
-
|
|
287
|
-
var
|
|
288
|
-
|
|
289
|
-
})
|
|
290
|
-
|
|
291
|
-
|
|
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
|
|
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 || ((
|
|
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 (
|
|
301
|
-
(m = (
|
|
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 (
|
|
307
|
-
(
|
|
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 ((
|
|
338
|
+
return ((g = o.content) == null ? void 0 : g.trim()) || "";
|
|
310
339
|
}
|
|
311
340
|
}
|
|
312
341
|
export {
|
|
313
|
-
|
|
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
|
|
3
|
-
import { KeyStore as p, maskApiKey as
|
|
4
|
-
import { LocalStorageAdapter as
|
|
5
|
-
import { DEFAULT_CONFIGS as
|
|
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
|
|
8
|
-
import { createDefaultConfig as
|
|
9
|
-
import { default as
|
|
10
|
-
import { default as
|
|
11
|
-
import { default as
|
|
12
|
-
const e = r({}), t = e.client,
|
|
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
|
-
|
|
15
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
36
|
+
_ as createProvider,
|
|
37
|
+
u as createProviderFlexible,
|
|
38
|
+
F as formatCost,
|
|
39
|
+
i as keyStore,
|
|
34
40
|
t as llmClient,
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
+
};
|