Merge remote-tracking branch 'origin/pr/1192' into work/v0.8.34
# Conflicts: # crates/tui/src/commands/session.rs # crates/tui/src/session_manager.rs # crates/tui/src/tui/session_picker.rs # crates/tui/src/tui/sidebar.rs # crates/tui/src/tui/ui.rs
This commit is contained in:
@@ -74,7 +74,9 @@ pub fn tokens(app: &mut App) -> CommandResult {
|
||||
.replace("{total}", &app.session.total_tokens.to_string())
|
||||
.replace(
|
||||
"{cost}",
|
||||
&app.format_cost_amount_precise(app.session_cost_for_currency(app.cost_currency)),
|
||||
&app.format_cost_amount_precise(
|
||||
app.displayed_session_cost_for_currency(app.cost_currency),
|
||||
),
|
||||
)
|
||||
.replace("{api_messages}", &message_count.to_string())
|
||||
.replace("{chat_messages}", &chat_count.to_string())
|
||||
@@ -84,9 +86,10 @@ pub fn tokens(app: &mut App) -> CommandResult {
|
||||
|
||||
/// Show session cost breakdown
|
||||
pub fn cost(app: &mut App) -> CommandResult {
|
||||
let total = app.displayed_session_cost_for_currency(app.cost_currency);
|
||||
let report = tr(app.ui_locale, MessageId::CmdCostReport).replace(
|
||||
"{cost}",
|
||||
&app.format_cost_amount_precise(app.session_cost_for_currency(app.cost_currency)),
|
||||
&app.format_cost_amount_precise(total),
|
||||
);
|
||||
CommandResult::message(report)
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ fn rename_with_manager(
|
||||
u64::from(app.session.total_tokens),
|
||||
app.system_prompt.as_ref(),
|
||||
);
|
||||
app.sync_cost_to_metadata(&mut session.metadata);
|
||||
session.metadata.title = new_title.to_string();
|
||||
|
||||
match manager.save_session(&session) {
|
||||
|
||||
@@ -28,6 +28,7 @@ pub fn save(app: &mut App, path: Option<&str>) -> CommandResult {
|
||||
app.system_prompt.as_ref(),
|
||||
Some(app.mode.label()),
|
||||
);
|
||||
app.sync_cost_to_metadata(&mut session.metadata);
|
||||
session.artifacts = app.session_artifacts.clone();
|
||||
|
||||
let sessions_dir = save_path
|
||||
|
||||
@@ -3060,13 +3060,14 @@ fn fork_session(session_id: Option<String>, last: bool, workspace: &Path) -> Res
|
||||
.system_prompt
|
||||
.as_ref()
|
||||
.map(|text| SystemPrompt::Text(text.clone()));
|
||||
let forked = create_saved_session(
|
||||
let mut forked = create_saved_session(
|
||||
&saved.messages,
|
||||
&saved.metadata.model,
|
||||
&saved.metadata.workspace,
|
||||
saved.metadata.total_tokens,
|
||||
system_prompt.as_ref(),
|
||||
);
|
||||
forked.metadata.copy_cost_from(&saved.metadata);
|
||||
manager.save_session(&forked)?;
|
||||
|
||||
let source_title = saved.metadata.title.trim();
|
||||
|
||||
@@ -1784,6 +1784,18 @@ impl App {
|
||||
self.refresh_displayed_cost_high_water();
|
||||
}
|
||||
|
||||
/// Copy current session/subagent cost accumulators into session metadata
|
||||
/// for persistence.
|
||||
pub fn sync_cost_to_metadata(&self, metadata: &mut crate::session_manager::SessionMetadata) {
|
||||
metadata.cost.session_cost_usd = self.session.session_cost;
|
||||
metadata.cost.session_cost_cny = self.session.session_cost_cny;
|
||||
metadata.cost.subagent_cost_usd = self.session.subagent_cost;
|
||||
metadata.cost.subagent_cost_cny = self.session.subagent_cost_cny;
|
||||
metadata.cost.displayed_cost_high_water_usd = self.session.displayed_cost_high_water;
|
||||
metadata.cost.displayed_cost_high_water_cny =
|
||||
self.session.displayed_cost_high_water_cny;
|
||||
}
|
||||
|
||||
/// Recompute the displayed cost high-water mark. Called any time a cost
|
||||
/// counter is mutated; never decreases.
|
||||
pub fn refresh_displayed_cost_high_water(&mut self) {
|
||||
|
||||
@@ -17,8 +17,8 @@ use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
|
||||
|
||||
use crate::palette;
|
||||
use crate::session_manager::{
|
||||
SavedSession, SessionManager, SessionMetadata, extract_title, extract_user_prompt,
|
||||
strip_thinking_tags,
|
||||
SavedSession, SessionCostSnapshot, SessionManager, SessionMetadata, extract_title,
|
||||
extract_user_prompt, strip_thinking_tags,
|
||||
};
|
||||
use crate::tui::views::{ModalKind, ModalView, ViewAction, ViewEvent};
|
||||
|
||||
@@ -801,7 +801,7 @@ mod tests {
|
||||
model: "deepseek-v4-pro".to_string(),
|
||||
workspace: std::path::PathBuf::from("/tmp"),
|
||||
mode: Some("agent".to_string()),
|
||||
cost: crate::session_manager::SessionCostSnapshot::default(),
|
||||
cost: SessionCostSnapshot::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1339,22 +1339,22 @@ fn render_context_panel(f: &mut Frame, area: Rect, app: &App) {
|
||||
)));
|
||||
|
||||
// ── Session cost ─────────────────────────────────────────────
|
||||
let total_cost = app.displayed_session_cost_for_currency(app.cost_currency);
|
||||
let displayed_total = app.displayed_session_cost_for_currency(app.cost_currency);
|
||||
let session_cost = app.session_cost_for_currency(app.cost_currency);
|
||||
let agent_cost = app.subagent_cost_for_currency(app.cost_currency);
|
||||
let real_total = session_cost + agent_cost;
|
||||
// Only show the additive breakdown when it matches the displayed
|
||||
// total; when the high-water mark is in effect (post-reconciliation),
|
||||
// the breakdown would not sum to the displayed value (#244).
|
||||
let cost_line = if (total_cost - real_total).abs() < COST_EQ_TOLERANCE {
|
||||
let cost_line = if (displayed_total - real_total).abs() < COST_EQ_TOLERANCE {
|
||||
format!(
|
||||
"cost: {} (session {} + agents {})",
|
||||
app.format_cost_amount(total_cost),
|
||||
app.format_cost_amount(displayed_total),
|
||||
app.format_cost_amount(session_cost),
|
||||
app.format_cost_amount(agent_cost)
|
||||
)
|
||||
} else {
|
||||
format!("cost: {}", app.format_cost_amount(total_cost),)
|
||||
format!("cost: {}", app.format_cost_amount(displayed_total))
|
||||
};
|
||||
lines.push(Line::from(Span::styled(
|
||||
cost_line,
|
||||
|
||||
@@ -3481,6 +3481,7 @@ fn build_session_snapshot(app: &App, manager: &SessionManager) -> SavedSession {
|
||||
app.system_prompt.as_ref(),
|
||||
);
|
||||
updated.metadata.mode = Some(app.mode.as_setting().to_string());
|
||||
app.sync_cost_to_metadata(&mut updated.metadata);
|
||||
updated.context_references = app.session_context_references.clone();
|
||||
updated.artifacts = app.session_artifacts.clone();
|
||||
updated
|
||||
@@ -3505,6 +3506,7 @@ fn build_session_snapshot(app: &App, manager: &SessionManager) -> SavedSession {
|
||||
Some(app.mode.as_setting()),
|
||||
)
|
||||
};
|
||||
app.sync_cost_to_metadata(&mut session.metadata);
|
||||
session.context_references = app.session_context_references.clone();
|
||||
session.artifacts = app.session_artifacts.clone();
|
||||
session
|
||||
@@ -6874,10 +6876,10 @@ fn apply_loaded_session(app: &mut App, session: &SavedSession) -> bool {
|
||||
app.workspace.clone_from(&session.metadata.workspace);
|
||||
app.session.total_tokens = u32::try_from(session.metadata.total_tokens).unwrap_or(u32::MAX);
|
||||
app.session.total_conversation_tokens = app.session.total_tokens;
|
||||
app.session.session_cost = 0.0;
|
||||
app.session.session_cost_cny = 0.0;
|
||||
app.session.subagent_cost = 0.0;
|
||||
app.session.subagent_cost_cny = 0.0;
|
||||
app.session.session_cost = session.metadata.cost.session_cost_usd;
|
||||
app.session.session_cost_cny = session.metadata.cost.session_cost_cny;
|
||||
app.session.subagent_cost = session.metadata.cost.subagent_cost_usd;
|
||||
app.session.subagent_cost_cny = session.metadata.cost.subagent_cost_cny;
|
||||
app.session.subagent_cost_event_seqs.clear();
|
||||
// Restore the high-water marks from persisted metadata so the
|
||||
// monotonic cost guarantee (#244) survives session restarts.
|
||||
|
||||
Reference in New Issue
Block a user