/** * 用量统计查询面(registry AnalyticsSource id="service-usage" 槽位的数据源)。 * * 数据源 = task_run 账本的 result JSON(E8 `stats.modelUsage` per-model echo:inputTokens/outputTokens/ * cacheReadTokens/cacheWriteTokens/costMicroUsd——costMicroUsd 是 authoritative micro-USD 记账非估算)。 * 架构 = store 侧薄扫窗(usageScan:SQL 只按时间窗过滤取四列,行内 JSON 提取在 store 完成)+ 本文件的 * 【纯函数】聚合(三后端同代码,零方言分叉;test 直接喂行)。 * * 口径(回帖契约,2026-07-09): * - tokensIn = Σ(inputTokens + cacheReadTokens + cacheWriteTokens)(即"进模型的上下文 token 总量", * cache 命中也计——计费口径;cache 明细在 breakdown 的 cacheReadTokens 单列可见); * - tokensOut = Σ outputTokens; * - costUsd = Σ costMicroUsd / 1e6(authoritative); * - 旧行/无 modelUsage echo 的行(1.x 早期或非终态)fallback `stats.tokens` 整数计入 tokensOut 并置 * `estimated:true` 于该聚合(诚实标注,契约要求"token 数标注估算与否");#59 起 `estimated` 还覆盖 * 第二种口径不完整:**有 echo 行但缺数值键**(该键按 0 计入 ⇒ 总量偏低)——两臂同义:别把总数当精确值; * - 行内数值键一律走 {@link numOf} 守卫:数据源是 DB 里的 JSON,裸算术遇缺席/非数会 NaN 毒掉整窗(#59); * - 扫窗有行数上限(store 侧 LIMIT)——触顶时结果携带 `truncated:true`(诚实截断,绝不静默)。 */ /** store 侧 usageScan 的行投影(所有后端同形状;createdAtMs=epoch ms)。 */ export interface UsageRow { owner: string | null; createdAtMs: number; status: string; /** result.stats 摘要;result NULL/不可解析 ⇒ undefined(不计 token/cost,仍计 tasks)。 */ stats?: { tokens?: number; modelUsage?: Record; /** core 7.14.0 [ref] C-b(`TaskResult.stats.usageMissing`):这一行的用量数字是**下界**不是测量值 * —— 至少有一轮模型没报用量,或整条结果是宿主/编排车道自铸的(一个模型轮都没跑过)。 * `true` 或缺席,**永不 `false`**;缺席 ⇔ 每轮都报了用量。fold 侧翻成既有的 * {@link UsageTotals.estimated}(见 `foldRow`),**不开第二条不精确轴**。 */ usageMissing?: true; }; } /** * 一行 per-model echo。**五键逐个可选**——本类型描述的不是本进程铸的对象,而是 **DB 里 result JSON 的一段**: * 旧版本写的行、别的 producer 写的行、以及 wire 契约本身(SDK `ModelUsageDelta` 五键全可选 + `[k]:unknown` * 开放)都可能少键。#59([ref]①):此处原先声明成「全必填」,`projectUsageStats` 又对任意 JSON 做裸 cast, * 于是 fold 侧的裸算术遇缺席就产出 NaN,**一行毒一整窗**(NaN 沿 Σ 传染,JSON.stringify(NaN)==="null", * 消费端读到的是「没有数」而不是「少了一行」)。声明改诚实 + fold 侧 {@link numOf} 数值守卫。 */ export interface UsageModelEntry { inputTokens?: number; outputTokens?: number; cacheReadTokens?: number; cacheWriteTokens?: number; /** S10(core 3.0 计量语义统一,§DESIGN-V2 V2-⑤)口径标记:`"uncached-components-v1"` = 三个输入键是 * 分量制(互不重叠,本文件的 tokensIn 求和公式正确)。缺席/别的值 = 口径不可分辨(存量行可能把 * cache 计入 inputTokens,求和会双算)⇒ fold 侧标 {@link UsageTotals.estimated}。 */ usageBasis?: string; /** 整数 micro-USD。**缺席 = 未知**,不是 0(SDK `ModelUsageDelta` 同款单轨契约)。注意在场的 `0` 仍有 * 歧义(引擎无价目表时恒发 0),那层消歧在部署面 `capabilities.pricingConfigured`——裁定见 budget.ts。 */ costMicroUsd?: number; } export interface UsageTotals { tasks: number; tokensIn: number; tokensOut: number; costUsd: number; /** 本聚合的口径**不完整**——三种来源:①有行走了 stats.tokens fallback(无 per-model echo,token 数为估算); * ②有 per-model echo 行**缺数值键**(该键按 0 计入,总量偏低;成本键缺席时尤其:少算 ≠ 免费); * ③(S10/V2-⑤)有行的 {@link UsageModelEntry.usageBasis} 缺席或非本读端认识的分量制——存量行某族 * 历史上把 cache 计入 inputTokens,tokensIn 求和会**双算**,口径不可分辨。 * ④(7.72.0 / core 7.14.0 [ref] C-b)有行自陈 `stats.usageMissing` —— 那一行的数字是**下界**:至少一轮 * 模型没报用量,或整条结果是宿主自铸的零结果(取消打断 / 零执行的治理拒绝)。`tokens: 0` 在那种行上读作 * 「未知」而不是「免费」。 * 四者都只表达「别把这个总数当精确值」,消费端一视同仁。 */ estimated: boolean; } /** GET /v1/usage/summary — 窗内总量。 */ export declare function usageSummary(rows: UsageRow[]): UsageTotals; export type UsageMetric = "tasks" | "tokensIn" | "tokensOut" | "costUsd"; export type UsageGranularity = "hour" | "day"; /** * GET /v1/usage/series — 时间序列(UTC 桶;空桶不发行,消费方按需补零)。 * * 🔴 **行带 `estimated`**(S-228):与 {@link usageSummary} / {@link usageBreakdown} **同轴同谓词** —— * 三个读面的桶都是同一只 {@link UsageTotals},由同一条 {@link foldRow} 折出来,所以「这个数字不精确」 * 的四个来源(旧行 fallback / per-model echo 缺键 / 缺 `usageBasis` 的 legacy 口径 / 行自陈 * `usageMissing` 的下界)在三面上是**同一句话**。修前只有 series 的出口把这一位扔了:桶里塞进一条 * 「没量到」的行,折线与一段真的低消耗在 wire 上逐字同形 —— 而那两件事的处置相反。 * 不新开判据、不加第二条轴:出口多投一个已经算好的位。 */ export declare function usageSeries(rows: UsageRow[], metric: UsageMetric, granularity: UsageGranularity): Array<{ t: string; v: number; estimated: boolean; }>; export type UsageDimension = "principal" | "model"; /** GET /v1/usage/breakdown — 按维度分桶。dimension=model 时按 per-model echo 展开(一行可贡献多个 model 桶; * fallback 行归 "(unattributed)");dimension=principal 时 owner NULL 归 "(anonymous)"。 */ export declare function usageBreakdown(rows: UsageRow[], dimension: UsageDimension): Array<{ key: string; } & UsageTotals>; /** store 行投影 helper:result JSON(已 parse 或原始字符串)→ UsageRow.stats。所有后端复用(方言只在 SQL)。 */ export declare function projectUsageStats(result: unknown): UsageRow["stats"]; /** usageScan 的统一行数上限(触顶=结果 truncated:true;试点规模远在限内)。 */ export declare const USAGE_SCAN_LIMIT = 50000; //# sourceMappingURL=usage-analytics.d.ts.map