mirror of
https://github.com/openai/codex.git
synced 2026-10-25 18:28:38 +00:00
[codex-analytics] feature plumbing and emittance
This commit is contained in:
@@ -55,6 +55,7 @@ use codex_analytics::CompactionPhase;
|
||||
use codex_analytics::CompactionReason;
|
||||
use codex_analytics::InvocationType;
|
||||
use codex_analytics::SubAgentThreadStartedInput;
|
||||
use codex_analytics::TurnResolvedConfigFact;
|
||||
use codex_analytics::build_track_events_context;
|
||||
use codex_app_server_protocol::AuthMode;
|
||||
use codex_app_server_protocol::McpServerElicitationRequest;
|
||||
@@ -188,6 +189,7 @@ use crate::config::resolve_web_search_mode_for_turn;
|
||||
use crate::context_manager::ContextManager;
|
||||
use crate::context_manager::TotalTokenUsageBreakdown;
|
||||
use crate::environment_context::EnvironmentContext;
|
||||
use crate::thread_rollout_truncation::initial_history_has_prior_user_turns;
|
||||
use codex_config::CONFIG_TOML_FILE;
|
||||
use codex_config::types::McpServerConfig;
|
||||
use codex_config::types::ShellEnvironmentPolicy;
|
||||
@@ -2339,6 +2341,11 @@ impl Session {
|
||||
SessionSource::SubAgent(_)
|
||||
)
|
||||
};
|
||||
let has_prior_user_turns = initial_history_has_prior_user_turns(&conversation_history);
|
||||
{
|
||||
let mut state = self.state.lock().await;
|
||||
state.set_next_turn_is_first(!has_prior_user_turns);
|
||||
}
|
||||
match conversation_history {
|
||||
InitialHistory::New | InitialHistory::Cleared => {
|
||||
// Defer initial context insertion until the first real turn starts so
|
||||
@@ -6187,6 +6194,8 @@ pub(crate) async fn run_turn(
|
||||
.await;
|
||||
}
|
||||
|
||||
track_turn_resolved_config_analytics(&sess, &turn_context, &input).await;
|
||||
|
||||
let skills_outcome = Some(turn_context.turn_skills.outcome.as_ref());
|
||||
sess.maybe_start_ghost_snapshot(Arc::clone(&turn_context), cancellation_token.child_token())
|
||||
.await;
|
||||
@@ -6490,6 +6499,52 @@ pub(crate) async fn run_turn(
|
||||
last_agent_message
|
||||
}
|
||||
|
||||
async fn track_turn_resolved_config_analytics(
|
||||
sess: &Session,
|
||||
turn_context: &TurnContext,
|
||||
input: &[UserInput],
|
||||
) {
|
||||
if !sess.enabled(Feature::GeneralAnalytics) {
|
||||
return;
|
||||
}
|
||||
|
||||
let thread_config = {
|
||||
let state = sess.state.lock().await;
|
||||
state.session_configuration.thread_config_snapshot()
|
||||
};
|
||||
let is_first_turn = {
|
||||
let mut state = sess.state.lock().await;
|
||||
state.take_next_turn_is_first()
|
||||
};
|
||||
sess.services
|
||||
.analytics_events_client
|
||||
.track_turn_resolved_config(TurnResolvedConfigFact {
|
||||
turn_id: turn_context.sub_id.clone(),
|
||||
thread_id: sess.conversation_id.to_string(),
|
||||
num_input_images: input
|
||||
.iter()
|
||||
.filter(|item| {
|
||||
matches!(item, UserInput::Image { .. } | UserInput::LocalImage { .. })
|
||||
})
|
||||
.count(),
|
||||
submission_type: None,
|
||||
ephemeral: thread_config.ephemeral,
|
||||
session_source: thread_config.session_source,
|
||||
model: turn_context.model_info.slug.clone(),
|
||||
model_provider: turn_context.config.model_provider_id.clone(),
|
||||
sandbox_policy: turn_context.sandbox_policy.get().clone(),
|
||||
reasoning_effort: turn_context.reasoning_effort,
|
||||
reasoning_summary: Some(turn_context.reasoning_summary),
|
||||
service_tier: turn_context.config.service_tier,
|
||||
approval_policy: turn_context.approval_policy.value(),
|
||||
approvals_reviewer: turn_context.config.approvals_reviewer,
|
||||
sandbox_network_access: turn_context.network_sandbox_policy.is_enabled(),
|
||||
collaboration_mode: turn_context.collaboration_mode.mode,
|
||||
personality: turn_context.personality,
|
||||
is_first_turn,
|
||||
});
|
||||
}
|
||||
|
||||
async fn run_pre_sampling_compact(
|
||||
sess: &Arc<Session>,
|
||||
turn_context: &Arc<TurnContext>,
|
||||
|
||||
@@ -78,7 +78,6 @@ pub(crate) async fn run_codex_thread_interactive(
|
||||
let CodexSpawnOk { codex, .. } = Codex::spawn(CodexSpawnArgs {
|
||||
config,
|
||||
auth_manager,
|
||||
analytics_events_client: Some(parent_session.services.analytics_events_client.clone()),
|
||||
models_manager,
|
||||
environment_manager: Arc::new(EnvironmentManager::from_environment(
|
||||
parent_ctx.environment.as_deref(),
|
||||
@@ -97,6 +96,7 @@ pub(crate) async fn run_codex_thread_interactive(
|
||||
user_shell_override: None,
|
||||
inherited_exec_policy: Some(Arc::clone(&parent_session.services.exec_policy)),
|
||||
parent_trace: None,
|
||||
analytics_events_client: Some(parent_session.services.analytics_events_client.clone()),
|
||||
})
|
||||
.await?;
|
||||
if parent_session.enabled(codex_features::Feature::GeneralAnalytics) {
|
||||
|
||||
@@ -436,7 +436,6 @@ async fn guardian_subagent_does_not_inherit_parent_exec_policy_rules() {
|
||||
let CodexSpawnOk { codex, .. } = Codex::spawn(CodexSpawnArgs {
|
||||
config,
|
||||
auth_manager,
|
||||
analytics_events_client: None,
|
||||
models_manager,
|
||||
environment_manager: Arc::new(EnvironmentManager::new(/*exec_server_url*/ None)),
|
||||
skills_manager,
|
||||
@@ -455,6 +454,7 @@ async fn guardian_subagent_does_not_inherit_parent_exec_policy_rules() {
|
||||
inherited_exec_policy: Some(Arc::new(parent_exec_policy)),
|
||||
user_shell_override: None,
|
||||
parent_trace: None,
|
||||
analytics_events_client: None,
|
||||
})
|
||||
.await
|
||||
.expect("spawn guardian subagent");
|
||||
|
||||
@@ -33,6 +33,7 @@ pub(crate) struct SessionState {
|
||||
pub(crate) active_connector_selection: HashSet<String>,
|
||||
pub(crate) pending_session_start_source: Option<codex_hooks::SessionStartSource>,
|
||||
granted_permissions: Option<PermissionProfile>,
|
||||
next_turn_is_first: bool,
|
||||
}
|
||||
|
||||
impl SessionState {
|
||||
@@ -51,6 +52,7 @@ impl SessionState {
|
||||
active_connector_selection: HashSet::new(),
|
||||
pending_session_start_source: None,
|
||||
granted_permissions: None,
|
||||
next_turn_is_first: true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +75,16 @@ impl SessionState {
|
||||
self.previous_turn_settings = previous_turn_settings;
|
||||
}
|
||||
|
||||
pub(crate) fn set_next_turn_is_first(&mut self, value: bool) {
|
||||
self.next_turn_is_first = value;
|
||||
}
|
||||
|
||||
pub(crate) fn take_next_turn_is_first(&mut self) -> bool {
|
||||
let is_first_turn = self.next_turn_is_first;
|
||||
self.next_turn_is_first = false;
|
||||
is_first_turn
|
||||
}
|
||||
|
||||
pub(crate) fn clone_history(&self) -> ContextManager {
|
||||
self.history.clone()
|
||||
}
|
||||
|
||||
@@ -8,9 +8,21 @@ use crate::event_mapping;
|
||||
use codex_protocol::items::TurnItem;
|
||||
use codex_protocol::models::ResponseItem;
|
||||
use codex_protocol::protocol::EventMsg;
|
||||
use codex_protocol::protocol::InitialHistory;
|
||||
use codex_protocol::protocol::InterAgentCommunication;
|
||||
use codex_protocol::protocol::RolloutItem;
|
||||
|
||||
pub(crate) fn initial_history_has_prior_user_turns(conversation_history: &InitialHistory) -> bool {
|
||||
conversation_history.scan_rollout_items(rollout_item_is_user_turn_boundary)
|
||||
}
|
||||
|
||||
fn rollout_item_is_user_turn_boundary(item: &RolloutItem) -> bool {
|
||||
match item {
|
||||
RolloutItem::ResponseItem(item) => is_user_turn_boundary(item),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the indices of user message boundaries in a rollout.
|
||||
///
|
||||
/// A user message boundary is a `RolloutItem::ResponseItem(ResponseItem::Message { .. })`
|
||||
|
||||
Reference in New Issue
Block a user