refactor: collapse core realtime adapters

This commit is contained in:
Ahmed Ibrahim
2026-04-20 19:01:55 -07:00
parent b2b94fe755
commit e7fe25dc5e
5 changed files with 143 additions and 151 deletions
+1 -2
View File
@@ -11,8 +11,7 @@ mod apps;
mod arc_monitor;
mod client;
mod client_common;
mod realtime_context;
mod realtime_conversation;
mod realtime;
pub(crate) mod session;
pub use session::SteerInputError;
mod codex_thread;
@@ -1,5 +1,5 @@
use crate::compact::content_items_to_text;
use crate::config::Config;
use crate::realtime_context::build_realtime_startup_context;
use crate::session::session::Session;
use codex_api::RealtimeEvent;
use codex_api::RealtimeSessionConfig;
@@ -9,6 +9,7 @@ use codex_app_server_protocol::AuthMode;
use codex_config::config_toml::RealtimeWsVersion;
use codex_login::default_client::default_headers;
use codex_protocol::error::Result as CodexResult;
use codex_protocol::models::ResponseItem;
use codex_protocol::protocol::CodexErrorInfo;
use codex_protocol::protocol::ConversationAudioParams;
use codex_protocol::protocol::ConversationStartParams;
@@ -23,6 +24,13 @@ use codex_protocol::protocol::RealtimeConversationSdpEvent;
use codex_protocol::protocol::RealtimeConversationStartedEvent;
use codex_protocol::protocol::RealtimeOutputModality;
use codex_protocol::protocol::RealtimeVoice;
use codex_thread_store::ListThreadsParams;
use codex_thread_store::SortDirection;
use codex_thread_store::StoredThread;
use codex_thread_store::ThreadSortKey;
use codex_thread_store::ThreadStore;
use dirs::home_dir;
use std::mem::take;
use std::sync::Arc;
use std::sync::atomic::Ordering;
use tracing::debug;
@@ -45,8 +53,127 @@ use codex_realtime::realtime_text_from_handoff_request;
#[cfg(test)]
use codex_realtime::wrap_realtime_delegation_input;
const MAX_RECENT_THREADS: usize = 40;
const REALTIME_STARTUP_CONTEXT_TOKEN_BUDGET: usize = 5_300;
pub(crate) async fn build_realtime_startup_context(
sess: &Session,
budget_tokens: usize,
) -> Option<String> {
let config = sess.get_config().await;
let history = sess.clone_history().await;
let current_thread_turns = current_thread_turns(history.raw_items());
let recent_threads = load_recent_threads(sess).await;
let context = codex_realtime::RealtimeStartupContext {
cwd: config.cwd.clone(),
current_thread_turns,
recent_threads,
user_root: home_dir(),
};
codex_realtime::build_realtime_startup_context(&context, budget_tokens).await
}
async fn load_recent_threads(sess: &Session) -> Vec<StoredThread> {
match sess
.services
.thread_store
.list_threads(ListThreadsParams {
page_size: MAX_RECENT_THREADS,
cursor: None,
sort_key: ThreadSortKey::UpdatedAt,
sort_direction: SortDirection::Desc,
allowed_sources: Vec::new(),
model_providers: None,
archived: false,
search_term: None,
})
.await
{
Ok(page) => page.items,
Err(err) => {
warn!("failed to load realtime startup threads from thread store: {err}");
Vec::new()
}
}
}
fn current_thread_turns(items: &[ResponseItem]) -> Vec<codex_realtime::RealtimeStartupContextTurn> {
let mut turns = Vec::new();
let mut current_user = Vec::new();
let mut current_assistant = Vec::new();
for item in items {
match item {
ResponseItem::Message { role, content, .. } if role == "user" => {
if crate::event_mapping::is_contextual_user_message_content(content) {
continue;
}
let Some(text) = content_items_to_text(content)
.map(|text| text.trim().to_string())
.filter(|text| !text.is_empty())
else {
continue;
};
if !current_user.is_empty() || !current_assistant.is_empty() {
turns.push(codex_realtime::RealtimeStartupContextTurn {
user_messages: take(&mut current_user),
assistant_messages: take(&mut current_assistant),
});
}
current_user.push(text);
}
ResponseItem::Message { role, content, .. } if role == "assistant" => {
let Some(text) = content_items_to_text(content)
.map(|text| text.trim().to_string())
.filter(|text| !text.is_empty())
else {
continue;
};
if current_user.is_empty() && current_assistant.is_empty() {
continue;
}
current_assistant.push(text);
}
_ => {}
}
}
if !current_user.is_empty() || !current_assistant.is_empty() {
turns.push(codex_realtime::RealtimeStartupContextTurn {
user_messages: current_user,
assistant_messages: current_assistant,
});
}
turns
}
#[cfg(test)]
fn build_current_thread_section(items: &[ResponseItem]) -> Option<String> {
codex_realtime::build_current_thread_section(&current_thread_turns(items))
}
#[cfg(test)]
pub(crate) use codex_realtime::CURRENT_THREAD_SECTION_TOKEN_BUDGET;
#[cfg(test)]
pub(crate) use codex_realtime::NOTES_SECTION_TOKEN_BUDGET;
pub(crate) use codex_realtime::REALTIME_TURN_TOKEN_BUDGET;
#[cfg(test)]
pub(crate) use codex_realtime::RECENT_WORK_SECTION_TOKEN_BUDGET;
#[cfg(test)]
pub(crate) use codex_realtime::STARTUP_CONTEXT_HEADER;
#[cfg(test)]
pub(crate) use codex_realtime::WORKSPACE_SECTION_TOKEN_BUDGET;
#[cfg(test)]
use codex_realtime::build_recent_work_section;
#[cfg(test)]
use codex_realtime::build_workspace_section_with_user_root;
#[cfg(test)]
use codex_realtime::format_section;
#[cfg(test)]
use codex_realtime::format_startup_context_blob;
pub(crate) use codex_realtime::truncate_realtime_text_to_token_budget;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum RealtimeConversationEnd {
Requested,
@@ -403,4 +530,8 @@ async fn send_realtime_conversation_closed(
#[cfg(test)]
#[path = "realtime_conversation_tests.rs"]
mod tests;
mod conversation_tests;
#[cfg(test)]
#[path = "realtime_context_tests.rs"]
mod context_tests;
-138
View File
@@ -1,138 +0,0 @@
use crate::compact::content_items_to_text;
use crate::event_mapping::is_contextual_user_message_content;
use crate::session::session::Session;
use codex_protocol::models::ResponseItem;
use codex_realtime::RealtimeStartupContext;
use codex_realtime::RealtimeStartupContextTurn;
use codex_thread_store::ListThreadsParams;
use codex_thread_store::SortDirection;
use codex_thread_store::StoredThread;
use codex_thread_store::ThreadSortKey;
use codex_thread_store::ThreadStore;
use dirs::home_dir;
use std::mem::take;
use tracing::warn;
const MAX_RECENT_THREADS: usize = 40;
pub(crate) async fn build_realtime_startup_context(
sess: &Session,
budget_tokens: usize,
) -> Option<String> {
let config = sess.get_config().await;
let history = sess.clone_history().await;
let current_thread_turns = current_thread_turns(history.raw_items());
let recent_threads = load_recent_threads(sess).await;
let context = RealtimeStartupContext {
cwd: config.cwd.clone(),
current_thread_turns,
recent_threads,
user_root: home_dir(),
};
codex_realtime::build_realtime_startup_context(&context, budget_tokens).await
}
async fn load_recent_threads(sess: &Session) -> Vec<StoredThread> {
match sess
.services
.thread_store
.list_threads(ListThreadsParams {
page_size: MAX_RECENT_THREADS,
cursor: None,
sort_key: ThreadSortKey::UpdatedAt,
sort_direction: SortDirection::Desc,
allowed_sources: Vec::new(),
model_providers: None,
archived: false,
search_term: None,
})
.await
{
Ok(page) => page.items,
Err(err) => {
warn!("failed to load realtime startup threads from thread store: {err}");
Vec::new()
}
}
}
fn current_thread_turns(items: &[ResponseItem]) -> Vec<RealtimeStartupContextTurn> {
let mut turns = Vec::new();
let mut current_user = Vec::new();
let mut current_assistant = Vec::new();
for item in items {
match item {
ResponseItem::Message { role, content, .. } if role == "user" => {
if is_contextual_user_message_content(content) {
continue;
}
let Some(text) = content_items_to_text(content)
.map(|text| text.trim().to_string())
.filter(|text| !text.is_empty())
else {
continue;
};
if !current_user.is_empty() || !current_assistant.is_empty() {
turns.push(RealtimeStartupContextTurn {
user_messages: take(&mut current_user),
assistant_messages: take(&mut current_assistant),
});
}
current_user.push(text);
}
ResponseItem::Message { role, content, .. } if role == "assistant" => {
let Some(text) = content_items_to_text(content)
.map(|text| text.trim().to_string())
.filter(|text| !text.is_empty())
else {
continue;
};
if current_user.is_empty() && current_assistant.is_empty() {
continue;
}
current_assistant.push(text);
}
_ => {}
}
}
if !current_user.is_empty() || !current_assistant.is_empty() {
turns.push(RealtimeStartupContextTurn {
user_messages: current_user,
assistant_messages: current_assistant,
});
}
turns
}
#[cfg(test)]
fn build_current_thread_section(items: &[ResponseItem]) -> Option<String> {
codex_realtime::build_current_thread_section(&current_thread_turns(items))
}
#[cfg(test)]
pub(crate) use codex_realtime::CURRENT_THREAD_SECTION_TOKEN_BUDGET;
#[cfg(test)]
pub(crate) use codex_realtime::NOTES_SECTION_TOKEN_BUDGET;
pub(crate) use codex_realtime::REALTIME_TURN_TOKEN_BUDGET;
#[cfg(test)]
pub(crate) use codex_realtime::RECENT_WORK_SECTION_TOKEN_BUDGET;
#[cfg(test)]
pub(crate) use codex_realtime::STARTUP_CONTEXT_HEADER;
#[cfg(test)]
pub(crate) use codex_realtime::WORKSPACE_SECTION_TOKEN_BUDGET;
#[cfg(test)]
use codex_realtime::build_recent_work_section;
#[cfg(test)]
use codex_realtime::build_workspace_section_with_user_root;
#[cfg(test)]
use codex_realtime::format_section;
#[cfg(test)]
use codex_realtime::format_startup_context_blob;
pub(crate) use codex_realtime::truncate_realtime_text_to_token_budget;
#[cfg(test)]
#[path = "realtime_context_tests.rs"]
mod tests;
+8 -8
View File
@@ -1,7 +1,7 @@
use crate::realtime_conversation::handle_audio as handle_realtime_conversation_audio;
use crate::realtime_conversation::handle_close as handle_realtime_conversation_close;
use crate::realtime_conversation::handle_start as handle_realtime_conversation_start;
use crate::realtime_conversation::handle_text as handle_realtime_conversation_text;
use crate::realtime::handle_audio as handle_realtime_conversation_audio;
use crate::realtime::handle_close as handle_realtime_conversation_close;
use crate::realtime::handle_start as handle_realtime_conversation_start;
use crate::realtime::handle_text as handle_realtime_conversation_text;
use async_channel::Receiver;
use codex_otel::set_parent_from_w3c_trace_context;
use codex_protocol::protocol::Submission;
@@ -17,10 +17,10 @@ use crate::config::Config;
use crate::config_loader::CloudRequirementsLoader;
use crate::config_loader::LoaderOverrides;
use crate::config_loader::load_config_layers_state;
use crate::realtime_context::REALTIME_TURN_TOKEN_BUDGET;
use crate::realtime_context::truncate_realtime_text_to_token_budget;
use crate::realtime_conversation::REALTIME_USER_TEXT_PREFIX;
use crate::realtime_conversation::prefix_realtime_v2_text;
use crate::realtime::REALTIME_TURN_TOKEN_BUDGET;
use crate::realtime::REALTIME_USER_TEXT_PREFIX;
use crate::realtime::prefix_realtime_v2_text;
use crate::realtime::truncate_realtime_text_to_token_budget;
use crate::session::spawn_review_thread;
use codex_exec_server::LOCAL_FS;
use codex_features::Feature;
+1 -1
View File
@@ -24,7 +24,7 @@ use crate::exec_policy::ExecPolicyManager;
use crate::installation_id::resolve_installation_id;
use crate::parse_turn_item;
use crate::path_utils::normalize_for_native_workdir;
use crate::realtime_conversation::RealtimeConversationManager;
use crate::realtime::RealtimeConversationManager;
use crate::render_skills_section;
use crate::rollout::find_thread_name_by_id;
use crate::session_prefix::format_subagent_notification_message;