@chenlongapps/opencode-token-usage 0.4.5 → 0.4.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -34,7 +34,7 @@ Alternatively, add the package to your project's `opencode.json` or `opencode.js
34
34
  ```
35
35
 
36
36
  > [!NOTE]
37
- > Requires Node.js 22+. OpenCode 2.0.9, 2.0.10, and 2.0.11 have been verified across project releases. The current SDK 2.0.11 build has been reverified on OpenCode 2.0.11 and 2.0.22.
37
+ > Requires Node.js 22+ (see the OpenTUI engine note under [Development](#development)). OpenCode 2.0.9 and 2.0.10 have been verified in earlier releases. The current SDK 2.0.24 build has been verified on OpenCode 2.0.11, 2.0.22, and 2.0.24.
38
38
 
39
39
  Restart OpenCode after installation. The panel appears in the native sidebar when `session.sidebar` is set to `auto` and the terminal is wide enough. OpenCode hides the sidebar in subagent views, so the plugin keeps one live summary line above the composer with Context, Total, Cost, and TPS. Click the line to open the full statistics in a centered dialog. Press Escape or click **esc** to close it; closing the dialog does not interrupt the subagent.
40
40
 
@@ -58,9 +58,11 @@ The dialog is split into five sections with separate statistics:
58
58
 
59
59
  One line under the title names the session and its active model, and the body never repeats the model name. Compact mode uses `K`/`M` abbreviations (`812`, `139.4K`, `3.70M`), hides empty rows and merges the two tool families in Context Breakdown into a single `Tools` row. Detailed mode shows exact numbers, empty rows, and the `System Tools` / `MCP Tools` split. The sidebar keeps exact numbers and its own layout.
60
60
 
61
- Costs follow the sidebar's pricing and `partial`/unavailable/free conventions. In detailed mode, By Model shows the applied Input, Output, Reasoning, Cache Read and Cache Write rates in USD per million text tokens. Each rate group names its source (OpenCode or the built-in snapshot), incoming-token tier and call count; a model can have multiple groups. `Incoming` means Input + Cache Read + Cache Write. Missing rates show `—`, confirmed free rates show `$0.00`, and unpriced calls are counted separately. These are the current rates used to estimate recorded calls, not historical provider bills. Context sections are unavailable until the viewed session has reported usage and a known context limit.
61
+ Costs follow the sidebar's pricing and `partial`/unavailable/free conventions. In detailed mode, By Model shows the applied Input, Output, Reasoning, Cache Read and Cache Write rates in USD per million text tokens. Each rate group names its source (OpenCode or the built-in snapshot), incoming-token tier and call count; a model can have multiple groups. `Incoming` means Input + Cache Read + Cache Write. Missing rates show `—`, confirmed free rates show `$0.00`, and unpriced calls are counted separately. These are the current rates used to estimate recorded calls, not historical provider bills.
62
62
 
63
- **Context Breakdown** separates Messages, System Tools, System Prompt, Skills, MCP Tools, and Other. It estimates the text and tool definitions in the viewed session's **latest assembled model request**; its percentages use the sum of those estimates. These are not provider-reported token counts and do not add up to the measured context window, which also includes output and reasoning. Media and provider-specific framing cannot be measured from the request. A server plugin records only aggregate estimates (not prompt text); sessions without a captured request or an available server RPC show “Source estimates unavailable”.
63
+ **Context Window** requires reported usage and a valid active-model context limit. **Last Request** shows the selected assistant call's reported tokens even when that limit is unknown or source estimates are unavailable. Its timestamp comes from that same message: stream end, completion, then creation time, using the first valid value and hiding the timestamp if none is available. Request selection follows the viewed session's revert cutoff and most recent completed compaction boundary.
64
+
65
+ **Context Breakdown** separates Messages, System Tools, System Prompt, Skills, MCP Tools, and Other. It estimates the text and tool definitions in the viewed session's **latest assembled model request**; its percentages use the sum of those estimates. Its timestamp is the estimate's own capture time, which may refer to a different request than Last Request. These are not provider-reported token counts and do not add up to the measured context window, which also includes output and reasoning. Media and provider-specific framing cannot be measured from the request. A server plugin records only aggregate estimates (not prompt text); sessions without a captured request or an available server RPC show “Source estimates unavailable”.
64
66
 
65
67
  ### Metrics
66
68
 
@@ -99,9 +101,11 @@ npm run build
99
101
  npm run test:smoke
100
102
  ```
101
103
 
104
+ The OpenCode SDK packages are pinned to `2.0.24`, with OpenTUI `0.5.14`. Clean installation, typecheck, tests, build, and real-host smoke have passed on Node.js 22. OpenTUI declares Node.js `>=26.4.0` for its native Node runtime, so npm reports `EBADENGINE` on Node 22; `engine-strict` installations require a Node version meeting that dependency's engine requirement. The plugin's TUI runs inside OpenCode, not as a standalone Node 22 renderer. This upgrade keeps the project's Node.js 22+ minimum unchanged.
105
+
102
106
  The [price update workflow](.github/workflows/update-prices.yml) checks models.dev every day at 22:00 UTC (or on demand via **Run workflow** on `main`). No snapshot diff means no release. Changes within the reviewed first-party source allowlist pass typecheck, tests, build, and pack validation, then automatically bump the patch version (for example, `0.4.4` → `0.4.5`), commit only `src/prices.generated.ts`, `package.json`, and `package-lock.json`, and atomically push `main` and its version tag. The workflow creates a GitHub Release and explicitly dispatches [`publish.yml`](.github/workflows/publish.yml) on that tag to publish via npm Trusted Publishing/OIDC; no long-lived npm token is needed. GitHub Actions must be allowed to push to `main` and create tags, and npm must trust `publish.yml` with permission to run `npm publish`.
103
107
 
104
- Failed validation never bumps the version. An unfinished tagged price release is retried with the same version before checking new prices; registry outages are not treated as missing versions. Publishing skips versions already on npm, prevents moving `latest` backwards, and verifies registry visibility after publication. Manufacturer/source allowlist additions, aliases, and manually verified price exceptions still require human review. See the [release guide](docs/releasing.md) for setup and retries.
108
+ Failed validation never bumps the version. An unfinished tagged price release is retried with the same version before checking new prices; registry outages are not treated as missing versions. Publishing skips versions already on npm, prevents moving `latest` backwards, and polls registry visibility for up to 10 minutes after publication, logging progress. A verification timeout is not proof that `npm publish` failed: check the registry and retry the same tag without bumping the version; already-published versions are not republished. Manufacturer/source allowlist additions, aliases, and manually verified price exceptions still require human review. See the [release guide](docs/releasing.md) for setup, verification timing, and retries.
105
109
 
106
110
  For a manual refresh, run `npm run prices:update` in a networked environment, review the generated diff and exceptions, then run the checks above. Builds and plugin refreshes do not contact models.dev.
107
111
 
package/README.zh-CN.md CHANGED
@@ -34,7 +34,7 @@ opencode plugin add @chenlongapps/opencode-token-usage
34
34
  ```
35
35
 
36
36
  > [!NOTE]
37
- > 需要 Node.js 22+。项目历次发布已验证 OpenCode 2.0.9、2.0.10 和 2.0.11;当前 SDK 2.0.11 构建已在 OpenCode 2.0.11 和 2.0.22 上重新验证。
37
+ > 需要 Node.js 22+(见[开发](#开发)中的 OpenTUI 引擎说明)。此前发布已验证 OpenCode 2.0.9 和 2.0.10;当前 SDK 2.0.24 构建已在 OpenCode 2.0.11、2.0.22 和 2.0.24 上验证。
38
38
 
39
39
  安装后重启 OpenCode。终端足够宽且 `session.sidebar` 设为 `auto` 时,面板会显示在原生侧边栏中。OpenCode 在子代理视图中隐藏侧边栏,因此插件会在输入区上方保留一行实时摘要,显示 Context、Total、Cost 和 TPS。点击摘要可在居中弹窗中查看完整统计;按 Escape 或点击 **esc** 即可关闭,关闭弹窗不会中断子代理。
40
40
 
@@ -58,9 +58,11 @@ opencode plugin add @chenlongapps/opencode-token-usage
58
58
 
59
59
  标题下方一行注明会话与当前活动模型,正文不再重复模型名。紧凑模式使用 `K`/`M` 缩写(`812`、`139.4K`、`3.70M`),隐藏零值行,并把 Context Breakdown 中的两类工具合并为一行 `Tools`;详细模式显示精确数字、零值行及 `System Tools` / `MCP Tools` 拆分。侧边栏仍使用精确数字和原有布局。
60
60
 
61
- 费用沿用侧边栏的价格及 `partial`、不可定价、免费口径。详细模式下,By Model 显示 Input、Output、Reasoning、Cache Read、Cache Write 的美元/百万文本 token 费率。每组费率注明来源(OpenCode 或内置快照)、传入 token 档位和调用数;同一模型可能有多组。传入 token 为 Input + Cache Read + Cache Write。缺失费率显示 `—`,确认免费显示 `$0.00`,无法定价的调用单独计数。这些是当前用于估算已记录调用的费率,不是历史提供商账单。当前会话尚未上报用量或上下文上限未知时,上下文相关区域显示不可用。
61
+ 费用沿用侧边栏的价格及 `partial`、不可定价、免费口径。详细模式下,By Model 显示 Input、Output、Reasoning、Cache Read、Cache Write 的美元/百万文本 token 费率。每组费率注明来源(OpenCode 或内置快照)、传入 token 档位和调用数;同一模型可能有多组。传入 token 为 Input + Cache Read + Cache Write。缺失费率显示 `—`,确认免费显示 `$0.00`,无法定价的调用单独计数。这些是当前用于估算已记录调用的费率,不是历史提供商账单。
62
62
 
63
- 弹窗中的 **Context Breakdown** 将消息、系统工具、系统提示词、技能、MCP 工具及其他分开展示。它根据被查看会话**最近一次已组装的模型请求**中的文本和工具定义估算,占比的分母是六项估算值之和;这些不是提供商上报的精确 token,也不会与上方含输出、推理的实测上下文窗口相加一致。媒体内容与提供商额外包装无法准确计量。服务端插件仅保存分类数值,不保存提示词正文;未捕获到请求或服务端 RPC 不可用时显示“来源估算不可用”。
63
+ **Context Window** 需要已上报用量和有效的活动模型上下文上限。**Last Request** 展示所选 assistant 调用的实测 token,上限未知或来源估算不可用时仍可显示。其时间取自同一条消息,依次选用有效的流式结束、完成、创建时间;均不可用时隐藏时间。请求选择遵循被查看会话的回退截断和最后一次已完成压缩边界。
64
+
65
+ 弹窗中的 **Context Breakdown** 将消息、系统工具、系统提示词、技能、MCP 工具及其他分开展示。它根据被查看会话**最近一次已组装的模型请求**中的文本和工具定义估算,占比的分母是六项估算值之和。其时间为该估算自身的采样时间,可能与 Last Request 对应不同请求。这些不是提供商上报的精确 token,也不会与上方含输出、推理的实测上下文窗口相加一致。媒体内容与提供商额外包装无法准确计量。服务端插件仅保存分类数值,不保存提示词正文;未捕获到请求或服务端 RPC 不可用时显示“来源估算不可用”。
64
66
 
65
67
  ### 指标
66
68
 
@@ -99,9 +101,11 @@ npm run build
99
101
  npm run test:smoke
100
102
  ```
101
103
 
104
+ OpenCode SDK 包精确锁定为 `2.0.24`,OpenTUI 为 `0.5.14`。Node.js 22 下已通过干净安装、类型检查、测试、构建和真实宿主 smoke。OpenTUI 为其原生 Node 运行时声明 Node.js `>=26.4.0`,因此 Node 22 安装时 npm 会输出 `EBADENGINE`;启用 `engine-strict` 时须使用满足该依赖引擎要求的 Node 版本。插件的 TUI 在 OpenCode 内运行,并非独立的 Node 22 渲染器。本次升级保持项目 Node.js 22+ 的最低要求不变。
105
+
102
106
  [价格更新工作流](.github/workflows/update-prices.yml) 每天 UTC 22:00 检查 models.dev,也可在 `main` 上通过 **Run workflow** 手动触发。快照无变化就不发布。经审核的原厂来源白名单内有变化时,先通过类型检查、测试、构建和打包检查,再自动升级 patch 版本(例如 `0.4.4` → `0.4.5`),只提交 `src/prices.generated.ts`、`package.json` 和 `package-lock.json`,并原子推送 `main` 与版本标签。工作流创建 GitHub Release,显式触发该标签上的 [`publish.yml`](.github/workflows/publish.yml),通过 npm Trusted Publishing/OIDC 发布,无需长期 npm token。仓库必须允许 GitHub Actions 推送 `main` 和创建标签;npm 必须信任 `publish.yml` 并允许执行 `npm publish`。
103
107
 
104
- 验证失败不会升版本。已打标签但未完成的自动价格发布会优先重试同一版本,再检查新价格;registry 故障不会被当作版本不存在。发布时跳过 npm 已有版本、防止 `latest` 倒退,并在发布后核验 registry 中的版本。新增原厂/来源白名单、别名及人工核实的价格例外仍需人工审核。配置和重试方法见[发布说明](docs/releasing.md)。
108
+ 验证失败不会升版本。已打标签但未完成的自动价格发布会优先重试同一版本,再检查新价格;registry 故障不会被当作版本不存在。发布时跳过 npm 已有版本、防止 `latest` 倒退,并在发布后最多轮询 10 分钟核验 registry 可见性,输出进度。校验超时不代表 `npm publish` 失败:先检查 registry,再重试原标签,不另升版本;已发布版本不会重复发布。新增原厂/来源白名单、别名及人工核实的价格例外仍需人工审核。配置、校验时限和重试方法见[发布说明](docs/releasing.md)。
105
109
 
106
110
  手动更新时,在联网的维护环境运行 `npm run prices:update`,审阅生成文件及例外差异,再执行上述检查。构建和插件刷新不会请求 models.dev。
107
111
 
@@ -3,20 +3,21 @@ import type { Plugin } from "@opencode/plugin";
3
3
  import type { ContextSources } from "./context-sources.js";
4
4
  type Request = Pick<SessionContext, "sessionID" | "model" | "system" | "messages" | "tools">;
5
5
  type Mcp = {
6
- list(): Promise<{
6
+ list(...args: Parameters<Plugin.Context["mcp"]["list"]>): Promise<{
7
7
  data: readonly {
8
8
  name: string;
9
9
  }[];
10
10
  }>;
11
11
  };
12
12
  type Storage = Pick<Plugin.Context["storage"], "get" | "set" | "remove">;
13
- /** The hook awaits MCP metadata only on a cache miss; persistence follows the request. */
13
+ /** MCP metadata waits are bounded; persistence never delays the model request. */
14
14
  export declare class ContextCapture {
15
15
  private readonly mcp;
16
16
  private readonly storage;
17
17
  private readonly ttl;
18
18
  private readonly now;
19
19
  private readonly elapsed;
20
+ private readonly mcpTimeoutMs;
20
21
  private names;
21
22
  private expiresAt;
22
23
  private listing;
@@ -31,8 +32,9 @@ export declare class ContextCapture {
31
32
  storageWrites: number;
32
33
  storageWriteMs: number;
33
34
  };
34
- constructor(mcp: Mcp, storage: Storage, ttl?: number, now?: () => number, elapsed?: () => number);
35
+ constructor(mcp: Mcp, storage: Storage, ttl?: number, now?: () => number, elapsed?: () => number, mcpTimeoutMs?: number);
35
36
  private servers;
37
+ private readServers;
36
38
  invalidateMcp(): void;
37
39
  capture(request: Request): Promise<void>;
38
40
  private record;
@@ -1,11 +1,12 @@
1
1
  import { estimateContextSources, parseContextSources } from "./context-sources.js";
2
- /** The hook awaits MCP metadata only on a cache miss; persistence follows the request. */
2
+ /** MCP metadata waits are bounded; persistence never delays the model request. */
3
3
  export class ContextCapture {
4
4
  mcp;
5
5
  storage;
6
6
  ttl;
7
7
  now;
8
8
  elapsed;
9
+ mcpTimeoutMs;
9
10
  names;
10
11
  expiresAt = 0;
11
12
  listing;
@@ -14,32 +15,48 @@ export class ContextCapture {
14
15
  latestEstimate = new Map();
15
16
  deleted = new Set();
16
17
  timings = { requests: 0, mcpReads: 0, mcpWaitMs: 0, storageWrites: 0, storageWriteMs: 0 };
17
- constructor(mcp, storage, ttl = 30_000, now = Date.now, elapsed = () => performance.now()) {
18
+ constructor(mcp, storage, ttl = 30_000, now = Date.now, elapsed = () => performance.now(), mcpTimeoutMs = 100) {
18
19
  this.mcp = mcp;
19
20
  this.storage = storage;
20
21
  this.ttl = ttl;
21
22
  this.now = now;
22
23
  this.elapsed = elapsed;
24
+ this.mcpTimeoutMs = mcpTimeoutMs;
23
25
  }
24
26
  async servers() {
25
27
  if (this.names && this.now() < this.expiresAt)
26
28
  return this.names;
27
29
  if (!this.listing) {
28
30
  this.timings.mcpReads++;
29
- this.listing = this.mcp.list().then(result => {
30
- this.names = result.data.map(server => server.name);
31
- this.expiresAt = this.now() + this.ttl;
32
- return this.names;
33
- }).catch(error => {
34
- if (this.names) {
35
- this.expiresAt = this.now() + this.ttl;
36
- return this.names;
37
- }
38
- throw error;
39
- }).finally(() => { this.listing = undefined; });
31
+ this.listing = this.readServers().finally(() => { this.listing = undefined; });
40
32
  }
41
33
  return this.listing;
42
34
  }
35
+ async readServers() {
36
+ const controller = new AbortController();
37
+ let timer;
38
+ try {
39
+ const timeout = new Promise((_, reject) => {
40
+ timer = setTimeout(() => {
41
+ const error = new DOMException("MCP metadata read timed out", "TimeoutError");
42
+ reject(error);
43
+ controller.abort(error);
44
+ }, this.mcpTimeoutMs);
45
+ });
46
+ const result = await Promise.race([this.mcp.list(undefined, { signal: controller.signal }), timeout]);
47
+ // Only the bounded read may update the cache, even if cancellation is ignored.
48
+ this.names = result.data.map(server => server.name);
49
+ this.expiresAt = this.now() + this.ttl;
50
+ return this.names;
51
+ }
52
+ catch {
53
+ // Reuse stale metadata without renewing it, so the next request can retry.
54
+ return this.names;
55
+ }
56
+ finally {
57
+ clearTimeout(timer);
58
+ }
59
+ }
43
60
  invalidateMcp() { this.expiresAt = 0; }
44
61
  capture(request) {
45
62
  const tasks = this.inflight.get(request.sessionID) ?? new Set();
@@ -64,7 +81,7 @@ export class ContextCapture {
64
81
  this.timings.requests++;
65
82
  this.timings.mcpWaitMs += this.elapsed() - start;
66
83
  }
67
- if (this.deleted.has(request.sessionID))
84
+ if (!names || this.deleted.has(request.sessionID))
68
85
  return;
69
86
  const value = estimateContextSources(request, names);
70
87
  this.latestEstimate.set(request.sessionID, value);
@@ -57,7 +57,10 @@ export function estimateContextSources(request, mcpServers = [], capturedAt = Da
57
57
  }
58
58
  if (part.type === "media") {
59
59
  // Media payload sizes do not translate to provider token counts.
60
- tokens.Other += estimate(part.filename ?? part.mediaType);
60
+ // Older hosts expose mediaType directly; SDK 2.0.24 wraps it in an asset.
61
+ const mediaType = part.media?.mediaType
62
+ ?? ("mediaType" in part && typeof part.mediaType === "string" ? part.mediaType : "");
63
+ tokens.Other += estimate(part.filename ?? mediaType);
61
64
  continue;
62
65
  }
63
66
  if (part.type === "tool-call") {
@@ -2,13 +2,13 @@ import type { UsageSource } from "./source.js";
2
2
  import { PerformanceMonitor } from "./performance.js";
3
3
  import type { PerformanceSummary } from "./performance.js";
4
4
  import type { ContextSources } from "./context-sources.js";
5
- import type { ContextDetails, ContextUsage, ModelCost, Summary } from "./usage.js";
5
+ import type { ContextUsage, ModelCost, RequestDetails, Summary } from "./usage.js";
6
6
  export interface UsageState {
7
7
  status: "loading" | "ready" | "stale" | "unavailable";
8
8
  summary?: Summary;
9
9
  context?: ContextUsage | undefined;
10
10
  details?: {
11
- context?: ContextDetails | undefined;
11
+ request?: RequestDetails | undefined;
12
12
  models: readonly ModelCost[];
13
13
  sources?: ContextSources | undefined;
14
14
  sessionTitle?: string | undefined;
@@ -1,6 +1,6 @@
1
1
  import { loadSnapshot, uniqueMessages, viewedMessages } from "./source.js";
2
2
  import { PerformanceMonitor, preparePerformance } from "./performance.js";
3
- import { contextDetails, summarize, summarizeModels } from "./usage.js";
3
+ import { contextUsage, requestDetails, summarize, summarizeModels } from "./usage.js";
4
4
  const sessionEvents = new Set([
5
5
  "session.created", "session.deleted", "session.forked", "session.moved",
6
6
  "session.model.selected", "session.agent.selected", "session.step.ended", "session.step.failed",
@@ -164,18 +164,20 @@ export class UsageController {
164
164
  this.prepared = preparePerformance(messages);
165
165
  clearTimeout(this.performanceTimer);
166
166
  this.performanceTimer = undefined;
167
- const context = contextDetails(viewedMessages(snapshot), snapshot.model.context, snapshot.sessions.get(snapshot.viewedID)?.revert?.messageID);
167
+ const history = viewedMessages(snapshot);
168
+ const session = snapshot.sessions.get(snapshot.viewedID);
169
+ const revertMessageID = session.revert?.messageID;
168
170
  this.update({
169
171
  status: "ready",
170
172
  summary: summarize(messages, snapshot.model.catalog),
171
- context: context?.usage,
172
- ...(this.detailed ? { details: { context, models: summarizeModels(messages, snapshot.model.catalog), sessionTitle: snapshot.sessions.get(snapshot.viewedID)?.title } } : {}),
173
+ context: contextUsage(history, snapshot.model.context, revertMessageID),
174
+ ...(this.detailed ? { details: { request: requestDetails(history, revertMessageID), models: summarizeModels(messages, snapshot.model.catalog), sessionTitle: session.title } } : {}),
173
175
  performance: this.performance.summaryPrepared(this.prepared, this.sessions),
174
176
  model: snapshot.model.label,
175
177
  });
176
178
  // The server plugin may be absent. An optional RPC must not delay measured usage.
177
179
  if (this.detailed && this.source.composition) {
178
- void this.source.composition(snapshot.sessions.get(snapshot.viewedID), AbortSignal.any([request.signal, AbortSignal.timeout(5_000)]))
180
+ void this.source.composition(session, AbortSignal.any([request.signal, AbortSignal.timeout(5_000)]))
179
181
  .then(sources => {
180
182
  if (this.disposed || generation !== this.generation || this.snapshot !== snapshot || this.state.status !== "ready" || !this.state.details)
181
183
  return;
@@ -262,6 +262,10 @@ export class PerformanceMonitor {
262
262
  const cutoff = event.created - LIVE_WINDOW_MS;
263
263
  while (step.samples.length > 0 && step.samples[0].time < cutoff)
264
264
  step.samples.shift();
265
+ // Advance smoothing once per accepted delta, independently of summary readers.
266
+ const raw = estimatedLiveTps(step.samples);
267
+ if (raw !== undefined)
268
+ step.liveTps = smoothLiveTps(step.liveTps, raw);
265
269
  step.last = Math.max(step.last ?? event.created, event.created);
266
270
  if (step.first === undefined) {
267
271
  step.first = event.created;
@@ -309,15 +313,13 @@ export class PerformanceMonitor {
309
313
  for (const step of this.active.values()) {
310
314
  if (!sessions.has(step.sessionID) || step.reconciledAt !== undefined)
311
315
  continue;
312
- const raw = estimatedLiveTps(step.samples);
313
- if (raw === undefined)
316
+ if (step.liveTps === undefined || step.samples.length < 2)
314
317
  continue;
315
318
  const first = step.samples[0];
316
319
  const last = step.samples[step.samples.length - 1];
317
320
  const duration = last.time - first.time;
318
321
  if (!finite(duration) || duration <= 0)
319
322
  continue;
320
- step.liveTps = smoothLiveTps(step.liveTps, raw);
321
323
  weighted += step.liveTps * duration;
322
324
  durationTotal += duration;
323
325
  }
@@ -1,5 +1,5 @@
1
1
  export declare const GENERATED_PRICE_SNAPSHOT: {
2
- readonly verified: "2026-10-07";
2
+ readonly verified: "2026-10-08";
3
3
  readonly source: "https://models.dev/api.json";
4
4
  readonly entries: readonly [{
5
5
  readonly providerID: "ai21";
@@ -92,8 +92,8 @@ export declare const GENERATED_PRICE_SNAPSHOT: {
92
92
  readonly providerID: "alibaba";
93
93
  readonly id: "qwen-vl-ocr";
94
94
  readonly prices: readonly [{
95
- readonly input: 0.72;
96
- readonly output: 0.72;
95
+ readonly input: 0.07;
96
+ readonly output: 0.16;
97
97
  }];
98
98
  readonly released: "2024-10-28";
99
99
  }, {
@@ -550,6 +550,29 @@ export declare const GENERATED_PRICE_SNAPSHOT: {
550
550
  };
551
551
  }];
552
552
  readonly released: "2025-10-15";
553
+ }, {
554
+ readonly providerID: "anthropic";
555
+ readonly id: "claude-haiku-5-5";
556
+ readonly prices: readonly [{
557
+ readonly input: 0.1;
558
+ readonly output: 0.5;
559
+ readonly cache: {
560
+ readonly read: 0.01;
561
+ readonly write: 0.125;
562
+ };
563
+ }, {
564
+ readonly input: 0.5;
565
+ readonly output: 2.5;
566
+ readonly cache: {
567
+ readonly read: 0.05;
568
+ readonly write: 0.625;
569
+ };
570
+ readonly tier: {
571
+ readonly type: "context";
572
+ readonly size: 100000;
573
+ };
574
+ }];
575
+ readonly released: "2026-10-07";
553
576
  }, {
554
577
  readonly providerID: "anthropic";
555
578
  readonly id: "claude-opus-4-5";
@@ -728,7 +751,7 @@ export declare const GENERATED_PRICE_SNAPSHOT: {
728
751
  readonly input: 2;
729
752
  readonly output: 10;
730
753
  readonly cache: {
731
- readonly read: 0.2;
754
+ readonly read: 0.1;
732
755
  readonly write: 2.5;
733
756
  };
734
757
  }];
@@ -1266,7 +1289,7 @@ export declare const GENERATED_PRICE_SNAPSHOT: {
1266
1289
  readonly input: 0.6;
1267
1290
  readonly output: 2.4;
1268
1291
  readonly cache: {
1269
- readonly read: 0.06;
1292
+ readonly read: 0.03;
1270
1293
  readonly write: 0.375;
1271
1294
  };
1272
1295
  }];
@@ -1316,6 +1339,17 @@ export declare const GENERATED_PRICE_SNAPSHOT: {
1316
1339
  };
1317
1340
  }];
1318
1341
  readonly released: "2026-06-01";
1342
+ }, {
1343
+ readonly providerID: "mistral";
1344
+ readonly id: "codestral-2508";
1345
+ readonly prices: readonly [{
1346
+ readonly input: 0.3;
1347
+ readonly output: 0.9;
1348
+ readonly cache: {
1349
+ readonly read: 0.03;
1350
+ };
1351
+ }];
1352
+ readonly released: "2025-07-30";
1319
1353
  }, {
1320
1354
  readonly providerID: "mistral";
1321
1355
  readonly id: "codestral-latest";
@@ -1391,6 +1425,28 @@ export declare const GENERATED_PRICE_SNAPSHOT: {
1391
1425
  readonly output: 5;
1392
1426
  }];
1393
1427
  readonly released: "2025-03-17";
1428
+ }, {
1429
+ readonly providerID: "mistral";
1430
+ readonly id: "ministral-14b-2512";
1431
+ readonly prices: readonly [{
1432
+ readonly input: 0.2;
1433
+ readonly output: 0.2;
1434
+ readonly cache: {
1435
+ readonly read: 0.02;
1436
+ };
1437
+ }];
1438
+ readonly released: "2025-12-02";
1439
+ }, {
1440
+ readonly providerID: "mistral";
1441
+ readonly id: "ministral-3b-2512";
1442
+ readonly prices: readonly [{
1443
+ readonly input: 0.1;
1444
+ readonly output: 0.1;
1445
+ readonly cache: {
1446
+ readonly read: 0.01;
1447
+ };
1448
+ }];
1449
+ readonly released: "2025-12-02";
1394
1450
  }, {
1395
1451
  readonly providerID: "mistral";
1396
1452
  readonly id: "ministral-3b-latest";
@@ -1399,6 +1455,17 @@ export declare const GENERATED_PRICE_SNAPSHOT: {
1399
1455
  readonly output: 0.04;
1400
1456
  }];
1401
1457
  readonly released: "2024-10-01";
1458
+ }, {
1459
+ readonly providerID: "mistral";
1460
+ readonly id: "ministral-8b-2512";
1461
+ readonly prices: readonly [{
1462
+ readonly input: 0.15;
1463
+ readonly output: 0.15;
1464
+ readonly cache: {
1465
+ readonly read: 0.015;
1466
+ };
1467
+ }];
1468
+ readonly released: "2025-12-02";
1402
1469
  }, {
1403
1470
  readonly providerID: "mistral";
1404
1471
  readonly id: "ministral-8b-latest";
@@ -1572,6 +1639,14 @@ export declare const GENERATED_PRICE_SNAPSHOT: {
1572
1639
  readonly output: 6;
1573
1640
  }];
1574
1641
  readonly released: "2024-11-01";
1642
+ }, {
1643
+ readonly providerID: "mistral";
1644
+ readonly id: "voxtral-small-2507";
1645
+ readonly prices: readonly [{
1646
+ readonly input: 0.1;
1647
+ readonly output: 0.4;
1648
+ }];
1649
+ readonly released: "2025-07-15";
1575
1650
  }, {
1576
1651
  readonly providerID: "mistral";
1577
1652
  readonly id: "voxtral-small-latest";
@@ -1621,6 +1696,7 @@ export declare const GENERATED_PRICE_SNAPSHOT: {
1621
1696
  readonly output: 15;
1622
1697
  readonly cache: {
1623
1698
  readonly read: 0.3;
1699
+ readonly write: 3;
1624
1700
  };
1625
1701
  }];
1626
1702
  readonly released: "2026-07-16";
@@ -1,5 +1,5 @@
1
1
  export const GENERATED_PRICE_SNAPSHOT = {
2
- verified: "2026-10-07",
2
+ verified: "2026-10-08",
3
3
  source: "https://models.dev/api.json",
4
4
  entries: [
5
5
  { "providerID": "ai21", "id": "jamba-large", "prices": [{ "input": 2, "output": 8 }], "released": "2025-07-01" },
@@ -13,7 +13,7 @@ export const GENERATED_PRICE_SNAPSHOT = {
13
13
  { "providerID": "alibaba", "id": "qwen-plus-character-ja", "prices": [{ "input": 0.5, "output": 1.4 }] },
14
14
  { "providerID": "alibaba", "id": "qwen-turbo", "prices": [{ "input": 0.05, "output": 0.2, "reasoning": 0.5 }], "released": "2024-11-01" },
15
15
  { "providerID": "alibaba", "id": "qwen-vl-max", "prices": [{ "input": 0.8, "output": 3.2 }], "released": "2024-04-08" },
16
- { "providerID": "alibaba", "id": "qwen-vl-ocr", "prices": [{ "input": 0.72, "output": 0.72 }], "released": "2024-10-28" },
16
+ { "providerID": "alibaba", "id": "qwen-vl-ocr", "prices": [{ "input": 0.07, "output": 0.16 }], "released": "2024-10-28" },
17
17
  { "providerID": "alibaba", "id": "qwen-vl-plus", "prices": [{ "input": 0.21, "output": 0.63 }], "released": "2024-01-25" },
18
18
  { "providerID": "alibaba", "id": "qwen2-5-14b-instruct", "prices": [{ "input": 0.35, "output": 1.4 }] },
19
19
  { "providerID": "alibaba", "id": "qwen2-5-32b-instruct", "prices": [{ "input": 0.7, "output": 2.8 }] },
@@ -58,6 +58,7 @@ export const GENERATED_PRICE_SNAPSHOT = {
58
58
  { "providerID": "anthropic", "id": "claude-fable-5-1", "prices": [{ "input": 10, "output": 50, "cache": { "read": 0.25, "write": 12.5 } }], "released": "2026-09-01" },
59
59
  { "providerID": "anthropic", "id": "claude-haiku-4-5", "prices": [{ "input": 1, "output": 5, "cache": { "read": 0.1, "write": 1.25 } }], "released": "2025-10-15" },
60
60
  { "providerID": "anthropic", "id": "claude-haiku-4-5-20251001", "prices": [{ "input": 1, "output": 5, "cache": { "read": 0.1, "write": 1.25 } }], "released": "2025-10-15" },
61
+ { "providerID": "anthropic", "id": "claude-haiku-5-5", "prices": [{ "input": 0.1, "output": 0.5, "cache": { "read": 0.01, "write": 0.125 } }, { "input": 0.5, "output": 2.5, "cache": { "read": 0.05, "write": 0.625 }, "tier": { "type": "context", "size": 100000 } }], "released": "2026-10-07" },
61
62
  { "providerID": "anthropic", "id": "claude-opus-4-5", "prices": [{ "input": 5, "output": 25, "cache": { "read": 0.5, "write": 6.25 } }], "released": "2025-11-24" },
62
63
  { "providerID": "anthropic", "id": "claude-opus-4-5-20251101", "prices": [{ "input": 5, "output": 25, "cache": { "read": 0.5, "write": 6.25 } }], "released": "2025-11-24" },
63
64
  { "providerID": "anthropic", "id": "claude-opus-4-6", "prices": [{ "input": 5, "output": 25, "cache": { "read": 0.5, "write": 6.25 } }], "released": "2026-02-04" },
@@ -72,7 +73,7 @@ export const GENERATED_PRICE_SNAPSHOT = {
72
73
  { "providerID": "anthropic", "id": "claude-sonnet-4-5-20250929", "prices": [{ "input": 3, "output": 15, "cache": { "read": 0.3, "write": 3.75 } }], "released": "2025-09-29" },
73
74
  { "providerID": "anthropic", "id": "claude-sonnet-4-6", "prices": [{ "input": 3, "output": 15, "cache": { "read": 0.3, "write": 3.75 } }], "released": "2026-02-17" },
74
75
  { "providerID": "anthropic", "id": "claude-sonnet-5", "prices": [{ "input": 2, "output": 10, "cache": { "read": 0.2, "write": 2.5 } }], "released": "2026-06-29" },
75
- { "providerID": "anthropic", "id": "claude-sonnet-5-5", "prices": [{ "input": 2, "output": 10, "cache": { "read": 0.2, "write": 2.5 } }], "released": "2026-09-28" },
76
+ { "providerID": "anthropic", "id": "claude-sonnet-5-5", "prices": [{ "input": 2, "output": 10, "cache": { "read": 0.1, "write": 2.5 } }], "released": "2026-09-28" },
76
77
  { "providerID": "arcee", "id": "trinity-large-thinking", "prices": [{ "input": 0.25, "output": 0.8, "cache": { "read": 0.06 } }], "released": "2026-04-01" },
77
78
  { "providerID": "cohere", "id": "command-a-03-2025", "prices": [{ "input": 2.5, "output": 10 }], "released": "2025-03-13" },
78
79
  { "providerID": "cohere", "id": "command-a-plus-05-2026", "prices": [{ "input": 2.5, "output": 10 }], "released": "2026-05-20" },
@@ -119,10 +120,11 @@ export const GENERATED_PRICE_SNAPSHOT = {
119
120
  { "providerID": "minimax", "id": "minimax-m2", "prices": [{ "input": 0.3, "output": 1.2 }], "released": "2025-10-27" },
120
121
  { "providerID": "minimax", "id": "minimax-m2.1", "prices": [{ "input": 0.3, "output": 1.2, "cache": { "read": 0.03, "write": 0.375 } }], "released": "2025-12-23" },
121
122
  { "providerID": "minimax", "id": "minimax-m2.5", "prices": [{ "input": 0.3, "output": 1.2, "cache": { "read": 0.03, "write": 0.375 } }], "released": "2026-02-12" },
122
- { "providerID": "minimax", "id": "minimax-m2.5-highspeed", "prices": [{ "input": 0.6, "output": 2.4, "cache": { "read": 0.06, "write": 0.375 } }], "released": "2026-02-13" },
123
+ { "providerID": "minimax", "id": "minimax-m2.5-highspeed", "prices": [{ "input": 0.6, "output": 2.4, "cache": { "read": 0.03, "write": 0.375 } }], "released": "2026-02-13" },
123
124
  { "providerID": "minimax", "id": "minimax-m2.7", "prices": [{ "input": 0.3, "output": 1.2, "cache": { "read": 0.06, "write": 0.375 } }], "released": "2026-03-18" },
124
125
  { "providerID": "minimax", "id": "minimax-m2.7-highspeed", "prices": [{ "input": 0.6, "output": 2.4, "cache": { "read": 0.06, "write": 0.375 } }], "released": "2026-03-18" },
125
126
  { "providerID": "minimax", "id": "minimax-m3", "prices": [{ "input": 0.3, "output": 1.2, "cache": { "read": 0.06 } }, { "input": 0.6, "output": 2.4, "cache": { "read": 0.12 }, "tier": { "type": "context", "size": 512000 } }], "released": "2026-06-01" },
127
+ { "providerID": "mistral", "id": "codestral-2508", "prices": [{ "input": 0.3, "output": 0.9, "cache": { "read": 0.03 } }], "released": "2025-07-30" },
126
128
  { "providerID": "mistral", "id": "codestral-latest", "prices": [{ "input": 0.3, "output": 0.9, "cache": { "read": 0.03 } }], "released": "2024-05-29" },
127
129
  { "providerID": "mistral", "id": "devstral-2512", "prices": [{ "input": 0.4, "output": 2 }], "released": "2025-12-09" },
128
130
  { "providerID": "mistral", "id": "devstral-latest", "prices": [{ "input": 0.4, "output": 2 }], "released": "2025-12-09" },
@@ -132,7 +134,10 @@ export const GENERATED_PRICE_SNAPSHOT = {
132
134
  { "providerID": "mistral", "id": "devstral-small-2507", "prices": [{ "input": 0.1, "output": 0.3 }], "released": "2025-07-10" },
133
135
  { "providerID": "mistral", "id": "labs-devstral-small-2512", "prices": [{ "input": 0, "output": 0 }], "released": "2025-12-09" },
134
136
  { "providerID": "mistral", "id": "magistral-medium-latest", "prices": [{ "input": 2, "output": 5 }], "released": "2025-03-17" },
137
+ { "providerID": "mistral", "id": "ministral-14b-2512", "prices": [{ "input": 0.2, "output": 0.2, "cache": { "read": 0.02 } }], "released": "2025-12-02" },
138
+ { "providerID": "mistral", "id": "ministral-3b-2512", "prices": [{ "input": 0.1, "output": 0.1, "cache": { "read": 0.01 } }], "released": "2025-12-02" },
135
139
  { "providerID": "mistral", "id": "ministral-3b-latest", "prices": [{ "input": 0.04, "output": 0.04 }], "released": "2024-10-01" },
140
+ { "providerID": "mistral", "id": "ministral-8b-2512", "prices": [{ "input": 0.15, "output": 0.15, "cache": { "read": 0.015 } }], "released": "2025-12-02" },
136
141
  { "providerID": "mistral", "id": "ministral-8b-latest", "prices": [{ "input": 0.1, "output": 0.1 }], "released": "2024-10-01" },
137
142
  { "providerID": "mistral", "id": "mistral-large-2411", "prices": [{ "input": 2, "output": 6 }], "released": "2024-11-18" },
138
143
  { "providerID": "mistral", "id": "mistral-large-2512", "prices": [{ "input": 0.5, "output": 1.5, "cache": { "read": 0.05 } }], "released": "2024-11-01" },
@@ -152,11 +157,12 @@ export const GENERATED_PRICE_SNAPSHOT = {
152
157
  { "providerID": "mistral", "id": "open-mixtral-8x7b", "prices": [{ "input": 0.7, "output": 0.7 }], "released": "2023-12-11" },
153
158
  { "providerID": "mistral", "id": "pixtral-12b", "prices": [{ "input": 0.15, "output": 0.15 }], "released": "2024-09-01" },
154
159
  { "providerID": "mistral", "id": "pixtral-large-latest", "prices": [{ "input": 2, "output": 6 }], "released": "2024-11-01" },
160
+ { "providerID": "mistral", "id": "voxtral-small-2507", "prices": [{ "input": 0.1, "output": 0.4 }], "released": "2025-07-15" },
155
161
  { "providerID": "mistral", "id": "voxtral-small-latest", "prices": [{ "input": 0.1, "output": 0.3 }], "released": "2025-07-15" },
156
162
  { "providerID": "moonshotai", "id": "kimi-k2.6", "prices": [{ "input": 0.95, "output": 4, "cache": { "read": 0.16 } }], "released": "2026-04-21" },
157
163
  { "providerID": "moonshotai", "id": "kimi-k2.7-code", "prices": [{ "input": 0.95, "output": 4, "cache": { "read": 0.19 } }], "released": "2026-06-12" },
158
164
  { "providerID": "moonshotai", "id": "kimi-k2.7-code-highspeed", "prices": [{ "input": 1.9, "output": 8, "cache": { "read": 0.38 } }], "released": "2026-06-12" },
159
- { "providerID": "moonshotai", "id": "kimi-k3", "prices": [{ "input": 3, "output": 15, "cache": { "read": 0.3 } }], "released": "2026-07-16" },
165
+ { "providerID": "moonshotai", "id": "kimi-k3", "prices": [{ "input": 3, "output": 15, "cache": { "read": 0.3, "write": 3 } }], "released": "2026-07-16" },
160
166
  { "providerID": "nvidia", "id": "nvidia/llama-3.1-nemotron-70b-instruct", "prices": [{ "input": 0, "output": 0 }], "released": "2025-04-15" },
161
167
  { "providerID": "nvidia", "id": "nvidia/llama-3.1-nemotron-nano-8b-v1", "prices": [{ "input": 0, "output": 0 }], "released": "2025-03-18" },
162
168
  { "providerID": "nvidia", "id": "nvidia/llama-3.1-nemotron-nano-vl-8b-v1", "prices": [{ "input": 0, "output": 0 }], "released": "2025-04-10" },
package/dist/pricing.d.ts CHANGED
@@ -14,7 +14,7 @@ export interface OfficialPriceEntry {
14
14
  /** Checked-in data: updating prices is explicit, not a network request at
15
15
  * startup, during a refresh, or during an npm build. */
16
16
  export declare const OFFICIAL_PRICE_SNAPSHOT: {
17
- verified: "2026-10-07";
17
+ verified: "2026-10-08";
18
18
  source: "https://models.dev/api.json";
19
19
  entries: OfficialPriceEntry[];
20
20
  };