Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions tycode-cli/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,19 @@ pub enum LocalCommandResult {

pub fn handle_local_command(state: &mut State, input: &str) -> LocalCommandResult {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unrelated: I noticed local commands don't show up in /help. I started fixing it, but not relevant for this PR.

match input.trim() {
"/timing" => {
state.show_timing = !state.show_timing;
LocalCommandResult::Handled {
msg: format!(
"Timings: {}",
if state.show_timing {
"enabled"
} else {
"disabled"
}
),
}
}
"/verbose" => {
state.show_reasoning = !state.show_reasoning;
LocalCommandResult::Handled {
Expand Down
16 changes: 16 additions & 0 deletions tycode-cli/src/interactive_app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,22 @@ impl InteractiveApp {
ChatEvent::ProfilesList { .. } => {
// CLI handles profiles via slash commands, ignore this event
}
ChatEvent::TimingUpdate {
waiting_for_human,
ai_processing,
tool_execution,
} => {
let total = waiting_for_human + ai_processing + tool_execution;
if self.state.show_timing {
self.formatter.print_system(&format!(
"Timing => Human: {:.1}s, AI: {:.1}s, Tools: {:.1}s, Total: {:.1}s",
waiting_for_human.as_secs_f64(),
ai_processing.as_secs_f64(),
tool_execution.as_secs_f64(),
total.as_secs_f64(),
));
}
}
}
Ok(())
}
Expand Down
1 change: 1 addition & 0 deletions tycode-cli/src/state.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#[derive(Default)]
pub struct State {
pub show_reasoning: bool,
pub show_timing: bool,
}
43 changes: 33 additions & 10 deletions tycode-core/src/chat/actor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,28 +38,41 @@ pub enum TimingState {
ExecutingTools,
}

#[derive(Debug, Clone)]
pub struct TimingStats {
#[derive(Clone, Debug, Default)]
pub struct TimingStat {
pub waiting_for_human: Duration,
pub ai_processing: Duration,
pub tool_execution: Duration,
}

impl std::ops::AddAssign for TimingStat {
fn add_assign(&mut self, rhs: Self) {
self.waiting_for_human += rhs.waiting_for_human;
self.ai_processing += rhs.ai_processing;
self.tool_execution += rhs.tool_execution;
}
}

#[derive(Debug, Clone)]
pub struct TimingStats {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A thought about naming:

Could rename this to Timing and rename TimingStat to TimingStats. Fits a bit better, as current_state and state_start aren't really fitting to be under a name TimingStats.

message: TimingStat,
session: TimingStat,
current_state: TimingState,
state_start: Option<Instant>,
}

impl TimingStats {
fn new() -> Self {
Self {
waiting_for_human: Duration::ZERO,
ai_processing: Duration::ZERO,
tool_execution: Duration::ZERO,
message: TimingStat::default(),
session: TimingStat::default(),
current_state: TimingState::Idle,
state_start: Some(Instant::now()),
}
}

pub fn total_time(&self) -> Duration {
self.waiting_for_human + self.ai_processing + self.tool_execution
pub fn session(&self) -> TimingStat {
self.session.clone()
}
}

Expand Down Expand Up @@ -447,18 +460,23 @@ impl ActorState {
let elapsed = start.elapsed();
match self.timing_stats.current_state {
TimingState::WaitingForHuman => {
self.timing_stats.waiting_for_human += elapsed;
self.timing_stats.message.waiting_for_human += elapsed;
}
TimingState::ProcessingAI => {
self.timing_stats.ai_processing += elapsed;
self.timing_stats.message.ai_processing += elapsed;
}
TimingState::ExecutingTools => {
self.timing_stats.tool_execution += elapsed;
self.timing_stats.message.tool_execution += elapsed;
}
TimingState::Idle => {}
}
}

if matches!(new_state, TimingState::WaitingForHuman) {
let message = std::mem::replace(&mut self.timing_stats.message, TimingStat::default());
self.timing_stats.session += message;
}

self.timing_stats.current_state = new_state;
self.timing_stats.state_start = Some(Instant::now());
}
Expand Down Expand Up @@ -517,6 +535,11 @@ async fn run_actor(
}
}

state.event_sender.send(ChatEvent::TimingUpdate {
waiting_for_human: state.timing_stats.message.waiting_for_human,
ai_processing: state.timing_stats.message.ai_processing,
tool_execution: state.timing_stats.message.tool_execution,
});
state.event_sender.set_typing(false);
state.transition_timing_state(TimingState::WaitingForHuman);
}
Expand Down
17 changes: 11 additions & 6 deletions tycode-core/src/chat/commands.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::agents::catalog::AgentCatalog;
use crate::ai::model::{Model, ModelCost};
use crate::ai::{ModelSettings, ReasoningBudget, TokenUsage, ToolUseData};
use crate::chat::actor::{create_provider, resume_session};
use crate::chat::actor::{create_provider, resume_session, TimingStat};
use crate::chat::ai::select_model_for_agent;
use crate::chat::tools::{current_agent, current_agent_mut};
use crate::chat::{
Expand Down Expand Up @@ -585,23 +585,28 @@ async fn handle_cost_command(state: &ActorState) -> Vec<ChatMessage> {
message.push_str(&format!(" Average per 1K tokens: ${avg_cost_per_1k:.6}\n"));
}

let timing = &state.timing_stats;
let TimingStat {
waiting_for_human,
ai_processing,
tool_execution,
} = state.timing_stats.session();
let total_time = waiting_for_human + ai_processing + tool_execution;
message.push_str("\nTime Spent:\n");
message.push_str(&format!(
" Waiting for human: {:>6.1}s\n",
timing.waiting_for_human.as_secs_f64()
waiting_for_human.as_secs_f64()
));
message.push_str(&format!(
" AI processing: {:>6.1}s\n",
timing.ai_processing.as_secs_f64()
ai_processing.as_secs_f64()
));
message.push_str(&format!(
" Tool execution: {:>6.1}s\n",
timing.tool_execution.as_secs_f64()
tool_execution.as_secs_f64()
));
message.push_str(&format!(
" Total session: {:>6.1}s\n",
timing.total_time().as_secs_f64()
total_time.as_secs_f64()
));

vec![create_message(message, MessageSender::System)]
Expand Down
6 changes: 6 additions & 0 deletions tycode-core/src/chat/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::persistence::session::SessionMetadata;
use crate::tools::tasks::TaskList;
use chrono::Utc;
use serde::{Deserialize, Serialize};
use std::time::Duration;
use tokio::sync::mpsc;

/// `ChatEvent` are the messages sent from the actor - the output of the actor.
Expand Down Expand Up @@ -43,6 +44,11 @@ pub enum ChatEvent {
ProfilesList {
profiles: Vec<String>,
},
TimingUpdate {
waiting_for_human: Duration,
ai_processing: Duration,
tool_execution: Duration,
},
Error(String),
}

Expand Down