mirror of
https://github.com/openai/codex.git
synced 2026-11-07 00:33:29 +00:00
[codex-analytics] Emit structured compaction codex errors (#27082)
## Summary - replace raw compaction `error` analytics with `codex_error_kind` and `codex_error_http_status_code` - derive compaction error telemetry from `CodexErr` using the same `CodexErrKind` mapping and HTTP status helper used by turn events - remove the pre-compact hook stop reason from the internal compaction outcome now that it is no longer emitted as raw analytics text ## Why Compaction `error` was a raw `CodexErr::to_string()` value, which can carry free-form provider or user-derived text. Structured Codex error fields preserve useful low-cardinality telemetry without sending the raw string. ## Validation - `just fmt` - `just test -p codex-analytics` - `just test -p codex-core compact::tests::build_token_limited_compacted_history_appends_summary_message` Attempted `just test -p codex-core`; the changed crate compiled, but the full target failed in unrelated environment-dependent tests such as missing helper binaries and shell snapshot timeouts.
This commit is contained in:
1 parent
b5a718ef67
commit
8d9f33c87c
8 files changed
+52
-35
No files matched your search
@@ -43,6 +43,7 @@ use crate::facts::AppInvocation;
|
|||||||
use crate::facts::AppMentionedInput;
|
use crate::facts::AppMentionedInput;
|
||||||
use crate::facts::AppUsedInput;
|
use crate::facts::AppUsedInput;
|
||||||
use crate::facts::CodexCompactionEvent;
|
use crate::facts::CodexCompactionEvent;
|
||||||
|
use crate::facts::CodexErrKind;
|
||||||
use crate::facts::CompactionImplementation;
|
use crate::facts::CompactionImplementation;
|
||||||
use crate::facts::CompactionPhase;
|
use crate::facts::CompactionPhase;
|
||||||
use crate::facts::CompactionReason;
|
use crate::facts::CompactionReason;
|
||||||
@@ -1278,7 +1279,8 @@ fn compaction_event_serializes_expected_shape() {
|
|||||||
phase: CompactionPhase::MidTurn,
|
phase: CompactionPhase::MidTurn,
|
||||||
strategy: CompactionStrategy::Memento,
|
strategy: CompactionStrategy::Memento,
|
||||||
status: CompactionStatus::Completed,
|
status: CompactionStatus::Completed,
|
||||||
error: None,
|
codex_error_kind: None,
|
||||||
|
codex_error_http_status_code: None,
|
||||||
active_context_tokens_before: 120_000,
|
active_context_tokens_before: 120_000,
|
||||||
active_context_tokens_after: 18_000,
|
active_context_tokens_after: 18_000,
|
||||||
retained_image_count: None,
|
retained_image_count: None,
|
||||||
@@ -1329,7 +1331,8 @@ fn compaction_event_serializes_expected_shape() {
|
|||||||
"phase": "mid_turn",
|
"phase": "mid_turn",
|
||||||
"strategy": "memento",
|
"strategy": "memento",
|
||||||
"status": "completed",
|
"status": "completed",
|
||||||
"error": null,
|
"codex_error_kind": null,
|
||||||
|
"codex_error_http_status_code": null,
|
||||||
"active_context_tokens_before": 120000,
|
"active_context_tokens_before": 120000,
|
||||||
"active_context_tokens_after": 18000,
|
"active_context_tokens_after": 18000,
|
||||||
"retained_image_count": null,
|
"retained_image_count": null,
|
||||||
@@ -1852,7 +1855,8 @@ async fn compaction_event_ingests_custom_fact() {
|
|||||||
phase: CompactionPhase::StandaloneTurn,
|
phase: CompactionPhase::StandaloneTurn,
|
||||||
strategy: CompactionStrategy::Memento,
|
strategy: CompactionStrategy::Memento,
|
||||||
status: CompactionStatus::Failed,
|
status: CompactionStatus::Failed,
|
||||||
error: Some("context limit exceeded".to_string()),
|
codex_error_kind: Some(CodexErrKind::ContextWindowExceeded),
|
||||||
|
codex_error_http_status_code: None,
|
||||||
active_context_tokens_before: 131_000,
|
active_context_tokens_before: 131_000,
|
||||||
active_context_tokens_after: 131_000,
|
active_context_tokens_after: 131_000,
|
||||||
retained_image_count: None,
|
retained_image_count: None,
|
||||||
@@ -1873,6 +1877,14 @@ async fn compaction_event_ingests_custom_fact() {
|
|||||||
assert_eq!(payload[0]["event_params"]["session_id"], "session-thread-1");
|
assert_eq!(payload[0]["event_params"]["session_id"], "session-thread-1");
|
||||||
assert_eq!(payload[0]["event_params"]["thread_id"], "thread-1");
|
assert_eq!(payload[0]["event_params"]["thread_id"], "thread-1");
|
||||||
assert_eq!(payload[0]["event_params"]["turn_id"], "turn-compact");
|
assert_eq!(payload[0]["event_params"]["turn_id"], "turn-compact");
|
||||||
|
assert_eq!(
|
||||||
|
payload[0]["event_params"]["codex_error_kind"],
|
||||||
|
json!("context_window_exceeded")
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
payload[0]["event_params"]["codex_error_http_status_code"],
|
||||||
|
json!(null)
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
payload[0]["event_params"]["app_server_client"]["product_client_id"],
|
payload[0]["event_params"]["app_server_client"]["product_client_id"],
|
||||||
DEFAULT_ORIGINATOR
|
DEFAULT_ORIGINATOR
|
||||||
@@ -2779,7 +2791,8 @@ async fn subagent_events_use_inherited_connection_unless_turn_connection_is_expl
|
|||||||
phase: CompactionPhase::StandaloneTurn,
|
phase: CompactionPhase::StandaloneTurn,
|
||||||
strategy: CompactionStrategy::Memento,
|
strategy: CompactionStrategy::Memento,
|
||||||
status: CompactionStatus::Completed,
|
status: CompactionStatus::Completed,
|
||||||
error: None,
|
codex_error_kind: None,
|
||||||
|
codex_error_http_status_code: None,
|
||||||
active_context_tokens_before: 131_000,
|
active_context_tokens_before: 131_000,
|
||||||
active_context_tokens_after: 64_000,
|
active_context_tokens_after: 64_000,
|
||||||
retained_image_count: None,
|
retained_image_count: None,
|
||||||
|
|||||||
@@ -762,7 +762,8 @@ pub(crate) struct CodexCompactionEventParams {
|
|||||||
pub(crate) phase: CompactionPhase,
|
pub(crate) phase: CompactionPhase,
|
||||||
pub(crate) strategy: CompactionStrategy,
|
pub(crate) strategy: CompactionStrategy,
|
||||||
pub(crate) status: CompactionStatus,
|
pub(crate) status: CompactionStatus,
|
||||||
pub(crate) error: Option<String>,
|
pub(crate) codex_error_kind: Option<CodexErrKind>,
|
||||||
|
pub(crate) codex_error_http_status_code: Option<u16>,
|
||||||
pub(crate) active_context_tokens_before: i64,
|
pub(crate) active_context_tokens_before: i64,
|
||||||
pub(crate) active_context_tokens_after: i64,
|
pub(crate) active_context_tokens_after: i64,
|
||||||
pub(crate) retained_image_count: Option<usize>,
|
pub(crate) retained_image_count: Option<usize>,
|
||||||
@@ -1000,7 +1001,8 @@ pub(crate) fn codex_compaction_event_params(
|
|||||||
phase: input.phase,
|
phase: input.phase,
|
||||||
strategy: input.strategy,
|
strategy: input.strategy,
|
||||||
status: input.status,
|
status: input.status,
|
||||||
error: input.error,
|
codex_error_kind: input.codex_error_kind,
|
||||||
|
codex_error_http_status_code: input.codex_error_http_status_code,
|
||||||
active_context_tokens_before: input.active_context_tokens_before,
|
active_context_tokens_before: input.active_context_tokens_before,
|
||||||
active_context_tokens_after: input.active_context_tokens_after,
|
active_context_tokens_after: input.active_context_tokens_after,
|
||||||
retained_image_count: input.retained_image_count,
|
retained_image_count: input.retained_image_count,
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ impl TurnCodexErrorFact {
|
|||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Serialize)]
|
#[derive(Clone, Copy, Debug, Serialize)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub(crate) enum CodexErrKind {
|
pub enum CodexErrKind {
|
||||||
TurnAborted,
|
TurnAborted,
|
||||||
Stream,
|
Stream,
|
||||||
ContextWindowExceeded,
|
ContextWindowExceeded,
|
||||||
@@ -408,7 +408,8 @@ pub struct CodexCompactionEvent {
|
|||||||
pub phase: CompactionPhase,
|
pub phase: CompactionPhase,
|
||||||
pub strategy: CompactionStrategy,
|
pub strategy: CompactionStrategy,
|
||||||
pub status: CompactionStatus,
|
pub status: CompactionStatus,
|
||||||
pub error: Option<String>,
|
pub codex_error_kind: Option<CodexErrKind>,
|
||||||
|
pub codex_error_http_status_code: Option<u16>,
|
||||||
pub active_context_tokens_before: i64,
|
pub active_context_tokens_before: i64,
|
||||||
pub active_context_tokens_after: i64,
|
pub active_context_tokens_after: i64,
|
||||||
pub retained_image_count: Option<usize>,
|
pub retained_image_count: Option<usize>,
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ pub use facts::AcceptedLineFingerprint;
|
|||||||
pub use facts::AnalyticsJsonRpcError;
|
pub use facts::AnalyticsJsonRpcError;
|
||||||
pub use facts::AppInvocation;
|
pub use facts::AppInvocation;
|
||||||
pub use facts::CodexCompactionEvent;
|
pub use facts::CodexCompactionEvent;
|
||||||
|
pub use facts::CodexErrKind;
|
||||||
pub use facts::CodexGoalEvent;
|
pub use facts::CodexGoalEvent;
|
||||||
pub use facts::CodexTurnSteerEvent;
|
pub use facts::CodexTurnSteerEvent;
|
||||||
pub use facts::CompactionImplementation;
|
pub use facts::CompactionImplementation;
|
||||||
|
|||||||
@@ -143,17 +143,17 @@ async fn run_compact_task_inner(
|
|||||||
let pre_compact_outcome = run_pre_compact_hooks(&sess, &turn_context, trigger).await;
|
let pre_compact_outcome = run_pre_compact_hooks(&sess, &turn_context, trigger).await;
|
||||||
match pre_compact_outcome {
|
match pre_compact_outcome {
|
||||||
PreCompactHookOutcome::Continue => {}
|
PreCompactHookOutcome::Continue => {}
|
||||||
PreCompactHookOutcome::Stopped { reason } => {
|
PreCompactHookOutcome::Stopped => {
|
||||||
let error = reason.unwrap_or_else(|| "PreCompact hook stopped execution".to_string());
|
let error = CodexErr::TurnAborted;
|
||||||
attempt
|
attempt
|
||||||
.track(
|
.track(
|
||||||
sess.as_ref(),
|
sess.as_ref(),
|
||||||
CompactionStatus::Interrupted,
|
CompactionStatus::Interrupted,
|
||||||
Some(error),
|
Some(&error),
|
||||||
CompactionAnalyticsDetails::default(),
|
CompactionAnalyticsDetails::default(),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
return Err(CodexErr::TurnAborted);
|
return Err(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let result = run_compact_task_inner_impl(
|
let result = run_compact_task_inner_impl(
|
||||||
@@ -165,7 +165,7 @@ async fn run_compact_task_inner(
|
|||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let status = compaction_status_from_result(&result);
|
let status = compaction_status_from_result(&result);
|
||||||
let error = result.as_ref().err().map(ToString::to_string);
|
let codex_error = result.as_ref().err();
|
||||||
if result.is_ok() {
|
if result.is_ok() {
|
||||||
let post_compact_outcome = run_post_compact_hooks(&sess, &turn_context, trigger).await;
|
let post_compact_outcome = run_post_compact_hooks(&sess, &turn_context, trigger).await;
|
||||||
if let PostCompactHookOutcome::Stopped = post_compact_outcome {
|
if let PostCompactHookOutcome::Stopped = post_compact_outcome {
|
||||||
@@ -173,7 +173,7 @@ async fn run_compact_task_inner(
|
|||||||
.track(
|
.track(
|
||||||
sess.as_ref(),
|
sess.as_ref(),
|
||||||
status,
|
status,
|
||||||
error,
|
codex_error,
|
||||||
CompactionAnalyticsDetails::default(),
|
CompactionAnalyticsDetails::default(),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
@@ -184,7 +184,7 @@ async fn run_compact_task_inner(
|
|||||||
.track(
|
.track(
|
||||||
sess.as_ref(),
|
sess.as_ref(),
|
||||||
status,
|
status,
|
||||||
error,
|
codex_error,
|
||||||
CompactionAnalyticsDetails::default(),
|
CompactionAnalyticsDetails::default(),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
@@ -373,7 +373,7 @@ impl CompactionAnalyticsAttempt {
|
|||||||
self,
|
self,
|
||||||
sess: &Session,
|
sess: &Session,
|
||||||
status: CompactionStatus,
|
status: CompactionStatus,
|
||||||
error: Option<String>,
|
codex_error: Option<&CodexErr>,
|
||||||
details: CompactionAnalyticsDetails,
|
details: CompactionAnalyticsDetails,
|
||||||
) {
|
) {
|
||||||
let CompactionAnalyticsDetails {
|
let CompactionAnalyticsDetails {
|
||||||
@@ -396,7 +396,9 @@ impl CompactionAnalyticsAttempt {
|
|||||||
phase: self.phase,
|
phase: self.phase,
|
||||||
strategy: CompactionStrategy::Memento,
|
strategy: CompactionStrategy::Memento,
|
||||||
status,
|
status,
|
||||||
error,
|
codex_error_kind: codex_error.map(Into::into),
|
||||||
|
codex_error_http_status_code: codex_error
|
||||||
|
.and_then(CodexErr::http_status_code_value),
|
||||||
active_context_tokens_before,
|
active_context_tokens_before,
|
||||||
active_context_tokens_after,
|
active_context_tokens_after,
|
||||||
retained_image_count,
|
retained_image_count,
|
||||||
|
|||||||
@@ -113,17 +113,17 @@ async fn run_remote_compact_task_inner(
|
|||||||
let pre_compact_outcome = run_pre_compact_hooks(sess, turn_context, trigger).await;
|
let pre_compact_outcome = run_pre_compact_hooks(sess, turn_context, trigger).await;
|
||||||
match pre_compact_outcome {
|
match pre_compact_outcome {
|
||||||
PreCompactHookOutcome::Continue => {}
|
PreCompactHookOutcome::Continue => {}
|
||||||
PreCompactHookOutcome::Stopped { reason } => {
|
PreCompactHookOutcome::Stopped => {
|
||||||
let error = reason.unwrap_or_else(|| "PreCompact hook stopped execution".to_string());
|
let error = CodexErr::TurnAborted;
|
||||||
attempt
|
attempt
|
||||||
.track(
|
.track(
|
||||||
sess.as_ref(),
|
sess.as_ref(),
|
||||||
codex_analytics::CompactionStatus::Interrupted,
|
codex_analytics::CompactionStatus::Interrupted,
|
||||||
Some(error),
|
Some(&error),
|
||||||
analytics_details,
|
analytics_details,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
return Err(CodexErr::TurnAborted);
|
return Err(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let result = run_remote_compact_task_inner_impl(
|
let result = run_remote_compact_task_inner_impl(
|
||||||
@@ -135,18 +135,18 @@ async fn run_remote_compact_task_inner(
|
|||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let status = compaction_status_from_result(&result);
|
let status = compaction_status_from_result(&result);
|
||||||
let error = result.as_ref().err().map(ToString::to_string);
|
let codex_error = result.as_ref().err();
|
||||||
if result.is_ok() {
|
if result.is_ok() {
|
||||||
let post_compact_outcome = run_post_compact_hooks(sess, turn_context, trigger).await;
|
let post_compact_outcome = run_post_compact_hooks(sess, turn_context, trigger).await;
|
||||||
if let PostCompactHookOutcome::Stopped = post_compact_outcome {
|
if let PostCompactHookOutcome::Stopped = post_compact_outcome {
|
||||||
attempt
|
attempt
|
||||||
.track(sess.as_ref(), status, error, analytics_details)
|
.track(sess.as_ref(), status, codex_error, analytics_details)
|
||||||
.await;
|
.await;
|
||||||
return Err(CodexErr::TurnAborted);
|
return Err(CodexErr::TurnAborted);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
attempt
|
attempt
|
||||||
.track(sess.as_ref(), status, error.clone(), analytics_details)
|
.track(sess.as_ref(), status, codex_error, analytics_details)
|
||||||
.await;
|
.await;
|
||||||
if let Err(err) = result {
|
if let Err(err) = result {
|
||||||
sess.track_turn_codex_error(turn_context, &err);
|
sess.track_turn_codex_error(turn_context, &err);
|
||||||
|
|||||||
@@ -127,17 +127,17 @@ async fn run_remote_compact_task_inner(
|
|||||||
let pre_compact_outcome = run_pre_compact_hooks(sess, turn_context, trigger).await;
|
let pre_compact_outcome = run_pre_compact_hooks(sess, turn_context, trigger).await;
|
||||||
match pre_compact_outcome {
|
match pre_compact_outcome {
|
||||||
PreCompactHookOutcome::Continue => {}
|
PreCompactHookOutcome::Continue => {}
|
||||||
PreCompactHookOutcome::Stopped { reason } => {
|
PreCompactHookOutcome::Stopped => {
|
||||||
let error = reason.unwrap_or_else(|| "PreCompact hook stopped execution".to_string());
|
let error = CodexErr::TurnAborted;
|
||||||
attempt
|
attempt
|
||||||
.track(
|
.track(
|
||||||
sess.as_ref(),
|
sess.as_ref(),
|
||||||
codex_analytics::CompactionStatus::Interrupted,
|
codex_analytics::CompactionStatus::Interrupted,
|
||||||
Some(error),
|
Some(&error),
|
||||||
analytics_details,
|
analytics_details,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
return Err(CodexErr::TurnAborted);
|
return Err(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let result = run_remote_compact_task_inner_impl(
|
let result = run_remote_compact_task_inner_impl(
|
||||||
@@ -150,18 +150,18 @@ async fn run_remote_compact_task_inner(
|
|||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let status = compaction_status_from_result(&result);
|
let status = compaction_status_from_result(&result);
|
||||||
let error = result.as_ref().err().map(ToString::to_string);
|
let codex_error = result.as_ref().err();
|
||||||
if result.is_ok() {
|
if result.is_ok() {
|
||||||
let post_compact_outcome = run_post_compact_hooks(sess, turn_context, trigger).await;
|
let post_compact_outcome = run_post_compact_hooks(sess, turn_context, trigger).await;
|
||||||
if let PostCompactHookOutcome::Stopped = post_compact_outcome {
|
if let PostCompactHookOutcome::Stopped = post_compact_outcome {
|
||||||
attempt
|
attempt
|
||||||
.track(sess.as_ref(), status, error, analytics_details)
|
.track(sess.as_ref(), status, codex_error, analytics_details)
|
||||||
.await;
|
.await;
|
||||||
return Err(CodexErr::TurnAborted);
|
return Err(CodexErr::TurnAborted);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
attempt
|
attempt
|
||||||
.track(sess.as_ref(), status, error.clone(), analytics_details)
|
.track(sess.as_ref(), status, codex_error, analytics_details)
|
||||||
.await;
|
.await;
|
||||||
if let Err(err) = result {
|
if let Err(err) = result {
|
||||||
sess.track_turn_codex_error(turn_context, &err);
|
sess.track_turn_codex_error(turn_context, &err);
|
||||||
|
|||||||
@@ -385,9 +385,7 @@ pub(crate) async fn run_pre_compact_hooks(
|
|||||||
let outcome = sess.hooks().run_pre_compact(request).await;
|
let outcome = sess.hooks().run_pre_compact(request).await;
|
||||||
emit_hook_completed_events(sess, turn_context, outcome.hook_events).await;
|
emit_hook_completed_events(sess, turn_context, outcome.hook_events).await;
|
||||||
if outcome.should_stop {
|
if outcome.should_stop {
|
||||||
PreCompactHookOutcome::Stopped {
|
PreCompactHookOutcome::Stopped
|
||||||
reason: outcome.stop_reason,
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
PreCompactHookOutcome::Continue
|
PreCompactHookOutcome::Continue
|
||||||
}
|
}
|
||||||
@@ -395,7 +393,7 @@ pub(crate) async fn run_pre_compact_hooks(
|
|||||||
|
|
||||||
pub(crate) enum PreCompactHookOutcome {
|
pub(crate) enum PreCompactHookOutcome {
|
||||||
Continue,
|
Continue,
|
||||||
Stopped { reason: Option<String> },
|
Stopped,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) enum PostCompactHookOutcome {
|
pub(crate) enum PostCompactHookOutcome {
|
||||||
|
|||||||
Reference in new issue
Block a user