core: emit responses api call analytics

This commit is contained in:
rhan-oai
2026-04-21 18:49:28 -07:00
parent 460c00c603
commit 28611de7b4
5 changed files with 211 additions and 18 deletions
+18 -12
View File
@@ -10,7 +10,6 @@ use crate::facts::AppMentionedInput;
use crate::facts::AppUsedInput;
use crate::facts::CodexResponsesApiCallFact;
use crate::facts::CodexResponsesApiCallInput;
use crate::facts::CodexResponsesApiItemPhase;
use crate::facts::CustomAnalyticsFact;
use crate::facts::HookRunFact;
use crate::facts::HookRunInput;
@@ -23,7 +22,6 @@ use crate::facts::TrackEventsContext;
use crate::facts::TurnResolvedConfigFact;
use crate::facts::TurnTokenUsageFact;
use crate::reducer::AnalyticsReducer;
use crate::response_items::response_items_metadata;
use codex_app_server_protocol::ClientRequest;
use codex_app_server_protocol::ClientResponse;
use codex_app_server_protocol::InitializeParams;
@@ -42,6 +40,7 @@ use tokio::sync::mpsc;
const ANALYTICS_EVENTS_QUEUE_SIZE: usize = 256;
const ANALYTICS_EVENTS_TIMEOUT: Duration = Duration::from_secs(10);
const ANALYTICS_EVENT_DEDUPE_MAX_KEYS: usize = 4096;
const RESPONSES_API_ERROR_MAX_BYTES: usize = 1024;
#[derive(Clone)]
pub(crate) struct AnalyticsEventsQueue {
@@ -223,25 +222,20 @@ impl AnalyticsEventsClient {
tracking: TrackEventsContext,
input: CodexResponsesApiCallInput,
) {
let mut items =
response_items_metadata(CodexResponsesApiItemPhase::Input, &input.input_items);
items.extend(response_items_metadata(
CodexResponsesApiItemPhase::Output,
&input.output_items,
));
let token_usage = input.token_usage;
let error = input.error.map(truncate_responses_api_error);
let event = CodexResponsesApiCallFact {
thread_id: tracking.thread_id,
turn_id: tracking.turn_id,
responses_id: input.responses_id,
turn_responses_call_index: input.turn_responses_call_index,
status: input.status,
error: input.error,
error,
started_at: input.started_at,
completed_at: input.completed_at,
duration_ms: input.duration_ms,
input_item_count: input.input_items.len(),
output_item_count: input.output_items.len(),
input_item_count: input.input_item_count,
output_item_count: input.output_item_count,
input_tokens: token_usage.as_ref().map(|usage| usage.input_tokens),
cached_input_tokens: token_usage.as_ref().map(|usage| usage.cached_input_tokens),
output_tokens: token_usage.as_ref().map(|usage| usage.output_tokens),
@@ -249,7 +243,7 @@ impl AnalyticsEventsClient {
.as_ref()
.map(|usage| usage.reasoning_output_tokens),
total_tokens: token_usage.as_ref().map(|usage| usage.total_tokens),
items,
items: input.items,
};
self.record_fact(AnalyticsFact::Custom(
@@ -339,6 +333,18 @@ impl AnalyticsEventsClient {
}
}
fn truncate_responses_api_error(mut error: String) -> String {
if error.len() <= RESPONSES_API_ERROR_MAX_BYTES {
return error;
}
let mut truncate_at = RESPONSES_API_ERROR_MAX_BYTES;
while !error.is_char_boundary(truncate_at) {
truncate_at -= 1;
}
error.truncate(truncate_at);
error
}
async fn send_track_events(
auth_manager: &Arc<AuthManager>,
base_url: &str,
+3 -3
View File
@@ -14,7 +14,6 @@ use codex_protocol::config_types::Personality;
use codex_protocol::config_types::ReasoningSummary;
use codex_protocol::config_types::ServiceTier;
use codex_protocol::models::MessagePhase;
use codex_protocol::models::ResponseItem;
use codex_protocol::openai_models::ReasoningEffort;
use codex_protocol::protocol::AskForApproval;
use codex_protocol::protocol::HookEventName;
@@ -322,8 +321,9 @@ pub struct CodexResponsesApiCallInput {
pub started_at: u64,
pub completed_at: Option<u64>,
pub duration_ms: Option<u64>,
pub input_items: Vec<ResponseItem>,
pub output_items: Vec<ResponseItem>,
pub input_item_count: usize,
pub output_item_count: usize,
pub items: Vec<CodexResponsesApiItemMetadata>,
pub token_usage: Option<TokenUsage>,
}
+19
View File
@@ -4,6 +4,7 @@ mod facts;
mod reducer;
mod response_items;
use codex_protocol::models::ResponseItem;
use serde::Serialize;
use std::time::SystemTime;
use std::time::UNIX_EPOCH;
@@ -22,6 +23,7 @@ pub use facts::AppInvocation;
pub use facts::CodexCompactionEvent;
pub use facts::CodexResponsesApiCallInput;
pub use facts::CodexResponsesApiCallStatus;
pub use facts::CodexResponsesApiItemMetadata;
pub use facts::CodexTurnSteerEvent;
pub use facts::CompactionImplementation;
pub use facts::CompactionPhase;
@@ -54,6 +56,23 @@ pub fn now_unix_seconds() -> u64 {
.as_secs()
}
pub fn responses_api_input_items_metadata(
items: &[ResponseItem],
) -> Vec<CodexResponsesApiItemMetadata> {
response_items::response_items_metadata(facts::CodexResponsesApiItemPhase::Input, items)
}
pub fn responses_api_output_item_metadata(
item_index: usize,
item: &ResponseItem,
) -> CodexResponsesApiItemMetadata {
response_items::response_item_metadata(
facts::CodexResponsesApiItemPhase::Output,
item_index,
item,
)
}
pub(crate) fn serialized_string<T: Serialize>(value: &T) -> Option<String> {
match serde_json::to_value(value).ok()? {
serde_json::Value::String(value) => Some(value),
+2 -2
View File
@@ -13,7 +13,7 @@ use codex_protocol::models::ReasoningItemContent;
use codex_protocol::models::ReasoningItemReasoningSummary;
use codex_protocol::models::ResponseItem;
pub fn response_items_metadata(
pub(crate) fn response_items_metadata(
phase: CodexResponsesApiItemPhase,
items: &[ResponseItem],
) -> Vec<CodexResponsesApiItemMetadata> {
@@ -24,7 +24,7 @@ pub fn response_items_metadata(
.collect()
}
fn response_item_metadata(
pub(crate) fn response_item_metadata(
item_phase: CodexResponsesApiItemPhase,
item_index: usize,
item: &ResponseItem,