mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
[codex-analytics] emit goal lifecycle analytics (#27078)
## Why - Currently, there is no analytics event for `/goal` behavior - Existing events cannot identify goal execution or its resulting outcome - The original update in [#26182](https://github.com/openai/codex/pull/26182) was implemented before `/goal` moved into `codex-goal-extension`. ## What Changed - Adds `codex_goal_event` serialization and enrichment to `codex-analytics` - Emits goal events from the canonical `codex-goal-extension` mutation and accounting paths: - `created` when a new logical goal is persisted - `usage_accounted` when cumulative goal usage is persisted - `status_changed` when the stored goal status changes - `cleared` when the goal is deleted - Preserves causal `turn_id` for turn driven events and uses null attribution for external or idle lifecycle events - Changes goal deletion to return the deleted row so `cleared` retains the stable goal ID ## Event Details Includes standard analytics metadata along with goal specific fields: - `goal_id`: Stable ID stored in the local SQLite goal row and shared across the goal's events - `event_kind`: Observed operation (see the 4 lifecycle events cited in the above bullet) - `goal_status`: Resulting or last stored status: `active`, `paused`, `blocked`, `usage_limited`, etc. - `has_token_budget`: Indicates whether a token budget is configured - `turn_id`: Causal turn ID, or null when no causal turn exists - `cumulative_tokens_accounted`: Cumulative tokens on `usage_accounted` events; null otherwise - `cumulative_time_accounted_seconds`: Cumulative active time on `usage_accounted` events; null otherwise ## Validation - `just test -p codex-analytics -p codex-state -p codex-goal-extension` - `just test -p codex-core -E 'test(/goal/)'` - `just test -p codex-app-server` - `cargo build -p codex-analytics -p codex-core -p codex-state -p codex-app-server`
This commit is contained in:
1 parent
4a3eac2144
commit
608b8b1cc6
23 files changed
+412
-24
No files matched your search
@@ -17,6 +17,8 @@ use serde::Serialize;
|
||||
|
||||
use crate::accounting::BudgetLimitedGoalDisposition;
|
||||
use crate::accounting::GoalAccountingState;
|
||||
use crate::analytics::GoalAnalytics;
|
||||
use crate::analytics::GoalEventAttribution;
|
||||
use crate::events::GoalEventEmitter;
|
||||
use crate::metrics::GoalMetrics;
|
||||
use crate::spec::CREATE_GOAL_TOOL_NAME;
|
||||
@@ -32,6 +34,7 @@ pub(crate) struct GoalToolExecutor {
|
||||
thread_id: ThreadId,
|
||||
state_db: Arc<codex_state::StateRuntime>,
|
||||
accounting_state: Arc<GoalAccountingState>,
|
||||
analytics: GoalAnalytics,
|
||||
event_emitter: GoalEventEmitter,
|
||||
metrics: GoalMetrics,
|
||||
}
|
||||
@@ -75,6 +78,7 @@ impl GoalToolExecutor {
|
||||
thread_id: ThreadId,
|
||||
state_db: Arc<codex_state::StateRuntime>,
|
||||
accounting_state: Arc<GoalAccountingState>,
|
||||
analytics: GoalAnalytics,
|
||||
event_emitter: GoalEventEmitter,
|
||||
metrics: GoalMetrics,
|
||||
) -> Self {
|
||||
@@ -83,6 +87,7 @@ impl GoalToolExecutor {
|
||||
thread_id,
|
||||
state_db,
|
||||
accounting_state,
|
||||
analytics,
|
||||
event_emitter,
|
||||
metrics,
|
||||
}
|
||||
@@ -92,6 +97,7 @@ impl GoalToolExecutor {
|
||||
thread_id: ThreadId,
|
||||
state_db: Arc<codex_state::StateRuntime>,
|
||||
accounting_state: Arc<GoalAccountingState>,
|
||||
analytics: GoalAnalytics,
|
||||
event_emitter: GoalEventEmitter,
|
||||
metrics: GoalMetrics,
|
||||
) -> Self {
|
||||
@@ -100,6 +106,7 @@ impl GoalToolExecutor {
|
||||
thread_id,
|
||||
state_db,
|
||||
accounting_state,
|
||||
analytics,
|
||||
event_emitter,
|
||||
metrics,
|
||||
}
|
||||
@@ -109,6 +116,7 @@ impl GoalToolExecutor {
|
||||
thread_id: ThreadId,
|
||||
state_db: Arc<codex_state::StateRuntime>,
|
||||
accounting_state: Arc<GoalAccountingState>,
|
||||
analytics: GoalAnalytics,
|
||||
event_emitter: GoalEventEmitter,
|
||||
metrics: GoalMetrics,
|
||||
) -> Self {
|
||||
@@ -117,6 +125,7 @@ impl GoalToolExecutor {
|
||||
thread_id,
|
||||
state_db,
|
||||
accounting_state,
|
||||
analytics,
|
||||
event_emitter,
|
||||
metrics,
|
||||
}
|
||||
@@ -200,6 +209,10 @@ impl GoalToolExecutor {
|
||||
.accounting_state
|
||||
.mark_current_turn_goal_active(goal.goal_id.clone());
|
||||
self.metrics.record_created();
|
||||
self.analytics.created(
|
||||
&goal,
|
||||
GoalEventAttribution::Turn(invocation.turn_id.as_str()),
|
||||
);
|
||||
let goal = protocol_goal_from_state(goal);
|
||||
self.emit_goal_updated_from_tool_call(&invocation, turn_id, goal.clone());
|
||||
goal_response(Some(goal), CompletionBudgetReport::Omit)
|
||||
@@ -259,6 +272,11 @@ impl GoalToolExecutor {
|
||||
})?;
|
||||
self.metrics
|
||||
.record_terminal_if_status_changed(previous_status, &goal);
|
||||
self.analytics.status_changed(
|
||||
&goal,
|
||||
previous_status,
|
||||
GoalEventAttribution::Turn(invocation.turn_id.as_str()),
|
||||
);
|
||||
let goal = protocol_goal_from_state(goal);
|
||||
let turn_id = self.accounting_state.clear_current_turn_goal();
|
||||
self.emit_goal_updated_from_tool_call(&invocation, turn_id, goal.clone());
|
||||
@@ -324,6 +342,13 @@ impl GoalToolExecutor {
|
||||
codex_state::GoalAccountingOutcome::Updated(goal) => {
|
||||
self.metrics
|
||||
.record_terminal_if_status_changed(previous_status, &goal);
|
||||
self.analytics
|
||||
.usage_accounted(&goal, GoalEventAttribution::Turn(turn_id.as_str()));
|
||||
self.analytics.status_changed(
|
||||
&goal,
|
||||
previous_status,
|
||||
GoalEventAttribution::Turn(turn_id.as_str()),
|
||||
);
|
||||
self.accounting_state.mark_progress_accounted_for_status(
|
||||
turn_id.as_str(),
|
||||
&snapshot,
|
||||
|
||||
Reference in new issue
Block a user