@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 +64 -3
- package/dist/core/LLMClient.js +141 -46
- 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 +90 -26
- package/dist/providers/BaseProvider.js +91 -54
- package/dist/providers/BedrockProvider.js +172 -0
- 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 +92 -38
- package/dist/providers/OpenRouterProvider.js +71 -38
- package/dist/providers/factory.js +28 -18
- package/dist/providers/index.js +13 -6
- package/dist/vue/components/LLMConfigModal.vue.js +27 -27
- package/dist/vue/components/ProviderSelector.vue.js +3 -3
- package/dist/vue/components/StoredKeysManager.vue.js +8 -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();
|
|
@@ -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),
|
|
32
|
-
return (r = this.provider.processResponse(
|
|
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
|
-
|
|
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,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, (
|
|
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 }) };
|
|
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
|
|
215
|
+
return new $(r, t, n, o, s, i);
|
|
123
216
|
}
|
|
124
217
|
return e;
|
|
125
218
|
}
|
|
126
219
|
}
|
|
127
|
-
class
|
|
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
|
|
167
|
-
return await this._streamIntoTarget(e,
|
|
259
|
+
const p = r({ ...n, stream: !0 });
|
|
260
|
+
return await this._streamIntoTarget(e, p, s, i);
|
|
168
261
|
}
|
|
169
|
-
const
|
|
170
|
-
return await this._promiseResponse(e,
|
|
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
|
|
186
|
-
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}`);
|
|
187
280
|
}
|
|
188
|
-
const i =
|
|
281
|
+
const i = U(s.provider, s);
|
|
189
282
|
return await i.initialize(), { provider: i, config: s, cleanup: () => {
|
|
190
|
-
var
|
|
191
|
-
return (
|
|
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
|
|
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), (
|
|
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), (
|
|
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
|
-
}), (
|
|
218
|
-
let
|
|
310
|
+
}), (b = e.setStreamingState) == null || b.call(e, !0), e._activeRequestId = s.requestId;
|
|
311
|
+
let l = "", p = null, c = null;
|
|
219
312
|
try {
|
|
220
|
-
|
|
221
|
-
var
|
|
222
|
-
|
|
223
|
-
})
|
|
224
|
-
|
|
225
|
-
|
|
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
|
|
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 || ((
|
|
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 (
|
|
235
|
-
(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 });
|
|
236
331
|
}
|
|
237
332
|
else
|
|
238
333
|
try {
|
|
239
334
|
this.contextNode.setAttribute("_truncated", !1);
|
|
240
|
-
} catch (
|
|
241
|
-
(
|
|
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
|
-
|
|
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
|
|
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
|
+
};
|