mirror of
https://github.com/openai/codex.git
synced 2026-10-28 18:58:46 +00:00
updates
This commit is contained in:
@@ -8,6 +8,7 @@ use codex_protocol::protocol::SkillScope;
|
||||
use serde::Serialize;
|
||||
use sha1::Digest;
|
||||
use sha1::Sha1;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::HashSet;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
@@ -253,17 +254,26 @@ enum TrackEventsJob {
|
||||
PluginDisabled(TrackPluginManagementJob),
|
||||
}
|
||||
|
||||
const SKILL_INVOCATIONS_JOB_TYPE: &str = "skill_invocations";
|
||||
const APP_MENTIONED_JOB_TYPE: &str = "app_mentioned";
|
||||
const APP_USED_JOB_TYPE: &str = "app_used";
|
||||
const PLUGIN_USED_JOB_TYPE: &str = "plugin_used";
|
||||
const PLUGIN_INSTALLED_JOB_TYPE: &str = "plugin_installed";
|
||||
const PLUGIN_UNINSTALLED_JOB_TYPE: &str = "plugin_uninstalled";
|
||||
const PLUGIN_ENABLED_JOB_TYPE: &str = "plugin_enabled";
|
||||
const PLUGIN_DISABLED_JOB_TYPE: &str = "plugin_disabled";
|
||||
|
||||
impl TrackEventsJob {
|
||||
fn job_type(&self) -> &'static str {
|
||||
match self {
|
||||
Self::SkillInvocations(_) => "skill_invocations",
|
||||
Self::AppMentioned(_) => "app_mentioned",
|
||||
Self::AppUsed(_) => "app_used",
|
||||
Self::PluginUsed(_) => "plugin_used",
|
||||
Self::PluginInstalled(_) => "plugin_installed",
|
||||
Self::PluginUninstalled(_) => "plugin_uninstalled",
|
||||
Self::PluginEnabled(_) => "plugin_enabled",
|
||||
Self::PluginDisabled(_) => "plugin_disabled",
|
||||
Self::SkillInvocations(_) => SKILL_INVOCATIONS_JOB_TYPE,
|
||||
Self::AppMentioned(_) => APP_MENTIONED_JOB_TYPE,
|
||||
Self::AppUsed(_) => APP_USED_JOB_TYPE,
|
||||
Self::PluginUsed(_) => PLUGIN_USED_JOB_TYPE,
|
||||
Self::PluginInstalled(_) => PLUGIN_INSTALLED_JOB_TYPE,
|
||||
Self::PluginUninstalled(_) => PLUGIN_UNINSTALLED_JOB_TYPE,
|
||||
Self::PluginEnabled(_) => PLUGIN_ENABLED_JOB_TYPE,
|
||||
Self::PluginDisabled(_) => PLUGIN_DISABLED_JOB_TYPE,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,28 +291,37 @@ impl TrackEventsJob {
|
||||
}
|
||||
}
|
||||
|
||||
fn job_type_for_events(events: &[TrackEventRequest]) -> &'static str {
|
||||
match events.first() {
|
||||
Some(TrackEventRequest::SkillInvocation(_)) => "skill_invocations",
|
||||
Some(TrackEventRequest::AppMentioned(_)) => "app_mentioned",
|
||||
Some(TrackEventRequest::AppUsed(_)) => "app_used",
|
||||
Some(TrackEventRequest::PluginUsed(_)) => "plugin_used",
|
||||
Some(TrackEventRequest::PluginInstalled(_)) => "plugin_installed",
|
||||
Some(TrackEventRequest::PluginUninstalled(_)) => "plugin_uninstalled",
|
||||
Some(TrackEventRequest::PluginEnabled(_)) => "plugin_enabled",
|
||||
Some(TrackEventRequest::PluginDisabled(_)) => "plugin_disabled",
|
||||
None => unreachable!("events should be non-empty"),
|
||||
fn job_type_for_event(event: &TrackEventRequest) -> &'static str {
|
||||
match event {
|
||||
TrackEventRequest::SkillInvocation(_) => SKILL_INVOCATIONS_JOB_TYPE,
|
||||
TrackEventRequest::AppMentioned(_) => APP_MENTIONED_JOB_TYPE,
|
||||
TrackEventRequest::AppUsed(_) => APP_USED_JOB_TYPE,
|
||||
TrackEventRequest::PluginUsed(_) => PLUGIN_USED_JOB_TYPE,
|
||||
TrackEventRequest::PluginInstalled(_) => PLUGIN_INSTALLED_JOB_TYPE,
|
||||
TrackEventRequest::PluginUninstalled(_) => PLUGIN_UNINSTALLED_JOB_TYPE,
|
||||
TrackEventRequest::PluginEnabled(_) => PLUGIN_ENABLED_JOB_TYPE,
|
||||
TrackEventRequest::PluginDisabled(_) => PLUGIN_DISABLED_JOB_TYPE,
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_analytics_events_failure_counters<'a>(
|
||||
fn event_counts_by_job_type(events: &[TrackEventRequest]) -> BTreeMap<&'static str, usize> {
|
||||
let mut counts = BTreeMap::new();
|
||||
for event in events {
|
||||
*counts.entry(job_type_for_event(event)).or_insert(0) += 1;
|
||||
}
|
||||
counts
|
||||
}
|
||||
|
||||
fn emit_analytics_events_failure_counters(
|
||||
reason: &'static str,
|
||||
job_type: &'static str,
|
||||
event_count: usize,
|
||||
extra_tags: &[(&'a str, &'a str)],
|
||||
extra_tags: &[(&str, &str)],
|
||||
) {
|
||||
if let Some(metrics) = codex_otel::metrics::global() {
|
||||
let mut tags = vec![("reason", reason), ("job_type", job_type)];
|
||||
let mut tags = Vec::with_capacity(2 + extra_tags.len());
|
||||
tags.push(("reason", reason));
|
||||
tags.push(("job_type", job_type));
|
||||
tags.extend(extra_tags.iter().copied());
|
||||
let _ = metrics.counter("codex.analytics_events.emit.failure", /*inc*/ 1, &tags);
|
||||
let _ = metrics.counter(
|
||||
@@ -313,6 +332,16 @@ fn emit_analytics_events_failure_counters<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_analytics_events_failure_counters_for_events(
|
||||
reason: &'static str,
|
||||
events: &[TrackEventRequest],
|
||||
extra_tags: &[(&str, &str)],
|
||||
) {
|
||||
for (job_type, event_count) in event_counts_by_job_type(events) {
|
||||
emit_analytics_events_failure_counters(reason, job_type, event_count, extra_tags);
|
||||
}
|
||||
}
|
||||
|
||||
struct TrackSkillInvocationsJob {
|
||||
config: Arc<Config>,
|
||||
tracking: TrackEventsContext,
|
||||
@@ -746,25 +775,23 @@ async fn send_track_events(
|
||||
if events.is_empty() {
|
||||
return;
|
||||
}
|
||||
let event_count = events.len();
|
||||
let job_type = job_type_for_events(&events);
|
||||
let Some(auth) = auth_manager.auth().await else {
|
||||
emit_analytics_events_failure_counters("auth_missing", job_type, event_count, &[]);
|
||||
emit_analytics_events_failure_counters_for_events("auth_missing", &events, &[]);
|
||||
return;
|
||||
};
|
||||
if !auth.is_chatgpt_auth() {
|
||||
emit_analytics_events_failure_counters("non_chatgpt_auth", job_type, event_count, &[]);
|
||||
emit_analytics_events_failure_counters_for_events("non_chatgpt_auth", &events, &[]);
|
||||
return;
|
||||
}
|
||||
let access_token = match auth.get_token() {
|
||||
Ok(token) => token,
|
||||
Err(_) => {
|
||||
emit_analytics_events_failure_counters("token_error", job_type, event_count, &[]);
|
||||
emit_analytics_events_failure_counters_for_events("token_error", &events, &[]);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let Some(account_id) = auth.get_account_id() else {
|
||||
emit_analytics_events_failure_counters("account_id_missing", job_type, event_count, &[]);
|
||||
emit_analytics_events_failure_counters_for_events("account_id_missing", &events, &[]);
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -786,17 +813,20 @@ async fn send_track_events(
|
||||
Ok(response) if response.status().is_success() => {}
|
||||
Ok(response) => {
|
||||
let status = response.status();
|
||||
emit_analytics_events_failure_counters(
|
||||
emit_analytics_events_failure_counters_for_events(
|
||||
"http_status",
|
||||
job_type,
|
||||
event_count,
|
||||
&payload.events,
|
||||
&[("status_code", status.as_str())],
|
||||
);
|
||||
let body = response.text().await.unwrap_or_default();
|
||||
tracing::warn!("events failed with status {status}: {body}");
|
||||
}
|
||||
Err(err) => {
|
||||
emit_analytics_events_failure_counters("request_error", job_type, event_count, &[]);
|
||||
emit_analytics_events_failure_counters_for_events(
|
||||
"request_error",
|
||||
&payload.events,
|
||||
&[],
|
||||
);
|
||||
tracing::warn!("failed to send events request: {err}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ use crate::plugins::PluginId;
|
||||
use crate::plugins::PluginTelemetryMetadata;
|
||||
use pretty_assertions::assert_eq;
|
||||
use serde_json::json;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::HashSet;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
@@ -419,6 +420,55 @@ fn track_events_job_event_count_matches_underlying_payloads() {
|
||||
assert_eq!(plugin_job.event_count(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn event_counts_by_job_type_groups_mixed_events() {
|
||||
let tracking = TrackEventsContext {
|
||||
model_slug: "gpt-5".to_string(),
|
||||
thread_id: "thread-1".to_string(),
|
||||
turn_id: "turn-1".to_string(),
|
||||
};
|
||||
let events = vec![
|
||||
TrackEventRequest::AppMentioned(CodexAppMentionedEventRequest {
|
||||
event_type: "codex_app_mentioned",
|
||||
event_params: codex_app_metadata(
|
||||
&tracking,
|
||||
AppInvocation {
|
||||
connector_id: Some("drive".to_string()),
|
||||
app_name: Some("Drive".to_string()),
|
||||
invocation_type: Some(InvocationType::Explicit),
|
||||
},
|
||||
),
|
||||
}),
|
||||
TrackEventRequest::AppUsed(CodexAppUsedEventRequest {
|
||||
event_type: "codex_app_used",
|
||||
event_params: codex_app_metadata(
|
||||
&tracking,
|
||||
AppInvocation {
|
||||
connector_id: Some("calendar".to_string()),
|
||||
app_name: Some("Calendar".to_string()),
|
||||
invocation_type: Some(InvocationType::Implicit),
|
||||
},
|
||||
),
|
||||
}),
|
||||
TrackEventRequest::AppMentioned(CodexAppMentionedEventRequest {
|
||||
event_type: "codex_app_mentioned",
|
||||
event_params: codex_app_metadata(
|
||||
&tracking,
|
||||
AppInvocation {
|
||||
connector_id: Some("gmail".to_string()),
|
||||
app_name: Some("Gmail".to_string()),
|
||||
invocation_type: None,
|
||||
},
|
||||
),
|
||||
}),
|
||||
];
|
||||
|
||||
assert_eq!(
|
||||
super::event_counts_by_job_type(&events),
|
||||
BTreeMap::from([("app_mentioned", 2), ("app_used", 1)])
|
||||
);
|
||||
}
|
||||
|
||||
fn sample_plugin_metadata() -> PluginTelemetryMetadata {
|
||||
PluginTelemetryMetadata {
|
||||
plugin_id: PluginId::parse("sample@test").expect("valid plugin id"),
|
||||
|
||||
Reference in New Issue
Block a user