mirror of
https://github.com/openai/codex.git
synced 2026-11-05 20:10:33 +00:00
refactor: collapse core realtime adapters
This commit is contained in:
@@ -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(¤t_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;
|
||||
@@ -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(¤t_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;
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user