@floez-werk/piagent-realtime-provider-cost 0.11.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/LICENSE +21 -0
- package/README.md +374 -0
- package/extensions/realtime-provider-cost.ts +673 -0
- package/package.json +64 -0
- package/src/color.ts +151 -0
- package/src/currency.ts +162 -0
- package/src/endpoint-pricing.ts +191 -0
- package/src/format.ts +84 -0
- package/src/icons.ts +71 -0
- package/src/model-routing.ts +50 -0
- package/src/pricing.ts +308 -0
- package/src/provider-cache.ts +178 -0
- package/src/rates.ts +58 -0
- package/src/settings.ts +183 -0
- package/src/upstream.ts +117 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 FloezWerk
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
# piagent-realtime-provider-cost
|
|
2
|
+
|
|
3
|
+
Shows the **effective token prices (input/output, per 1M tokens)** of the
|
|
4
|
+
**last API call** in the Pi status bar – right next to the session cost sum.
|
|
5
|
+
|
|
6
|
+
Unlike the core footer's cost sum, these are the rates **actually billed by
|
|
7
|
+
OpenRouter** (including provider routing, discounts and peak overrides), not the
|
|
8
|
+
catalogue prices from `models-store.json`. For OpenRouter models the **serving
|
|
9
|
+
provider** is appended as a tag.
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
↑$2/↓$12 (Fir) # arrows (Unicode, full size in every font), tag = Fireworks
|
|
13
|
+
in:$2/out:$12 # ASCII mode (icons: ascii)
|
|
14
|
+
↑$2/↓$12 (⟳) # provider/costs are currently resolved via the generation API
|
|
15
|
+
↑$1.5/↓$6 (?) # model just switched: catalogue prices, provider not known yet
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
**Colour:** the in/out **icons** are **coloured by their deviation from the
|
|
19
|
+
catalogue price** (green/yellow/orange/red, see [Colours](#colours)); the numbers
|
|
20
|
+
and the provider tag stay in the base colour (**white**). When a **provider
|
|
21
|
+
switch** is detected, the **whole** item is drawn **bold gold** (`bold:#ffd700`)
|
|
22
|
+
for one prompt – the deviation colours do not apply then.
|
|
23
|
+
|
|
24
|
+
All values are **per 1M tokens** in the configured currency.
|
|
25
|
+
|
|
26
|
+
## Contents
|
|
27
|
+
|
|
28
|
+
- [Installation](#installation)
|
|
29
|
+
- [Setup](#setup)
|
|
30
|
+
- [Without pi-powerline-footer](#without-pi-powerline-footer)
|
|
31
|
+
- [With pi-powerline-footer (recommended)](#with-pi-powerline-footer-recommended)
|
|
32
|
+
- [Colours](#colours)
|
|
33
|
+
- [Commands](#commands)
|
|
34
|
+
- [Configuration](#configuration)
|
|
35
|
+
- [Icons](#icons)
|
|
36
|
+
- [Rounding](#rounding)
|
|
37
|
+
- [How it works](#how-it-works)
|
|
38
|
+
- [Background: why the generation API detour?](#background-why-the-generation-api-detour)
|
|
39
|
+
- [Dependencies](#dependencies)
|
|
40
|
+
- [License](#license)
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
From npm (scoped as `@floez-werk`):
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pi install npm:@floez-werk/piagent-realtime-provider-cost
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Or directly from the repository:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pi install git:git@github.com:FloezWerk/piagent-realtime-provider-cost.git
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
> This repository is developed against a local Gitea instance and mirrored to
|
|
57
|
+
> the public GitHub repository `FloezWerk/piagent-realtime-provider-cost`.
|
|
58
|
+
> Installation instructions always reference the **GitHub** URL – the Gitea path
|
|
59
|
+
> is internal and must not appear in user-facing docs.
|
|
60
|
+
|
|
61
|
+
Local/development:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
pi -e ./extensions/realtime-provider-cost.ts
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
After changes in a running session: `/reload`.
|
|
68
|
+
|
|
69
|
+
Afterwards the extension is active immediately – **without further
|
|
70
|
+
configuration** the value appears in the footer next to the cost sum. If you use
|
|
71
|
+
the Powerline bar, the item should additionally be hooked in there (next
|
|
72
|
+
section).
|
|
73
|
+
|
|
74
|
+
## Setup
|
|
75
|
+
|
|
76
|
+
### Without pi-powerline-footer
|
|
77
|
+
|
|
78
|
+
Works standalone: `ctx.ui.setStatus` is a core API, the core footer shows the
|
|
79
|
+
value as its own line below the status line.
|
|
80
|
+
|
|
81
|
+
### With pi-powerline-footer (recommended)
|
|
82
|
+
|
|
83
|
+
Add a custom item that reads the `realtime-provider-cost` status channel:
|
|
84
|
+
|
|
85
|
+
```jsonc
|
|
86
|
+
{
|
|
87
|
+
"powerline": {
|
|
88
|
+
"preset": "default",
|
|
89
|
+
"customItems": [
|
|
90
|
+
{
|
|
91
|
+
"id": "provider-cost",
|
|
92
|
+
"statusKey": "realtime-provider-cost",
|
|
93
|
+
"position": "right",
|
|
94
|
+
"color": "warning",
|
|
95
|
+
"selfColorize": true,
|
|
96
|
+
"hideWhenMissing": true,
|
|
97
|
+
"excludeFromExtensionStatuses": true
|
|
98
|
+
}
|
|
99
|
+
]
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Explicit positioning right next to `cost` via `powerline.layout`:
|
|
105
|
+
|
|
106
|
+
```jsonc
|
|
107
|
+
{
|
|
108
|
+
"powerline": {
|
|
109
|
+
"layout": {
|
|
110
|
+
"left": ["model", "thinking", "shell_mode", "path", "git", "queue", "context_pct", "cache_read", "cost", "custom:provider-cost"]
|
|
111
|
+
},
|
|
112
|
+
"customItems": [
|
|
113
|
+
{ "id": "provider-cost", "statusKey": "realtime-provider-cost", "selfColorize": true }
|
|
114
|
+
]
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
> **Important:** set `selfColorize: true`. Otherwise Powerline strips the item's
|
|
120
|
+
> ANSI colour codes and colours it itself – the dynamic white/gold switch on a
|
|
121
|
+
> provider change would be lost.
|
|
122
|
+
|
|
123
|
+
## Colours
|
|
124
|
+
|
|
125
|
+
There are two layers:
|
|
126
|
+
|
|
127
|
+
1. **Deviation colour coding.** The effective rate is compared with the model's
|
|
128
|
+
**catalogue price** (`models-store.json`); the colour shows the deviation. What
|
|
129
|
+
gets coloured are the **icons/arrows** (in and out separately); the numbers
|
|
130
|
+
themselves stay in the base colour so they remain readable on dark backgrounds.
|
|
131
|
+
The provider tag is also in the base colour.
|
|
132
|
+
|
|
133
|
+
Thresholds are configurable (setting `deviationThresholds`, command
|
|
134
|
+
`/provider-cost threshold …`); the percentages below are the defaults:
|
|
135
|
+
|
|
136
|
+
| Deviation from catalogue price | Colour |
|
|
137
|
+
| --- | --- |
|
|
138
|
+
| more than `green` % **cheaper** (< −10 %) | **green** |
|
|
139
|
+
| up to `yellow` % more expensive (0 % < x ≤ 10 %) | **yellow** |
|
|
140
|
+
| `yellow`–`orange` % more expensive (> 10 % and ≤ 20 %) | **orange** (256-colour 208) |
|
|
141
|
+
| more than `orange` % more expensive (> 20 %) | **red** |
|
|
142
|
+
| otherwise (0 %, up to `green` % cheaper, or no catalogue price known) | base colour |
|
|
143
|
+
|
|
144
|
+
> In and out are coloured **individually** (e.g. input arrow green, output
|
|
145
|
+
> arrow red). If there is no effective price or no catalogue price (e.g. `?`),
|
|
146
|
+
> the base colour stays.
|
|
147
|
+
|
|
148
|
+
Example: `↑`green `$2` / `↓`red `$12` (numbers white).
|
|
149
|
+
|
|
150
|
+
**Readability.** A terminal cannot draw an outline/stroke around glyphs (pure
|
|
151
|
+
font rendering). Therefore the **icon** carries the deviation colour and the
|
|
152
|
+
number stays neutral. `deviationStyle` controls the icon's SGR attributes:
|
|
153
|
+
|
|
154
|
+
| `deviationStyle` | Effect |
|
|
155
|
+
| --- | --- |
|
|
156
|
+
| `plain` (default) | plain foreground colour on the arrow |
|
|
157
|
+
| `bold` | arrow bolder/brighter (`SGR 1`) |
|
|
158
|
+
| `reverse` | arrow as a coloured block (`SGR 7`) |
|
|
159
|
+
|
|
160
|
+
Set via `/provider-cost style <plain|bold|reverse>` or setting
|
|
161
|
+
`deviationStyle`.
|
|
162
|
+
|
|
163
|
+
2. **Base/switch colour** for everything else. **White** by default; when a
|
|
164
|
+
**provider switch** is detected the **whole** item is drawn **bold gold**
|
|
165
|
+
(`bold:#ffd700`) for one prompt and overrides the deviation colours. Set via
|
|
166
|
+
`/provider-cost color …` / `/provider-cost switchColor …` or the settings
|
|
167
|
+
`color` / `switchColor`.
|
|
168
|
+
|
|
169
|
+
Colour specs are freely choosable:
|
|
170
|
+
|
|
171
|
+
| Syntax | Example | Result |
|
|
172
|
+
| --- | --- | --- |
|
|
173
|
+
| Palette | `white`, `yellow`, `orange`, `red`, `green`, `cyan`, `magenta`, `blue`, `gray`, `none` | SGR 97/93/38;5;208/91/92/96/95/94/90 |
|
|
174
|
+
| Hex (truecolor) | `#ffd700`, `#fd0` | `38;2;r;g;b` |
|
|
175
|
+
| 256-colour | `226` (0-255) | `38;5;n` |
|
|
176
|
+
| Bold | `bold:yellow`, `bold:#ffd700`, `bold:226` | `1;<colour>` |
|
|
177
|
+
| Reverse | `reverse:red`, `reverse:orange` | `7;<colour>` (colour becomes the background) |
|
|
178
|
+
| Combined | `bold:reverse:red` | `1;7;<colour>` |
|
|
179
|
+
|
|
180
|
+
These are **not** CSS names and **not** theme names (`warning`, `error`, …) from
|
|
181
|
+
the Pi/Powerline theme world – the extension colours in ANSI itself so it can
|
|
182
|
+
switch dynamically (see `selfColorize` above).
|
|
183
|
+
|
|
184
|
+
Common alternatives for the switch highlight:
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
/provider-cost switchColor bold:#ffd700 # default: bold gold (truecolor)
|
|
188
|
+
/provider-cost switchColor bold:220 # gold, 256-colour (available everywhere)
|
|
189
|
+
/provider-cost switchColor bold:226 # pure yellow, 256-colour
|
|
190
|
+
/provider-cost switchColor bold:yellow # bold bright yellow
|
|
191
|
+
/provider-cost color none # no colouring at all
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Commands
|
|
195
|
+
|
|
196
|
+
| Command | Effect |
|
|
197
|
+
| --- | --- |
|
|
198
|
+
| `/provider-cost` or `/provider-cost status` | State, currency, icons, colours, lookup+refresh interval, display, tag/source, cache age in prompts |
|
|
199
|
+
| `/provider-cost on` | Enable the display (persisted) |
|
|
200
|
+
| `/provider-cost off` | Disable the display (persisted) |
|
|
201
|
+
| `/provider-cost toggle` | Toggle (persisted) |
|
|
202
|
+
| `/provider-cost refresh` | Reload exchange rates **and** re-resolve provider/costs via the generation API (if possible) |
|
|
203
|
+
| `/provider-cost currency <CODE>` | Set the display currency (persisted) |
|
|
204
|
+
| `/provider-cost icons <auto\|nerd\|ascii>` | Set the icon mode (persisted) |
|
|
205
|
+
| `/provider-cost color <spec>` | Set the base colour, e.g. `white`, `#ffd700`, `226`, `bold:yellow` (persisted, see [Colours](#colours)) |
|
|
206
|
+
| `/provider-cost switchColor <spec>` | Set the switch colour (provider change) (persisted, see [Colours](#colours)) |
|
|
207
|
+
| `/provider-cost style <plain\|bold\|reverse>` | Attributes of the deviation colour on the in/out icon (persisted; default `plain`) |
|
|
208
|
+
| `/provider-cost threshold <green\|yellow\|orange> <pct>` | Set a deviation threshold in percent (persisted; defaults 10/10/20) |
|
|
209
|
+
| `/provider-cost lookup <on\|off\|refresh>` | Provider resolution on/off; `refresh` clears the provider **and** pricing cache and re-resolves |
|
|
210
|
+
| `/provider-cost notify <on\|off>` | Notification for every automatic generation-API request (persisted; default `off`) |
|
|
211
|
+
|
|
212
|
+
## Configuration
|
|
213
|
+
|
|
214
|
+
In `~/.pi/agent/settings.json` under the root key `realtime-provider-cost`
|
|
215
|
+
(matching the extension name). Other keys are left untouched; every value can
|
|
216
|
+
also be set via a command.
|
|
217
|
+
|
|
218
|
+
```jsonc
|
|
219
|
+
{
|
|
220
|
+
"realtime-provider-cost": {
|
|
221
|
+
"enabled": true,
|
|
222
|
+
"currency": "EUR",
|
|
223
|
+
"icons": "nerd",
|
|
224
|
+
"color": "white",
|
|
225
|
+
"switchColor": "bold:#ffd700",
|
|
226
|
+
"deviationStyle": "plain",
|
|
227
|
+
"deviationThresholds": { "green": 10, "yellow": 10, "orange": 20 },
|
|
228
|
+
"lookupUpstreamProvider": true,
|
|
229
|
+
"providerCacheRefreshPrompts": 10,
|
|
230
|
+
"notifyGenerationLookup": false
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
| Field | Default | Description |
|
|
236
|
+
| --- | --- | --- |
|
|
237
|
+
| `enabled` | `true` | Display on/off |
|
|
238
|
+
| `currency` | `"USD"` | `USD`, `CNY`, `EUR`, `GBP`, `JPY`, `CAD`, `AUD`, `CHF`, `INR`, `KRW` |
|
|
239
|
+
| `icons` | `"auto"` | `auto` (terminal heuristic), `nerd`, `ascii` – `nerd`/`auto` use `↑`/`↓`, `ascii` uses `in:`/`out:` |
|
|
240
|
+
| `color` | `"white"` | Base colour: palette name, `#rrggbb` or `0-255`, optionally with `bold:` |
|
|
241
|
+
| `switchColor` | `"bold:#ffd700"` | Colour right after a detected provider change |
|
|
242
|
+
| `deviationStyle` | `"plain"` | SGR attributes of the deviation colour on the icon: `plain`, `bold`, `reverse` |
|
|
243
|
+
| `deviationThresholds` | `{green:10, yellow:10, orange:20}` | Percentage thresholds: below `-green` green, up to `yellow` yellow, up to `orange` orange, above red (all ≥ 0) |
|
|
244
|
+
| `lookupUpstreamProvider` | `true` | Provider/cost resolution active (routing constraint + cache + generation API) |
|
|
245
|
+
| `providerCacheRefreshPrompts` | `10` | After this many **prompts** (user turns) a `generation` cache entry is refreshed; `0` = always re-resolve |
|
|
246
|
+
| `notifyGenerationLookup` | `false` | Notify before every automatic generation-API request (with reason) |
|
|
247
|
+
|
|
248
|
+
### Icons
|
|
249
|
+
|
|
250
|
+
1. Env `PROVIDER_COST_NERD_FONTS=1` (nerd) / `=0` (ascii)
|
|
251
|
+
2. Config `icons`
|
|
252
|
+
3. `auto`: heuristic like Powerline (`GHOSTTY_RESOURCES_DIR` or
|
|
253
|
+
`TERM_PROGRAM`/`TERM` ∈ iterm, wezterm, kitty, ghostty, alacritty, kaku)
|
|
254
|
+
|
|
255
|
+
Many terminals only set `TERM=xterm-256color` → `auto` yields ASCII
|
|
256
|
+
(`in:`/`out:`); for icons use `icons: "nerd"` or `/provider-cost icons nerd`.
|
|
257
|
+
|
|
258
|
+
> The previously used Nerd Font arrows (`U+F090`/`U+F08B`) were replaced by
|
|
259
|
+
> `↑`/`↓` because private-use glyphs are rendered noticeably smaller.
|
|
260
|
+
|
|
261
|
+
### Rounding
|
|
262
|
+
|
|
263
|
+
Rounded to **at most 4 decimal places**, trailing zeros removed (`$2`, `$12.5`,
|
|
264
|
+
`$0.2896`).
|
|
265
|
+
|
|
266
|
+
## How it works
|
|
267
|
+
|
|
268
|
+
- **Real billing instead of catalogue price.** The source of the numbers is
|
|
269
|
+
OpenRouter: the **generation API** returns `total_cost` (actually charged) and
|
|
270
|
+
the token counts, and the **endpoint prices** provide the bucket ratio
|
|
271
|
+
(input/output/cache) of the provider that actually served the request:
|
|
272
|
+
|
|
273
|
+
```
|
|
274
|
+
modelled = prompt*in + completion*out + cacheRead*cacheReadTokens (from endpoint prices)
|
|
275
|
+
factor = total_cost / modelled # discounts, peak overrides, price changes
|
|
276
|
+
in-rate = prompt * factor (USD per 1M tokens)
|
|
277
|
+
out-rate = completion * factor
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
The `factor` makes the display match the invoice even when endpoint prices do
|
|
281
|
+
not (yet) exactly match the billed rate. As long as no API data is available,
|
|
282
|
+
the approximation from `usage.cost.*` (Pi catalogue) is shown.
|
|
283
|
+
|
|
284
|
+
- **Resolution.** Order:
|
|
285
|
+
1. **Routing constraint** from `models.json`
|
|
286
|
+
(`providers.openrouter.modelOverrides.<model>.compat.openRouterRouting.only`)
|
|
287
|
+
– only counts as *certain* when `allow_fallbacks: false` is set. With
|
|
288
|
+
`allow_fallbacks: true` (OpenRouter default) another provider may serve even
|
|
289
|
+
if `only` names exactly one.
|
|
290
|
+
2. **Persistent rate cache** (`~/.pi/agent/realtime-provider-cost/provider-cache.json`),
|
|
291
|
+
key = request model. Entries expire after `providerCacheRefreshPrompts`
|
|
292
|
+
**prompts** (not by time).
|
|
293
|
+
3. **Generation API** `GET https://openrouter.ai/api/v1/generation?id=<responseId>`
|
|
294
|
+
– returns the provider **and** the real amount, at most 1 call per model at a
|
|
295
|
+
time. Data is only available a few seconds after the call → retry with
|
|
296
|
+
backoff (1s/2s/4s/8s). Meanwhile an **"update in progress" icon** (`⟳`) is
|
|
297
|
+
shown instead of the provider tag.
|
|
298
|
+
|
|
299
|
+
**When is the API called?**
|
|
300
|
+
- Provider *certain* (one `only` entry **and** `allow_fallbacks: false`) →
|
|
301
|
+
once on the first call, afterwards only every N prompts (rate cache).
|
|
302
|
+
- Provider *not certain* (`allow_fallbacks: true` or no `only`) →
|
|
303
|
+
**per response**, because only the actual provider counts.
|
|
304
|
+
- **Model switch** (different request model than the previous call) → forced
|
|
305
|
+
refresh of provider **and** costs, even if the cache would still be fresh. A
|
|
306
|
+
session restore with the same model is not a switch.
|
|
307
|
+
|
|
308
|
+
**Notification.** (Optional, default **off**: setting
|
|
309
|
+
`notifyGenerationLookup` or `/provider-cost notify on`.) Every automatically
|
|
310
|
+
triggered generation-API request is reported as a notify, including the reason
|
|
311
|
+
in parentheses, e.g.
|
|
312
|
+
`Generation API: resolving provider/costs for deepseek/… (cache miss).`
|
|
313
|
+
Reasons: `cache miss`, `cache expired (N prompts)`, `cache without rates`,
|
|
314
|
+
`stale cache`, `model switch`, `manual refresh` (`/provider-cost refresh`),
|
|
315
|
+
`cache cleared` (`/provider-cost lookup refresh`).
|
|
316
|
+
|
|
317
|
+
- **Preview on model switch.** When the model is switched (`model_select`), the
|
|
318
|
+
**catalogue prices** (`models-store.json`) of the new model are shown
|
|
319
|
+
immediately. The serving provider is not known at that point yet (the first
|
|
320
|
+
call of the new model is still running) → the tag shows `?`. As soon as the
|
|
321
|
+
first response arrives, the real value (including provider tag, or `⟳` while
|
|
322
|
+
resolving) replaces the preview. Tiered pricing is not applied in the preview –
|
|
323
|
+
without token counts only the base rates are known.
|
|
324
|
+
- **`/provider-cost refresh`** → reloads the exchange rates **and** forces a
|
|
325
|
+
generation-API call (provider + costs) when possible (OpenRouter,
|
|
326
|
+
`lookupUpstreamProvider` active, `responseId` present, no lookup already
|
|
327
|
+
running). Clear the cache first with `/provider-cost lookup refresh`.
|
|
328
|
+
|
|
329
|
+
The prompt counter is persisted in the cache and survives restarts. Example
|
|
330
|
+
with `providerCacheRefreshPrompts: 10`: resolution on the 1st prompt, then
|
|
331
|
+
again after the 10th further prompt.
|
|
332
|
+
|
|
333
|
+
- **Invalidation by call counter:** there is no signal that reveals a provider
|
|
334
|
+
switch per response (model slug, `system_fingerprint`, `native_finish_reason`,
|
|
335
|
+
`service_tier` are provider-independent). With a certain provider it is stable
|
|
336
|
+
in the short term, so resolving every N prompts suffices; otherwise it is
|
|
337
|
+
queried per response.
|
|
338
|
+
- **Two caches:** `provider-cache.json` (provider + rates per model) and
|
|
339
|
+
`endpoint-pricing.json` (provider price lists, 24 h). `/provider-cost lookup refresh`
|
|
340
|
+
clears both.
|
|
341
|
+
- **No batch endpoint:** OpenRouter offers neither multiple IDs nor a generations
|
|
342
|
+
list; `/api/v1/activity` requires a management key.
|
|
343
|
+
- **While streaming** the last known value stays; it is only updated on
|
|
344
|
+
`message_end`.
|
|
345
|
+
- **Failover:** if one side is not computable (e.g. `usage.input == 0`) or the
|
|
346
|
+
conversion rate is missing, `?` is shown per side.
|
|
347
|
+
- **Subscription providers** (OAuth or `kimi-coding`) → the item is hidden.
|
|
348
|
+
- **Free models** are shown as `$0/$0`.
|
|
349
|
+
- **Currency conversion** mirrors `pi-powerline-footer` (same source, 24h cache,
|
|
350
|
+
own file `…/realtime-provider-cost/currency-rates.json`); independent of
|
|
351
|
+
Powerline.
|
|
352
|
+
- **No interference with Pi:** the extension replaces neither the footer nor the
|
|
353
|
+
cost calculation; the session cost sum next to it remains Pi's catalogue value.
|
|
354
|
+
|
|
355
|
+
## Background: why the generation API detour?
|
|
356
|
+
|
|
357
|
+
OpenRouter delivers the serving provider in the stream chunk as a `provider`
|
|
358
|
+
field, but Pi (`pi-ai`) discards it (it only reads `chunk.id` and `chunk.model`).
|
|
359
|
+
Pi likewise discards the actual cost amount (`usage.cost`) and computes costs from
|
|
360
|
+
the price table (`models-store.json`) – which reflects the **model base price**,
|
|
361
|
+
not the price of the routed provider (example `deepseek/deepseek-v4.1-flash` via
|
|
362
|
+
Fireworks: catalogue 0.15/0.60 vs. real 0.22/0.66). No provider header exists
|
|
363
|
+
(`X-Provider-Name` is only listed as "exposed" but is not sent). The generation
|
|
364
|
+
API is therefore the only reliable source for provider and billed amount.
|
|
365
|
+
|
|
366
|
+
## Dependencies
|
|
367
|
+
|
|
368
|
+
`@earendil-works/pi-ai`, `@earendil-works/pi-coding-agent` and
|
|
369
|
+
`@earendil-works/pi-tui` are bundled by Pi and are therefore only declared as
|
|
370
|
+
optional `peerDependencies`. No runtime dependencies on other extensions.
|
|
371
|
+
|
|
372
|
+
## License
|
|
373
|
+
|
|
374
|
+
[MIT](./LICENSE) – see [`LICENSE`](./LICENSE).
|