mirror of
https://github.com/openai/codex.git
synced 2026-11-08 08:37:30 +00:00
Honor thread analytics opt-outs when using shared clients (#44646)
## Why An enabled shared analytics client could override a thread's explicit opt-out. Delegated threads also emitted initialization events through the parent's client, bypassing the child's analytics setting. ## What changed - Use a disabled analytics client when `config.analytics_enabled` is `Some(false)`, without disabling analytics for sibling threads or overriding a disabled host client. - Emit delegated thread initialization events through the child's analytics client. - Expose the effective analytics state through `CodexThread::analytics_enabled()`. ## Testing Add regression tests for explicit and unset thread settings with enabled, disabled, and absent shared clients, plus delegated child opt-outs with an enabled parent. Update compaction and rollout-budget rollback tests to wait for thread idle after turn completion and fail immediately on rollback errors. GitOrigin-RevId: 547852909af1a6822b95c0bcd2336b0d6bc7aad1
This commit is contained in:
1 parent
e25bedc166
commit
60825b4988
8 files changed
+295
-19
No files matched your search
@@ -16,6 +16,7 @@ use codex_core::TurnInputRequest;
|
||||
use codex_core::compact::SUMMARIZATION_PROMPT;
|
||||
use codex_core::config::Config;
|
||||
use codex_core::spawn::CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR;
|
||||
use codex_extension_api::ExtensionRegistryBuilder;
|
||||
use codex_history::CodexHarnessMetadata;
|
||||
use codex_history::RolloutItem;
|
||||
use codex_protocol::config_types::CollaborationMode;
|
||||
@@ -29,6 +30,7 @@ use codex_protocol::protocol::Op;
|
||||
use codex_protocol::protocol::ThreadSettingsOverrides;
|
||||
use codex_protocol::protocol::WarningEvent;
|
||||
use codex_protocol::user_input::UserInput;
|
||||
use core_test_support::ThreadIdle;
|
||||
use core_test_support::context_snapshot;
|
||||
use core_test_support::context_snapshot::ContextSnapshotOptions;
|
||||
use core_test_support::context_snapshot::ContextSnapshotRenderMode;
|
||||
@@ -543,8 +545,13 @@ async fn snapshot_rollback_past_compaction_replays_append_only_history() -> Resu
|
||||
base.submit(Op::ThreadRollback { num_turns: 1 })
|
||||
.await
|
||||
.expect("submit thread rollback");
|
||||
let rollback_event =
|
||||
wait_for_event(&base, |ev| matches!(ev, EventMsg::ThreadRolledBack(_))).await;
|
||||
let rollback_event = wait_for_event(&base, |ev| {
|
||||
if let EventMsg::Error(error) = ev {
|
||||
panic!("rollback failed: {error:?}");
|
||||
}
|
||||
matches!(ev, EventMsg::ThreadRolledBack(_))
|
||||
})
|
||||
.await;
|
||||
let EventMsg::ThreadRolledBack(rollback_event) = rollback_event else {
|
||||
panic!("expected thread rolled back event");
|
||||
};
|
||||
@@ -658,6 +665,9 @@ async fn snapshot_rollback_followup_turn_trims_context_updates() -> Result<()> {
|
||||
.submit(Op::ThreadRollback { num_turns: 1 })
|
||||
.await?;
|
||||
let rollback_event = wait_for_event(&conversation, |ev| {
|
||||
if let EventMsg::Error(error) = ev {
|
||||
panic!("rollback failed: {error:?}");
|
||||
}
|
||||
matches!(ev, EventMsg::ThreadRolledBack(_))
|
||||
})
|
||||
.await;
|
||||
@@ -844,15 +854,19 @@ async fn start_test_conversation(
|
||||
) -> (Arc<TempDir>, Config, Arc<ThreadManager>, Arc<CodexThread>) {
|
||||
let base_url = format!("{}/v1", server.uri());
|
||||
let model = model.map(str::to_string);
|
||||
let mut builder = test_codex().with_config(move |config| {
|
||||
config.update_plan_enabled = true;
|
||||
config.model_provider.name = "Non-OpenAI Model provider".to_string();
|
||||
config.model_provider.base_url = Some(base_url);
|
||||
config.compact_prompt = Some(SUMMARIZATION_PROMPT.to_string());
|
||||
if let Some(model) = model {
|
||||
config.model = Some(model);
|
||||
}
|
||||
});
|
||||
let mut extensions = ExtensionRegistryBuilder::new();
|
||||
extensions.thread_lifecycle_contributor(Arc::new(ThreadIdle));
|
||||
let mut builder = test_codex()
|
||||
.with_extensions(Arc::new(extensions.build()))
|
||||
.with_config(move |config| {
|
||||
config.update_plan_enabled = true;
|
||||
config.model_provider.name = "Non-OpenAI Model provider".to_string();
|
||||
config.model_provider.base_url = Some(base_url);
|
||||
config.compact_prompt = Some(SUMMARIZATION_PROMPT.to_string());
|
||||
if let Some(model) = model {
|
||||
config.model = Some(model);
|
||||
}
|
||||
});
|
||||
let test = Box::pin(builder.build(server))
|
||||
.await
|
||||
.expect("create conversation");
|
||||
@@ -868,6 +882,7 @@ async fn user_turn(conversation: &Arc<CodexThread>, text: &str) {
|
||||
.await
|
||||
.expect("submit user turn");
|
||||
wait_for_event(conversation, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
|
||||
ThreadIdle::wait(conversation).await;
|
||||
}
|
||||
|
||||
async fn compact_conversation(conversation: &Arc<CodexThread>) {
|
||||
@@ -887,6 +902,7 @@ async fn compact_conversation(conversation: &Arc<CodexThread>) {
|
||||
};
|
||||
assert_eq!(message, COMPACT_WARNING_MESSAGE);
|
||||
wait_for_event(conversation, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
|
||||
ThreadIdle::wait(conversation).await;
|
||||
}
|
||||
|
||||
fn fetch_conversation_path(conversation: &Arc<CodexThread>) -> std::path::PathBuf {
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
use anyhow::Result;
|
||||
use codex_core::TurnInputRequest;
|
||||
use codex_core::config::RolloutBudgetConfig;
|
||||
use codex_extension_api::ExtensionRegistryBuilder;
|
||||
use codex_features::Feature;
|
||||
use codex_model_provider_info::built_in_model_providers;
|
||||
use codex_protocol::protocol::CodexErrorInfo;
|
||||
use codex_protocol::protocol::EventMsg;
|
||||
use codex_protocol::protocol::Op;
|
||||
use codex_protocol::user_input::UserInput;
|
||||
use core_test_support::ThreadIdle;
|
||||
use core_test_support::responses::ResponsesRequest;
|
||||
use core_test_support::responses::ev_assistant_message;
|
||||
use core_test_support::responses::ev_completed;
|
||||
@@ -22,6 +24,7 @@ use core_test_support::test_codex::test_codex;
|
||||
use core_test_support::wait_for_event;
|
||||
use pretty_assertions::assert_eq;
|
||||
use serde_json::json;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use test_case::test_case;
|
||||
use tokio::time::timeout;
|
||||
@@ -482,7 +485,10 @@ async fn restates_the_current_remainder_after_rollback() -> Result<()> {
|
||||
],
|
||||
)
|
||||
.await;
|
||||
let mut extensions = ExtensionRegistryBuilder::new();
|
||||
extensions.thread_lifecycle_contributor(Arc::new(ThreadIdle));
|
||||
let test = test_codex()
|
||||
.with_extensions(Arc::new(extensions.build()))
|
||||
.with_config(|config| {
|
||||
config.rollout_budget = Some(RolloutBudgetConfig {
|
||||
reminder_at_remaining_tokens: vec![50],
|
||||
@@ -493,10 +499,14 @@ async fn restates_the_current_remainder_after_rollback() -> Result<()> {
|
||||
.await?;
|
||||
|
||||
test.submit_turn("rolled-back turn").await?;
|
||||
ThreadIdle::wait(&test.codex).await;
|
||||
test.codex
|
||||
.submit(Op::ThreadRollback { num_turns: 1 })
|
||||
.await?;
|
||||
wait_for_event(&test.codex, |event| {
|
||||
if let EventMsg::Error(error) = event {
|
||||
panic!("rollback failed: {error:?}");
|
||||
}
|
||||
matches!(event, EventMsg::ThreadRolledBack(_))
|
||||
})
|
||||
.await;
|
||||
|
||||
Reference in new issue
Block a user