@anvaka/vue-llm 0.2.0 → 0.3.1
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 +81 -3
- package/dist/core/LLMClient.js +95 -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 +88 -0
- package/dist/providers/AnthropicProvider.js +139 -85
- 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 +8 -2
package/README.md
CHANGED
|
@@ -3,9 +3,11 @@
|
|
|
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)
|
|
10
|
+
- Automatic prompt caching for Claude (Anthropic + Bedrock) — caches the system+tools prefix, plus the rolling conversation in agent loops; opt out with `promptCache: false`
|
|
9
11
|
- Vue plugin for dependency injection
|
|
10
12
|
- `useLLM()` composable with reactive streaming state
|
|
11
13
|
- Ready-to-use components: `ProviderSelector`, `LLMConfigModal`, `StoredKeysManager`
|
|
@@ -121,13 +123,89 @@ For scripts outside Vue components, use the singleton exports:
|
|
|
121
123
|
import { llmClient, configStore, keyStore } from '@anvaka/vue-llm'
|
|
122
124
|
|
|
123
125
|
// Stream directly
|
|
124
|
-
|
|
126
|
+
const { content, usage, cost } = await llmClient.stream(
|
|
127
|
+
{ messages: [...] },
|
|
128
|
+
chunk => console.log(chunk.fullContent)
|
|
129
|
+
)
|
|
125
130
|
|
|
126
131
|
// Manage configs
|
|
127
132
|
configStore.saveConfig('my-provider', { ... })
|
|
128
133
|
configStore.setActiveProviderId('my-provider')
|
|
129
134
|
```
|
|
130
135
|
|
|
136
|
+
## Usage & Cost
|
|
137
|
+
|
|
138
|
+
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.
|
|
139
|
+
|
|
140
|
+
```js
|
|
141
|
+
const { content, usage, cost } = await client.stream({ messages: [...] })
|
|
142
|
+
|
|
143
|
+
usage // { inputTokens, outputTokens, totalTokens,
|
|
144
|
+
// cachedInputTokens?, cacheCreationInputTokens?, reasoningTokens?, raw }
|
|
145
|
+
cost // { total, input, cachedInput, cacheCreation, output, currency, rates }
|
|
146
|
+
// or null when the model isn't in the rates table
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
`runAgentLoop` aggregates usage across all iterations (each tool round-trip is one call) and emits a `usage` event per iteration:
|
|
150
|
+
|
|
151
|
+
```js
|
|
152
|
+
const { messages, usage, cost } = await client.runAgentLoop({
|
|
153
|
+
messages: [{ role: 'user', content: '7 * 11?' }],
|
|
154
|
+
tools, executors,
|
|
155
|
+
onEvent: ev => {
|
|
156
|
+
if (ev.type === 'usage') console.log(`iter cost: ${ev.cost?.total}`)
|
|
157
|
+
}
|
|
158
|
+
})
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Prompt caching (Claude)
|
|
162
|
+
|
|
163
|
+
Anthropic and Bedrock requests are sent with `cache_control` markers by default, so repeated prefixes are read from cache instead of re-billed at full input price. Two prefixes are tagged:
|
|
164
|
+
|
|
165
|
+
- **System + tools** — on every Claude request. The static prefix recurs identically across calls and runs within the cache TTL (~5 min).
|
|
166
|
+
- **Rolling conversation** — added by `runAgentLoop` only, since it re-sends the whole growing transcript each turn. Iteration *N* reads iterations `1..N-1` from cache and only writes the new turn.
|
|
167
|
+
|
|
168
|
+
Cache hits and writes surface in the usual `usage` fields (`cachedInputTokens`, `cacheCreationInputTokens`) and are priced via the `cachedInput` / `cacheCreation` rate keys. No setup is required — prefixes under the model's minimum cacheable length simply aren't cached (no error).
|
|
169
|
+
|
|
170
|
+
Disable it per call or per provider config:
|
|
171
|
+
|
|
172
|
+
```js
|
|
173
|
+
client.stream({ messages: [...], promptCache: false }) // single request
|
|
174
|
+
configStore.saveConfig('claude', { ...cfg, promptCache: false }) // all requests for this provider
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Overriding rates
|
|
178
|
+
|
|
179
|
+
Built-in rates are sourced from public pricing pages and *will* drift. Three ways to override, in priority order:
|
|
180
|
+
|
|
181
|
+
```js
|
|
182
|
+
// 1. Per-instance — wins over everything
|
|
183
|
+
import { LLMClient } from '@anvaka/vue-llm'
|
|
184
|
+
const client = new LLMClient({
|
|
185
|
+
pricing: {
|
|
186
|
+
openai: { 'gpt-4o': { input: 1.50, output: 6.00, cachedInput: 0.75 } }
|
|
187
|
+
}
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
// 2. Global runtime override
|
|
191
|
+
import { registerPricing } from '@anvaka/vue-llm'
|
|
192
|
+
registerPricing('openai', 'gpt-4o', { input: 1.50, output: 6.00 })
|
|
193
|
+
|
|
194
|
+
// 3. Inline (without LLMClient)
|
|
195
|
+
import { calculateCost } from '@anvaka/vue-llm/pricing'
|
|
196
|
+
const cost = calculateCost(usage, { provider: 'openai', model: 'gpt-4o' })
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
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).
|
|
200
|
+
|
|
201
|
+
Model lookup is exact match first, then longest-prefix match — so registering `claude-haiku-4-5` automatically covers `claude-haiku-4-5-20251001`.
|
|
202
|
+
|
|
203
|
+
```js
|
|
204
|
+
// Pricing-only import (no Vue):
|
|
205
|
+
import { calculateCost, formatCost, registerPricing, DEFAULT_RATES } from '@anvaka/vue-llm/pricing'
|
|
206
|
+
formatCost(0.00012) // "$0.000120"
|
|
207
|
+
```
|
|
208
|
+
|
|
131
209
|
## Theming
|
|
132
210
|
Override any `--llm-*` CSS variable globally or per container.
|
|
133
211
|
```css
|
|
@@ -150,7 +228,7 @@ registerProvider('my-provider', MyProvider)
|
|
|
150
228
|
// From '@anvaka/vue-llm/providers'
|
|
151
229
|
import {
|
|
152
230
|
BaseProvider,
|
|
153
|
-
PROVIDERS, // { OPENAI, ANTHROPIC, GROK, GEMINI, OLLAMA, LLAMA_SERVER, OPENROUTER, CUSTOM }
|
|
231
|
+
PROVIDERS, // { OPENAI, ANTHROPIC, BEDROCK, GROK, GEMINI, OLLAMA, LLAMA_SERVER, OPENROUTER, DEEPSEEK, CUSTOM }
|
|
154
232
|
DEFAULT_CONFIGS, // Default configs for each provider type
|
|
155
233
|
createProvider, // (type, config) => Provider
|
|
156
234
|
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,61 @@ class $ {
|
|
|
104
120
|
temperature: n,
|
|
105
121
|
model: o,
|
|
106
122
|
maxTokens: l,
|
|
107
|
-
enableThinking:
|
|
123
|
+
enableThinking: d
|
|
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 p = 0, m = null;
|
|
129
|
+
const f = { inputTokens: 0, outputTokens: 0, totalTokens: 0, cachedInputTokens: 0, cacheCreationInputTokens: 0, reasoningTokens: 0 };
|
|
130
|
+
let b = !1;
|
|
131
|
+
for (; p < e; ) {
|
|
132
|
+
p++, r && r({ type: "iter-start", iter: p });
|
|
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: d ?? !1,
|
|
141
|
+
// Re-sends the whole growing conversation each turn, so cache the
|
|
142
|
+
// rolling transcript prefix (Anthropic-family providers honor this).
|
|
143
|
+
cacheTranscript: !0,
|
|
123
144
|
requestId: this.generateRequestId()
|
|
124
|
-
}),
|
|
145
|
+
}), h = await this.provider.streamRequest(c, x, (a) => {
|
|
125
146
|
a.content && r && r({ type: "text-delta", text: a.content }), a.toolCallDelta && r && r({ type: "tool-call-delta", delta: a.toolCallDelta });
|
|
126
|
-
}),
|
|
127
|
-
if (
|
|
147
|
+
}), 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;
|
|
148
|
+
if (w) {
|
|
149
|
+
b = !0;
|
|
150
|
+
for (const a of Object.keys(f))
|
|
151
|
+
w[a] != null && (f[a] += w[a]);
|
|
152
|
+
r && r({ type: "usage", usage: w, cost: this.costFor(w, { provider: this.config.provider, model: x.model }) });
|
|
153
|
+
}
|
|
154
|
+
const k = { role: "assistant", content: q };
|
|
155
|
+
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
156
|
m = "no-tool-calls";
|
|
129
157
|
break;
|
|
130
158
|
}
|
|
131
|
-
for (const a of
|
|
159
|
+
for (const a of y) {
|
|
132
160
|
r && r({ type: "tool-call", id: a.id, name: a.name, args: a.args });
|
|
133
|
-
const
|
|
134
|
-
let
|
|
135
|
-
if (!
|
|
136
|
-
|
|
161
|
+
const R = i && i[a.name];
|
|
162
|
+
let T;
|
|
163
|
+
if (!R)
|
|
164
|
+
T = `Error: unknown tool '${a.name}'`;
|
|
137
165
|
else
|
|
138
166
|
try {
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
} catch (
|
|
142
|
-
|
|
167
|
+
const u = await R(a.args || {});
|
|
168
|
+
T = typeof u == "string" ? u : JSON.stringify(u ?? "");
|
|
169
|
+
} catch (u) {
|
|
170
|
+
T = `Error: ${(u == null ? void 0 : u.message) || String(u)}`;
|
|
143
171
|
}
|
|
144
|
-
|
|
172
|
+
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
173
|
}
|
|
146
174
|
}
|
|
147
|
-
|
|
175
|
+
m || (m = "max-iters");
|
|
176
|
+
const g = b ? f : null, v = this.costFor(g, { provider: this.config.provider, model: o || this.config.model });
|
|
177
|
+
return r && r({ type: "stop", reason: m, iterations: p, usage: g, cost: v }), { messages: c, iterations: p, stopReason: m, usage: g, cost: v };
|
|
148
178
|
}
|
|
149
179
|
generateRequestId() {
|
|
150
180
|
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
@@ -185,12 +215,12 @@ class $ {
|
|
|
185
215
|
createLLMWrapper(t, s = null, i = null) {
|
|
186
216
|
const r = this;
|
|
187
217
|
function e(n, o = {}) {
|
|
188
|
-
return new
|
|
218
|
+
return new $(r, t, n, o, s, i);
|
|
189
219
|
}
|
|
190
220
|
return e;
|
|
191
221
|
}
|
|
192
222
|
}
|
|
193
|
-
class
|
|
223
|
+
class $ {
|
|
194
224
|
constructor(t, s, i, r, e, n) {
|
|
195
225
|
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
226
|
}
|
|
@@ -229,8 +259,8 @@ class S {
|
|
|
229
259
|
};
|
|
230
260
|
try {
|
|
231
261
|
if (this.targetNode) {
|
|
232
|
-
const
|
|
233
|
-
return await this._streamIntoTarget(e,
|
|
262
|
+
const d = r({ ...n, stream: !0 });
|
|
263
|
+
return await this._streamIntoTarget(e, d, s, i);
|
|
234
264
|
}
|
|
235
265
|
const l = r({ ...n, stream: !1 });
|
|
236
266
|
return await this._promiseResponse(e, l, s, i);
|
|
@@ -248,10 +278,10 @@ class S {
|
|
|
248
278
|
throw new Error("Provider presets require a configured ConfigStore instance");
|
|
249
279
|
const s = (e = (r = this.client).getConfigByName) == null ? void 0 : e.call(r, t);
|
|
250
280
|
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.${
|
|
281
|
+
const l = (((o = (n = this.client.configStore).getEnabledConfigs) == null ? void 0 : o.call(n)) || []).map((c) => c.name || `${c.provider} (${c.baseUrl})`).filter(Boolean), d = l.length ? ` Available presets: ${l.join(", ")}` : " No configured providers found.";
|
|
282
|
+
throw new Error(`Provider preset '${t}' not found.${d}`);
|
|
253
283
|
}
|
|
254
|
-
const i =
|
|
284
|
+
const i = U(s.provider, s);
|
|
255
285
|
return await i.initialize(), { provider: i, config: s, cleanup: () => {
|
|
256
286
|
var l;
|
|
257
287
|
return (l = i.cancelAllRequests) == null ? void 0 : l.call(i);
|
|
@@ -271,44 +301,46 @@ class S {
|
|
|
271
301
|
}
|
|
272
302
|
}
|
|
273
303
|
async _streamIntoTarget(t, s, i, r) {
|
|
274
|
-
var
|
|
304
|
+
var p, m, f, b, g, v, x, h, q, y, C, w, k, a, R, T;
|
|
275
305
|
let e, n;
|
|
276
|
-
typeof this.targetNode == "string" ? (n = this.targetNode, e = this.contextNode.createChild(n), (
|
|
306
|
+
typeof this.targetNode == "string" ? (n = this.targetNode, e = this.contextNode.createChild(n), (p = e.setSelected) == null || p.call(e)) : (e = this.targetNode, n = ((m = e.getText) == null ? void 0 : m.call(e)) || ""), this._applyAttributes(e);
|
|
277
307
|
const o = r == null ? void 0 : r.name;
|
|
278
|
-
o && e.addTag && e.addTag("provider", o), (
|
|
308
|
+
o && e.addTag && e.addTag("provider", o), (f = e.addLLMLog) == null || f.call(e, this.operationName || "streaming-request", {
|
|
279
309
|
provider: r == null ? void 0 : r.provider,
|
|
280
310
|
model: r == null ? void 0 : r.model,
|
|
281
311
|
messages: t,
|
|
282
312
|
options: s
|
|
283
|
-
}), (
|
|
284
|
-
let l = "",
|
|
313
|
+
}), (b = e.setStreamingState) == null || b.call(e, !0), e._activeRequestId = s.requestId;
|
|
314
|
+
let l = "", d = null, c = null;
|
|
285
315
|
try {
|
|
286
|
-
|
|
287
|
-
var
|
|
288
|
-
|
|
289
|
-
})
|
|
290
|
-
|
|
291
|
-
|
|
316
|
+
await i.streamRequest(t, s, (_) => {
|
|
317
|
+
var I, S;
|
|
318
|
+
_.content && (l = _.fullContent, (I = e.setText) == null || I.call(e, l)), _.fullUsage && (c = _.fullUsage), _.finishReason && (d = _.finishReason), _.done && ((S = e.setStreamingState) == null || S.call(e, !1), e._activeRequestId = null);
|
|
319
|
+
});
|
|
320
|
+
const u = (v = (g = this.client).costFor) == null ? void 0 : v.call(g, c, { provider: r == null ? void 0 : r.provider, model: s.model });
|
|
321
|
+
return (x = e.addLLMLog) == null || x.call(e, (this.operationName || "streaming-request") + "-response", { requestId: s.requestId }, { content: l, usage: c, cost: u }), d === "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;
|
|
322
|
+
} catch (u) {
|
|
323
|
+
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
324
|
}
|
|
293
325
|
}
|
|
294
326
|
async _promiseResponse(t, s, i, r) {
|
|
295
|
-
var
|
|
327
|
+
var d, c, p, m, f, b, g;
|
|
296
328
|
const e = i.prepareRequest(t, s), n = await i.makeRequest(e), o = i.processResponse(n);
|
|
297
|
-
if ((o.finishReason || ((
|
|
329
|
+
if ((o.finishReason || ((c = (d = n == null ? void 0 : n.choices) == null ? void 0 : d[0]) == null ? void 0 : c.finish_reason) || null) === "length")
|
|
298
330
|
try {
|
|
299
331
|
this.contextNode.setAttribute("_truncated", !0);
|
|
300
|
-
} catch (
|
|
301
|
-
(m = (
|
|
332
|
+
} catch (v) {
|
|
333
|
+
(m = (p = this.client.logger) == null ? void 0 : p.warn) == null || m.call(p, "Unable to mark node as truncated", { error: v });
|
|
302
334
|
}
|
|
303
335
|
else
|
|
304
336
|
try {
|
|
305
337
|
this.contextNode.setAttribute("_truncated", !1);
|
|
306
|
-
} catch (
|
|
307
|
-
(
|
|
338
|
+
} catch (v) {
|
|
339
|
+
(b = (f = this.client.logger) == null ? void 0 : f.warn) == null || b.call(f, "Unable to clear truncated marker on node", { error: v });
|
|
308
340
|
}
|
|
309
|
-
return ((
|
|
341
|
+
return ((g = o.content) == null ? void 0 : g.trim()) || "";
|
|
310
342
|
}
|
|
311
343
|
}
|
|
312
344
|
export {
|
|
313
|
-
|
|
345
|
+
P as LLMClient
|
|
314
346
|
};
|
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,88 @@
|
|
|
1
|
+
const t = {
|
|
2
|
+
openai: {
|
|
3
|
+
// Flagship + workhorse (April–May 2026 lineup)
|
|
4
|
+
"gpt-5.5-pro": { input: 5, output: 30, cachedInput: 0.5 },
|
|
5
|
+
"gpt-5.5": { input: 5, output: 30, cachedInput: 0.5 },
|
|
6
|
+
"gpt-5.4-pro": { input: 30, output: 180 },
|
|
7
|
+
"gpt-5.4": { input: 2.5, output: 15, cachedInput: 0.25 },
|
|
8
|
+
"gpt-5.4-mini": { input: 0.75, output: 4.5, cachedInput: 0.075 },
|
|
9
|
+
"gpt-5.4-nano": { input: 0.2, output: 1.25, cachedInput: 0.02 },
|
|
10
|
+
// GPT-5.x family — still widely deployed
|
|
11
|
+
"gpt-5.3": { input: 1.75, output: 14, cachedInput: 0.175 },
|
|
12
|
+
"gpt-5.2-pro": { input: 10.5, output: 84 },
|
|
13
|
+
"gpt-5.2": { input: 1.75, output: 14, cachedInput: 0.175 },
|
|
14
|
+
"gpt-5.1": { input: 0.625, output: 5, cachedInput: 0.125 },
|
|
15
|
+
"gpt-5-chat": { input: 1.25, output: 10, cachedInput: 0.125 },
|
|
16
|
+
"gpt-5": { input: 0.625, output: 5, cachedInput: 0.125 },
|
|
17
|
+
"gpt-5-mini": { input: 0.25, output: 2, cachedInput: 0.025 },
|
|
18
|
+
"gpt-5-nano": { input: 0.05, output: 0.4, cachedInput: 5e-3 },
|
|
19
|
+
// Reasoning models
|
|
20
|
+
"o4-mini": { input: 1.1, output: 4.4, cachedInput: 0.275 },
|
|
21
|
+
o3: { input: 2, output: 8, cachedInput: 0.5 },
|
|
22
|
+
"o3-mini": { input: 1.1, output: 4.4, cachedInput: 0.55 },
|
|
23
|
+
// Codex variants
|
|
24
|
+
"gpt-5.1-codex": { input: 1.25, output: 10, cachedInput: 0.125 },
|
|
25
|
+
"gpt-5.1-codex-mini": { input: 0.25, output: 2, cachedInput: 0.025 },
|
|
26
|
+
"gpt-5-codex": { input: 1.25, output: 10, cachedInput: 0.125 }
|
|
27
|
+
},
|
|
28
|
+
anthropic: {
|
|
29
|
+
"claude-opus-4-7": { input: 5, output: 25, cachedInput: 0.5, cacheCreation: 6.25 },
|
|
30
|
+
"claude-opus-4-6": { input: 5, output: 25, cachedInput: 0.5, cacheCreation: 6.25 },
|
|
31
|
+
"claude-sonnet-4-6": { input: 3, output: 15, cachedInput: 0.3, cacheCreation: 3.75 },
|
|
32
|
+
"claude-sonnet-4-5": { input: 3, output: 15, cachedInput: 0.3, cacheCreation: 3.75 },
|
|
33
|
+
"claude-haiku-4-5": { input: 1, output: 5, cachedInput: 0.1, cacheCreation: 1.25 }
|
|
34
|
+
},
|
|
35
|
+
// Bedrock charges the same per-token rates as Anthropic direct for the
|
|
36
|
+
// Claude inference profiles; the model IDs differ (us.anthropic.* prefix).
|
|
37
|
+
bedrock: {
|
|
38
|
+
"us.anthropic.claude-opus-4-7": { input: 5, output: 25, cachedInput: 0.5, cacheCreation: 6.25 },
|
|
39
|
+
"us.anthropic.claude-opus-4-6": { input: 5, output: 25, cachedInput: 0.5, cacheCreation: 6.25 },
|
|
40
|
+
"us.anthropic.claude-sonnet-4-6": { input: 3, output: 15, cachedInput: 0.3, cacheCreation: 3.75 },
|
|
41
|
+
"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 }
|
|
43
|
+
},
|
|
44
|
+
gemini: {
|
|
45
|
+
// Gemini 3.x — current lineup (May 2026)
|
|
46
|
+
"gemini-3.5-flash": { input: 1.5, output: 9, cachedInput: 0.15 },
|
|
47
|
+
"gemini-3.1-pro-preview": { input: 2, output: 12, cachedInput: 0.2 },
|
|
48
|
+
"gemini-3.1-flash-lite-preview": { input: 0.25, output: 1.5, cachedInput: 0.025 },
|
|
49
|
+
"gemini-3-pro-preview": { input: 2, output: 12 },
|
|
50
|
+
"gemini-3-flash-preview": { input: 0.5, output: 3, cachedInput: 0.05 },
|
|
51
|
+
// Gemini 2.5 — still in active use
|
|
52
|
+
"gemini-2.5-pro": { input: 1, output: 10, cachedInput: 0.125 },
|
|
53
|
+
"gemini-2.5-flash": { input: 0.3, output: 2.5, cachedInput: 0.03 },
|
|
54
|
+
"gemini-2.5-flash-lite": { input: 0.1, output: 0.4, cachedInput: 0.01 }
|
|
55
|
+
},
|
|
56
|
+
grok: {
|
|
57
|
+
// Grok 4.3 (April 30, 2026 release) — current primary, 1M context
|
|
58
|
+
"grok-4.3": { input: 1.25, output: 2.5, cachedInput: 0.2 },
|
|
59
|
+
"grok-4.20-0309-reasoning": { input: 1.25, output: 2.5, cachedInput: 0.2 },
|
|
60
|
+
"grok-4.20-0309-non-reasoning": { input: 1.25, output: 2.5, cachedInput: 0.2 },
|
|
61
|
+
"grok-4.20-multi-agent-0309": { input: 1.25, output: 2.5, cachedInput: 0.2 },
|
|
62
|
+
"grok-4.20": { input: 1.25, output: 2.5, cachedInput: 0.2 },
|
|
63
|
+
"grok-build-0.1": { input: 1, output: 2 },
|
|
64
|
+
// Cheap fast tier
|
|
65
|
+
"grok-4-fast": { input: 0.2, output: 0.5 },
|
|
66
|
+
"grok-code-fast-1": { input: 0.2, output: 1.5 }
|
|
67
|
+
},
|
|
68
|
+
deepseek: {
|
|
69
|
+
"deepseek-v4-pro": { input: 0.435, output: 0.87, cachedInput: 36e-4 },
|
|
70
|
+
"deepseek-v4-flash": { input: 0.1, output: 0.2, cachedInput: 28e-4 },
|
|
71
|
+
"deepseek-v3.2": { input: 0.252, output: 0.378, cachedInput: 0.0252 },
|
|
72
|
+
"deepseek-v3.2-thinking": { input: 0.252, output: 0.378, cachedInput: 0.0252 },
|
|
73
|
+
"deepseek-chat": { input: 0.014, output: 0.028 },
|
|
74
|
+
"deepseek-reasoner": { input: 0.55, output: 2 }
|
|
75
|
+
},
|
|
76
|
+
// OpenRouter prices vary per upstream model and include their own margin —
|
|
77
|
+
// ship empty; callers can either register their own rates or use
|
|
78
|
+
// OpenRouterProvider.discoverModelsWithMetadata() (which returns pricing).
|
|
79
|
+
openrouter: {},
|
|
80
|
+
// Local inference — free at the per-token layer.
|
|
81
|
+
ollama: {},
|
|
82
|
+
"llama-server": {},
|
|
83
|
+
// Custom OpenAI-compatible endpoints: unknown by definition.
|
|
84
|
+
custom: {}
|
|
85
|
+
};
|
|
86
|
+
export {
|
|
87
|
+
t as DEFAULT_RATES
|
|
88
|
+
};
|