mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Add goal lifecycle metrics (#20799)
## Why Adding goal metrics makes it possible to track how often goals are created, completed, and stopped by budget limits, plus the final token and wall-clock usage for terminal outcomes. ## What Changed - Added OpenTelemetry metric constants for goal lifecycle tracking: - `codex.goal.created`: increments each time a new persisted goal is created or an existing goal is replaced with a new objective. - `codex.goal.completed`: increments when a goal transitions to `complete`. - `codex.goal.budget_limited`: increments when a goal transitions to `budget_limited` because its token budget has been reached. - `codex.goal.token_count`: records the final persisted token count when a goal transitions to `complete` or `budget_limited`. - `codex.goal.duration_s`: records the final persisted elapsed wall-clock time, in seconds, when a goal transitions to `complete` or `budget_limited`. - Emitted creation metrics when a goal is created or replaced. - Emitted terminal outcome counters and final usage histograms when a goal transitions to `complete` or `budget_limited`, avoiding double-counting later in-flight accounting for already budget-limited goals. - Added focused `codex-core` tests for create/complete metrics and one-time budget-limit metrics.
This commit is contained in:
committed by
GitHub
Unverified
parent
69283aa1c0
commit
91b7350187
@@ -246,6 +246,8 @@ use codex_config::loader::project_trust_key;
|
||||
use codex_config::types::McpServerTransportConfig;
|
||||
use codex_core::CodexThread;
|
||||
use codex_core::CodexThreadTurnContextOverrides;
|
||||
use codex_core::ExternalGoalPreviousStatus;
|
||||
use codex_core::ExternalGoalSet;
|
||||
use codex_core::ForkSnapshot;
|
||||
use codex_core::NewThread;
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -148,7 +148,7 @@ impl ThreadGoalRequestProcessor {
|
||||
thread.prepare_external_goal_mutation().await;
|
||||
}
|
||||
|
||||
let goal = (if let Some(objective) = objective {
|
||||
let (goal, previous_status) = (if let Some(objective) = objective {
|
||||
let existing_goal = state_db
|
||||
.get_thread_goal(thread_id)
|
||||
.await
|
||||
@@ -157,6 +157,7 @@ impl ThreadGoalRequestProcessor {
|
||||
goal.objective == objective
|
||||
&& goal.status != codex_state::ThreadGoalStatus::Complete
|
||||
}) {
|
||||
let previous_status = ExternalGoalPreviousStatus::Existing(goal.status);
|
||||
state_db
|
||||
.update_thread_goal(
|
||||
thread_id,
|
||||
@@ -174,7 +175,9 @@ impl ThreadGoalRequestProcessor {
|
||||
)
|
||||
})
|
||||
})
|
||||
.map(|goal| (goal, previous_status))
|
||||
} else {
|
||||
let previous_status = ExternalGoalPreviousStatus::NewGoal;
|
||||
state_db
|
||||
.replace_thread_goal(
|
||||
thread_id,
|
||||
@@ -183,8 +186,19 @@ impl ThreadGoalRequestProcessor {
|
||||
params.token_budget.flatten(),
|
||||
)
|
||||
.await
|
||||
.map(|goal| (goal, previous_status))
|
||||
}
|
||||
} else {
|
||||
let existing_goal = state_db
|
||||
.get_thread_goal(thread_id)
|
||||
.await
|
||||
.map_err(|err| invalid_request(err.to_string()))?;
|
||||
let Some(existing_goal) = existing_goal else {
|
||||
return Err(invalid_request(format!(
|
||||
"cannot update goal for thread {thread_id}: no goal exists"
|
||||
)));
|
||||
};
|
||||
let previous_status = ExternalGoalPreviousStatus::Existing(existing_goal.status);
|
||||
state_db
|
||||
.update_thread_goal(
|
||||
thread_id,
|
||||
@@ -200,9 +214,13 @@ impl ThreadGoalRequestProcessor {
|
||||
anyhow::anyhow!("cannot update goal for thread {thread_id}: no goal exists")
|
||||
})
|
||||
})
|
||||
.map(|goal| (goal, previous_status))
|
||||
})
|
||||
.map_err(|err| invalid_request(err.to_string()))?;
|
||||
let goal_status = goal.status;
|
||||
let external_goal_set = ExternalGoalSet {
|
||||
goal: goal.clone(),
|
||||
previous_status,
|
||||
};
|
||||
let goal = api_thread_goal_from_state(goal);
|
||||
self.outgoing
|
||||
.send_response(
|
||||
@@ -213,7 +231,7 @@ impl ThreadGoalRequestProcessor {
|
||||
self.emit_thread_goal_updated_ordered(thread_id, goal, listener_command_tx)
|
||||
.await;
|
||||
if let Some(thread) = running_thread.as_ref() {
|
||||
thread.apply_external_goal_set(goal_status).await;
|
||||
thread.apply_external_goal_set(external_goal_set).await;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user