diff --git a/crates/ide-agent/src/config.rs b/crates/ide-agent/src/config.rs index ea0c33c20..bfff80bbb 100644 --- a/crates/ide-agent/src/config.rs +++ b/crates/ide-agent/src/config.rs @@ -104,17 +104,10 @@ pub(crate) fn commands_of(update: &Value) -> Vec { .collect() } -/// `usage_update`: `{ used, size, cost?: { amount, currency } }`. An update without a cost -/// keeps the last one (Claude also sends one for its rate limits, without the cost). -pub(crate) fn usage_of(update: &Value, prev: Option) -> Option { +/// `usage_update`: `{ used, size }`. +pub(crate) fn usage_of(update: &Value) -> Option { let tokens = |key: &str| update.get(key)?.as_f64().map(|n| n.clamp(0.0, f64::from(u32::MAX)) as u32); - let (Some(used), Some(size)) = (tokens("used"), tokens("size").filter(|s| *s > 0)) else { return prev }; - let (cost, currency) = match (update.pointer("/cost/amount").and_then(Value::as_f64), prev) { - (Some(cost), _) => (Some(cost), update.pointer("/cost/currency").and_then(Value::as_str).map(str::to_owned)), - (None, Some(p)) => (p.cost, p.currency), - (None, None) => (None, None), - }; - Some(AgentUsage { used, size, cost, currency }) + Some(AgentUsage { used: tokens("used")?, size: tokens("size").filter(|s| *s > 0)? }) } fn legacy( @@ -217,16 +210,8 @@ mod tests { ]})); assert_eq!(cmds[0].hint.as_deref(), Some("what to review")); assert_eq!((cmds[1].name.as_str(), cmds[1].hint.as_ref()), ("init", None)); - let u = usage_of(&json!({"used": 1200, "size": 200000, "cost": {"amount": 0.42, "currency": "USD"}}), None) - .unwrap(); - assert_eq!((u.used, u.cost, u.currency.as_deref()), (1200, Some(0.42), Some("USD"))); - assert!(usage_of(&json!({"used": 1, "size": 0}), None).is_none()); - } - - #[test] - fn usage_without_a_cost_keeps_the_last_one() { - let u = usage_of(&json!({"used": 100, "size": 1000, "cost": {"amount": 1.5, "currency": "USD"}}), None); - let u = usage_of(&json!({"used": 200, "size": 1000, "_meta": {"_claude/rateLimit": {}}}), u).unwrap(); - assert_eq!((u.used, u.cost, u.currency.as_deref()), (200, Some(1.5), Some("USD"))); + let u = usage_of(&json!({"used": 1200, "size": 200000, "cost": {"amount": 0.42, "currency": "USD"}})).unwrap(); + assert_eq!((u.used, u.size), (1200, 200000)); + assert!(usage_of(&json!({"used": 1, "size": 0})).is_none()); } } diff --git a/crates/ide-agent/src/lib.rs b/crates/ide-agent/src/lib.rs index 584962e1f..437dac00a 100644 --- a/crates/ide-agent/src/lib.rs +++ b/crates/ide-agent/src/lib.rs @@ -915,7 +915,7 @@ impl Task { true } "usage_update" => { - s.task.usage = config::usage_of(update, s.task.usage.take()); + s.task.usage = config::usage_of(update).or(s.task.usage.take()); true } _ => false, diff --git a/crates/ide-agent/src/model.rs b/crates/ide-agent/src/model.rs index 369882ee6..5ba2707a1 100644 --- a/crates/ide-agent/src/model.rs +++ b/crates/ide-agent/src/model.rs @@ -114,9 +114,6 @@ pub struct AgentUsage { pub used: u32, /// Size of the context window. pub size: u32, - /// Session cost so far. - pub cost: Option, - pub currency: Option, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Type)] diff --git a/src/bindings.ts b/src/bindings.ts index d0677fb15..5ca5aa183 100644 --- a/src/bindings.ts +++ b/src/bindings.ts @@ -1112,9 +1112,6 @@ export type AgentUsage = { used: number, /** Size of the context window. */ size: number, - /** Session cost so far. */ - cost: number | null, - currency: string | null, }; export type AppInfo = { diff --git a/src/features/agents/SessionEditor.tsx b/src/features/agents/SessionEditor.tsx index e670a3172..b6e6351af 100644 --- a/src/features/agents/SessionEditor.tsx +++ b/src/features/agents/SessionEditor.tsx @@ -172,7 +172,6 @@ function Header({ task, entries, head }: { task: AgentTask; entries: AgentEntry[ ); } -/** Context window use: "12%", the numbers (and cost) in the tooltip. */ /** Context use (when the agent reports it) and the time spent on the task. */ function Meters({ task, entries }: { task: AgentTask; entries: AgentEntry[] | null }) { const tx = useTx(); @@ -188,6 +187,7 @@ function Meters({ task, entries }: { task: AgentTask; entries: AgentEntry[] | nu ); } +/** Context window use: "12%", the numbers in the tooltip. */ function Usage({ usage }: { usage: AgentUsage }) { const label = usageLabel(usage); return ( diff --git a/src/features/agents/agents.test.ts b/src/features/agents/agents.test.ts index b8b4c49b0..85c29fb69 100644 --- a/src/features/agents/agents.test.ts +++ b/src/features/agents/agents.test.ts @@ -124,9 +124,9 @@ describe("agent composer", () => { "eye", "lock", ]); - expect(usageLabel({ used: 24_000, size: 200_000, cost: 0.4, currency: "USD" })).toEqual({ + expect(usageLabel({ used: 24_000, size: 200_000 })).toEqual({ short: "12%", - detail: "24k / 200k tokens · $0.40", + detail: "24k / 200k tokens", }); expect(imageFromDataUrl("data:image/png;base64,AAA", "a.png")).toEqual({ mimeType: "image/png", data: "AAA", name: "a.png" }); expect(imageFromDataUrl("data:text/plain;base64,AAA")).toBeNull(); @@ -135,7 +135,7 @@ describe("agent composer", () => { describe("usage meters", () => { it("formats token counts", () => { - expect(usageLabel({ used: 83_100, size: 1_000_000, cost: null, currency: null }).detail).toBe("83.1k / 1M tokens"); + expect(usageLabel({ used: 83_100, size: 1_000_000 }).detail).toBe("83.1k / 1M tokens"); }); it("adds up the time spent on turns", () => { diff --git a/src/features/agents/composerModel.ts b/src/features/agents/composerModel.ts index cb8c2cb59..17468b8d4 100644 --- a/src/features/agents/composerModel.ts +++ b/src/features/agents/composerModel.ts @@ -131,8 +131,7 @@ export function nextValue(option: AgentConfigOption, current = option.current): /** "12%" of the context window and a tooltip with the numbers. */ export function usageLabel(u: AgentUsage): { short: string; detail: string } { const pct = Math.min(100, Math.round((u.used / u.size) * 100)); - const cost = u.cost != null ? ` · ${costLabel(u)}` : ""; - return { short: `${pct}%`, detail: tx`${tokens(u.used)} / ${tokens(u.size)} tokens${cost}` }; + return { short: `${pct}%`, detail: tx`${tokens(u.used)} / ${tokens(u.size)} tokens` }; } /** `850`, `83.1k`, `1M`. */ @@ -142,12 +141,6 @@ function tokens(n: number): string { return `${Math.round(n)}`; } -/** Session cost (`$4.23`), when the agent reports one. */ -function costLabel(u: AgentUsage): string | null { - if (u.cost == null) return null; - return `${u.currency === "USD" || !u.currency ? "$" : `${u.currency} `}${u.cost.toFixed(2)}`; -} - /** Time the agent spent on the task's turns; a running one counts up to `now` (`null` when idle). */ export function workTime(entries: readonly AgentEntry[], now: number | null): number { const users = entries.filter((e): e is Extract => e.type === "user"); diff --git a/src/platform/locales/text/zh.json b/src/platform/locales/text/zh.json index 96a4bba59..994c2e993 100644 --- a/src/platform/locales/text/zh.json +++ b/src/platform/locales/text/zh.json @@ -3697,7 +3697,7 @@ "Delete “{title}”? Its branch {branch} is kept.": "是否删除“{title}”?其分支 {branch} 将保留。", "Delete “{title}”? Files it changed are kept.": "是否删除“{title}”?它更改的文件将保留。", "{name} is over 5 MB.": "{name} 超过 5 MB。", - "{used} / {size} tokens{cost}": "{used} / {size} 个令牌{cost}", + "{used} / {size} tokens": "{used} / {size} 个令牌", "Replace in {path}:{line}": "在 {path}:{line} 中替换", "Clean up {count} files?": "是否清理 {count} 个文件?", "Leave empty to use the folder name ({folder}).": "留空则使用文件夹名称({folder})。",