mirror of
https://github.com/openai/codex.git
synced 2026-10-27 18:48:37 +00:00
Track app-server start in ChatGPT telemetry
This commit is contained in:
@@ -4,6 +4,8 @@ use crate::events::CodexAcceptedLineFingerprintsEventParams;
|
||||
use crate::events::CodexAcceptedLineFingerprintsEventRequest;
|
||||
use crate::events::CodexAppMentionedEventRequest;
|
||||
use crate::events::CodexAppServerClientMetadata;
|
||||
use crate::events::CodexAppServerStartedEventParams;
|
||||
use crate::events::CodexAppServerStartedEventRequest;
|
||||
use crate::events::CodexAppUsedEventRequest;
|
||||
use crate::events::CodexCommandExecutionEventParams;
|
||||
use crate::events::CodexCommandExecutionEventRequest;
|
||||
@@ -41,6 +43,7 @@ use crate::facts::AnalyticsFact;
|
||||
use crate::facts::AnalyticsJsonRpcError;
|
||||
use crate::facts::AppInvocation;
|
||||
use crate::facts::AppMentionedInput;
|
||||
use crate::facts::AppServerStartedInput;
|
||||
use crate::facts::AppUsedInput;
|
||||
use crate::facts::CodexCompactionEvent;
|
||||
use crate::facts::CompactionImplementation;
|
||||
@@ -1362,6 +1365,39 @@ fn thread_initialized_event_serializes_expected_shape() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn app_server_started_event_serializes_expected_shape() {
|
||||
let event = TrackEventRequest::AppServerStarted(CodexAppServerStartedEventRequest {
|
||||
event_type: "codex_app_server_started",
|
||||
event_params: CodexAppServerStartedEventParams {
|
||||
runtime: sample_runtime_metadata(),
|
||||
remote_control_enabled: true,
|
||||
startup_duration_ms: 987,
|
||||
completed_at: 12,
|
||||
},
|
||||
});
|
||||
|
||||
let payload = serde_json::to_value(&event).expect("serialize app-server started event");
|
||||
|
||||
assert_eq!(
|
||||
payload,
|
||||
json!({
|
||||
"event_type": "codex_app_server_started",
|
||||
"event_params": {
|
||||
"runtime": {
|
||||
"codex_rs_version": "0.1.0",
|
||||
"runtime_os": "macos",
|
||||
"runtime_os_version": "15.3.1",
|
||||
"runtime_arch": "aarch64"
|
||||
},
|
||||
"remote_control_enabled": true,
|
||||
"startup_duration_ms": 987,
|
||||
"completed_at": 12
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn command_execution_event_serializes_expected_shape() {
|
||||
let event = TrackEventRequest::CommandExecution(CodexCommandExecutionEventRequest {
|
||||
@@ -1639,6 +1675,45 @@ async fn initialize_caches_client_and_thread_lifecycle_publishes_once_initialize
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn app_server_started_fact_emits_event() {
|
||||
let mut reducer = AnalyticsReducer::default();
|
||||
let mut events = Vec::new();
|
||||
|
||||
reducer
|
||||
.ingest(
|
||||
AnalyticsFact::Custom(CustomAnalyticsFact::AppServerStarted(
|
||||
AppServerStartedInput {
|
||||
runtime: sample_runtime_metadata(),
|
||||
remote_control_enabled: true,
|
||||
startup_duration_ms: 456,
|
||||
completed_at: 12,
|
||||
},
|
||||
)),
|
||||
&mut events,
|
||||
)
|
||||
.await;
|
||||
|
||||
let payload = serde_json::to_value(&events).expect("serialize events");
|
||||
assert_eq!(
|
||||
payload,
|
||||
json!([{
|
||||
"event_type": "codex_app_server_started",
|
||||
"event_params": {
|
||||
"runtime": {
|
||||
"codex_rs_version": "0.1.0",
|
||||
"runtime_os": "macos",
|
||||
"runtime_os_version": "15.3.1",
|
||||
"runtime_arch": "aarch64"
|
||||
},
|
||||
"remote_control_enabled": true,
|
||||
"startup_duration_ms": 456,
|
||||
"completed_at": 12
|
||||
}
|
||||
}])
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn unrelated_client_requests_are_ignored_by_reducer() {
|
||||
let mut reducer = AnalyticsReducer::default();
|
||||
|
||||
@@ -8,6 +8,7 @@ use crate::facts::AnalyticsFact;
|
||||
use crate::facts::AnalyticsJsonRpcError;
|
||||
use crate::facts::AppInvocation;
|
||||
use crate::facts::AppMentionedInput;
|
||||
use crate::facts::AppServerStartedInput;
|
||||
use crate::facts::AppUsedInput;
|
||||
use crate::facts::CustomAnalyticsFact;
|
||||
use crate::facts::HookRunFact;
|
||||
@@ -20,6 +21,7 @@ use crate::facts::SubAgentThreadStartedInput;
|
||||
use crate::facts::TrackEventsContext;
|
||||
use crate::facts::TurnResolvedConfigFact;
|
||||
use crate::facts::TurnTokenUsageFact;
|
||||
use crate::now_unix_seconds;
|
||||
use crate::reducer::AnalyticsReducer;
|
||||
use codex_app_server_protocol::ClientRequest;
|
||||
use codex_app_server_protocol::ClientResponsePayload;
|
||||
@@ -38,12 +40,35 @@ use std::collections::HashSet;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use std::time::Duration;
|
||||
use std::time::Instant;
|
||||
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;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct StartedTimer {
|
||||
started_at: Instant,
|
||||
}
|
||||
|
||||
impl StartedTimer {
|
||||
#[must_use]
|
||||
pub fn start() -> Self {
|
||||
Self {
|
||||
started_at: Instant::now(),
|
||||
}
|
||||
}
|
||||
|
||||
fn elapsed_ms(self) -> u64 {
|
||||
self.started_at
|
||||
.elapsed()
|
||||
.as_millis()
|
||||
.try_into()
|
||||
.unwrap_or(u64::MAX)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct AnalyticsEventsQueue {
|
||||
pub(crate) sender: mpsc::Sender<AnalyticsFact>,
|
||||
@@ -163,6 +188,17 @@ impl AnalyticsEventsClient {
|
||||
});
|
||||
}
|
||||
|
||||
pub fn track_app_server_started(&self, timer: StartedTimer, remote_control_enabled: bool) {
|
||||
self.record_fact(AnalyticsFact::Custom(
|
||||
CustomAnalyticsFact::AppServerStarted(AppServerStartedInput {
|
||||
runtime: current_runtime_metadata(),
|
||||
remote_control_enabled,
|
||||
startup_duration_ms: timer.elapsed_ms(),
|
||||
completed_at: now_unix_seconds(),
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
pub fn track_subagent_thread_started(&self, input: SubAgentThreadStartedInput) {
|
||||
self.record_fact(AnalyticsFact::Custom(
|
||||
CustomAnalyticsFact::SubAgentThreadStarted(input),
|
||||
|
||||
@@ -56,6 +56,7 @@ pub(crate) struct TrackEventsRequest {
|
||||
#[serde(untagged)]
|
||||
pub(crate) enum TrackEventRequest {
|
||||
SkillInvocation(SkillInvocationEventRequest),
|
||||
AppServerStarted(CodexAppServerStartedEventRequest),
|
||||
ThreadInitialized(ThreadInitializedEvent),
|
||||
GuardianReview(Box<GuardianReviewEventRequest>),
|
||||
AppMentioned(CodexAppMentionedEventRequest),
|
||||
@@ -144,6 +145,20 @@ pub(crate) struct CodexRuntimeMetadata {
|
||||
pub(crate) runtime_arch: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub(crate) struct CodexAppServerStartedEventParams {
|
||||
pub(crate) runtime: CodexRuntimeMetadata,
|
||||
pub(crate) remote_control_enabled: bool,
|
||||
pub(crate) startup_duration_ms: u64,
|
||||
pub(crate) completed_at: u64,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub(crate) struct CodexAppServerStartedEventRequest {
|
||||
pub(crate) event_type: &'static str,
|
||||
pub(crate) event_params: CodexAppServerStartedEventParams,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub(crate) struct ThreadInitializedEventParams {
|
||||
pub(crate) thread_id: String,
|
||||
|
||||
@@ -274,6 +274,13 @@ pub struct CodexCompactionEvent {
|
||||
pub duration_ms: Option<u64>,
|
||||
}
|
||||
|
||||
pub(crate) struct AppServerStartedInput {
|
||||
pub runtime: CodexRuntimeMetadata,
|
||||
pub remote_control_enabled: bool,
|
||||
pub startup_duration_ms: u64,
|
||||
pub completed_at: u64,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) enum AnalyticsFact {
|
||||
Initialize {
|
||||
@@ -323,6 +330,7 @@ pub(crate) enum AnalyticsFact {
|
||||
}
|
||||
|
||||
pub(crate) enum CustomAnalyticsFact {
|
||||
AppServerStarted(AppServerStartedInput),
|
||||
SubAgentThreadStarted(SubAgentThreadStartedInput),
|
||||
Compaction(Box<CodexCompactionEvent>),
|
||||
GuardianReview(Box<GuardianReviewEventParams>),
|
||||
|
||||
@@ -10,6 +10,7 @@ use std::time::UNIX_EPOCH;
|
||||
pub use accepted_lines::accepted_line_fingerprints_from_unified_diff;
|
||||
pub use accepted_lines::fingerprint_hash;
|
||||
pub use client::AnalyticsEventsClient;
|
||||
pub use client::StartedTimer;
|
||||
pub use events::AppServerRpcTransport;
|
||||
pub use events::GuardianApprovalRequestSource;
|
||||
pub use events::GuardianReviewAnalyticsResult;
|
||||
|
||||
@@ -5,6 +5,8 @@ use crate::accepted_lines::accepted_line_repo_hash_for_cwd;
|
||||
use crate::events::AppServerRpcTransport;
|
||||
use crate::events::CodexAppMentionedEventRequest;
|
||||
use crate::events::CodexAppServerClientMetadata;
|
||||
use crate::events::CodexAppServerStartedEventParams;
|
||||
use crate::events::CodexAppServerStartedEventRequest;
|
||||
use crate::events::CodexAppUsedEventRequest;
|
||||
use crate::events::CodexCollabAgentToolCallEventParams;
|
||||
use crate::events::CodexCollabAgentToolCallEventRequest;
|
||||
@@ -61,6 +63,7 @@ use crate::events::subagent_thread_started_event_request;
|
||||
use crate::facts::AnalyticsFact;
|
||||
use crate::facts::AnalyticsJsonRpcError;
|
||||
use crate::facts::AppMentionedInput;
|
||||
use crate::facts::AppServerStartedInput;
|
||||
use crate::facts::AppUsedInput;
|
||||
use crate::facts::CodexCompactionEvent;
|
||||
use crate::facts::CustomAnalyticsFact;
|
||||
@@ -446,6 +449,9 @@ impl AnalyticsReducer {
|
||||
self.ingest_server_request_aborted(completed_at_ms, request_id, out);
|
||||
}
|
||||
AnalyticsFact::Custom(input) => match input {
|
||||
CustomAnalyticsFact::AppServerStarted(input) => {
|
||||
self.ingest_app_server_started(input, out);
|
||||
}
|
||||
CustomAnalyticsFact::SubAgentThreadStarted(input) => {
|
||||
self.ingest_subagent_thread_started(input, out);
|
||||
}
|
||||
@@ -508,6 +514,24 @@ impl AnalyticsReducer {
|
||||
);
|
||||
}
|
||||
|
||||
fn ingest_app_server_started(
|
||||
&mut self,
|
||||
input: AppServerStartedInput,
|
||||
out: &mut Vec<TrackEventRequest>,
|
||||
) {
|
||||
out.push(TrackEventRequest::AppServerStarted(
|
||||
CodexAppServerStartedEventRequest {
|
||||
event_type: "codex_app_server_started",
|
||||
event_params: CodexAppServerStartedEventParams {
|
||||
runtime: input.runtime,
|
||||
remote_control_enabled: input.remote_control_enabled,
|
||||
startup_duration_ms: input.startup_duration_ms,
|
||||
completed_at: input.completed_at,
|
||||
},
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
fn ingest_subagent_thread_started(
|
||||
&mut self,
|
||||
input: SubAgentThreadStartedInput,
|
||||
|
||||
Reference in New Issue
Block a user