Download codex-rs/app-server/src/request_processors/thread_summary.rs from SaylorTwift/codex: direct link, hf CLI and curl.
- Browser
- Download file 9.52 kB
-
https://huggingface.co/SaylorTwift/codex/resolve/main/codex-rs/app-server/src/request_processors/thread_summary.rs
- Command line
-
hf download hf://SaylorTwift/codex/codex-rs/app-server/src/request_processors/thread_summary.rs
-
curl -L -o thread_summary.rs https://huggingface.co/SaylorTwift/codex/resolve/main/codex-rs/app-server/src/request_processors/thread_summary.rs
9.52 kB
| use super::*; | |
| use chrono::DateTime; | |
| use chrono::Utc; | |
| use codex_protocol::config_types::MultiAgentMode; | |
| pub(crate) async fn read_summary_from_rollout( | |
| path: &Path, | |
| fallback_provider: &str, | |
| ) -> std::io::Result<ConversationSummary> { | |
| let head = read_head_for_summary(path).await?; | |
| let Some(first) = head.first() else { | |
| return Err(IoError::other(format!( | |
| "rollout at {} is empty", | |
| path.display() | |
| ))); | |
| }; | |
| let session_meta_line = | |
| serde_json::from_value::<SessionMetaLine>(first.clone()).map_err(|_| { | |
| IoError::other(format!( | |
| "rollout at {} does not start with session metadata", | |
| path.display() | |
| )) | |
| })?; | |
| let SessionMetaLine { | |
| meta: session_meta, | |
| git, | |
| } = session_meta_line; | |
| let mut session_meta = session_meta; | |
| session_meta.source = with_thread_spawn_agent_metadata( | |
| session_meta.source.clone(), | |
| session_meta.agent_nickname.clone(), | |
| session_meta.agent_role.clone(), | |
| ); | |
| let created_at = if session_meta.timestamp.is_empty() { | |
| None | |
| } else { | |
| Some(session_meta.timestamp.as_str()) | |
| }; | |
| let updated_at = read_updated_at(path, created_at).await; | |
| if let Some(summary) = extract_conversation_summary( | |
| path.to_path_buf(), | |
| &head, | |
| &session_meta, | |
| git.as_ref(), | |
| fallback_provider, | |
| updated_at.clone(), | |
| ) { | |
| return Ok(summary); | |
| } | |
| let timestamp = if session_meta.timestamp.is_empty() { | |
| None | |
| } else { | |
| Some(session_meta.timestamp.clone()) | |
| }; | |
| let model_provider = session_meta | |
| .model_provider | |
| .clone() | |
| .unwrap_or_else(|| fallback_provider.to_string()); | |
| let git_info = git.as_ref().map(map_git_info); | |
| let updated_at = updated_at.or_else(|| timestamp.clone()); | |
| Ok(ConversationSummary { | |
| conversation_id: session_meta.id, | |
| timestamp, | |
| updated_at, | |
| path: path.to_path_buf(), | |
| preview: String::new(), | |
| model_provider, | |
| cwd: session_meta.cwd, | |
| cli_version: session_meta.cli_version, | |
| source: session_meta.source, | |
| git_info, | |
| }) | |
| } | |
| fn extract_conversation_summary( | |
| path: PathBuf, | |
| head: &[serde_json::Value], | |
| session_meta: &SessionMeta, | |
| git: Option<&CoreGitInfo>, | |
| fallback_provider: &str, | |
| updated_at: Option<String>, | |
| ) -> Option<ConversationSummary> { | |
| let preview = head | |
| .iter() | |
| .filter_map(|value| serde_json::from_value::<ResponseItem>(value.clone()).ok()) | |
| .find_map(|item| match codex_core::parse_turn_item(&item) { | |
| Some(TurnItem::UserMessage(user)) => Some(user.message()), | |
| _ => None, | |
| })?; | |
| let preview = strip_user_message_prefix(preview.as_str()); | |
| let timestamp = if session_meta.timestamp.is_empty() { | |
| None | |
| } else { | |
| Some(session_meta.timestamp.clone()) | |
| }; | |
| let conversation_id = session_meta.id; | |
| let model_provider = session_meta | |
| .model_provider | |
| .clone() | |
| .unwrap_or_else(|| fallback_provider.to_string()); | |
| let git_info = git.map(map_git_info); | |
| let updated_at = updated_at.or_else(|| timestamp.clone()); | |
| Some(ConversationSummary { | |
| conversation_id, | |
| timestamp, | |
| updated_at, | |
| path, | |
| preview: preview.to_string(), | |
| model_provider, | |
| cwd: session_meta.cwd.clone(), | |
| cli_version: session_meta.cli_version.clone(), | |
| source: session_meta.source.clone(), | |
| git_info, | |
| }) | |
| } | |
| fn map_git_info(git_info: &CoreGitInfo) -> ConversationGitInfo { | |
| ConversationGitInfo { | |
| sha: git_info.commit_hash.as_ref().map(|sha| sha.0.clone()), | |
| branch: git_info.branch.clone(), | |
| origin_url: git_info.repository_url.clone().map(String::from), | |
| } | |
| } | |
| pub(super) fn with_thread_spawn_agent_metadata( | |
| source: codex_protocol::protocol::SessionSource, | |
| agent_nickname: Option<String>, | |
| agent_role: Option<String>, | |
| ) -> codex_protocol::protocol::SessionSource { | |
| if agent_nickname.is_none() && agent_role.is_none() { | |
| return source; | |
| } | |
| match source { | |
| codex_protocol::protocol::SessionSource::SubAgent( | |
| codex_protocol::protocol::SubAgentSource::ThreadSpawn { | |
| parent_thread_id, | |
| depth, | |
| agent_path, | |
| agent_nickname: existing_agent_nickname, | |
| agent_role: existing_agent_role, | |
| }, | |
| ) => codex_protocol::protocol::SessionSource::SubAgent( | |
| codex_protocol::protocol::SubAgentSource::ThreadSpawn { | |
| parent_thread_id, | |
| depth, | |
| agent_path, | |
| agent_nickname: agent_nickname.or(existing_agent_nickname), | |
| agent_role: agent_role.or(existing_agent_role), | |
| }, | |
| ), | |
| _ => source, | |
| } | |
| } | |
| pub(crate) fn thread_response_active_permission_profile( | |
| active_permission_profile: Option<codex_protocol::models::ActivePermissionProfile>, | |
| ) -> Option<codex_app_server_protocol::ActivePermissionProfile> { | |
| active_permission_profile.map(Into::into) | |
| } | |
| pub(crate) fn thread_settings_from_config_snapshot( | |
| config_snapshot: &ThreadConfigSnapshot, | |
| ) -> ThreadSettings { | |
| ThreadSettings { | |
| disabled_plugin_ids: config_snapshot.disabled_plugin_ids.clone(), | |
| cwd: config_snapshot.cwd().clone(), | |
| approval_policy: config_snapshot.approval_policy.into(), | |
| approvals_reviewer: config_snapshot.approvals_reviewer.into(), | |
| sandbox_policy: config_snapshot.sandbox_policy().into(), | |
| active_permission_profile: thread_response_active_permission_profile( | |
| config_snapshot.active_permission_profile.clone(), | |
| ), | |
| model: config_snapshot.model.clone(), | |
| model_provider: config_snapshot.model_provider_id.clone(), | |
| service_tier: config_snapshot.service_tier.clone(), | |
| effort: config_snapshot.reasoning_effort.clone(), | |
| summary: config_snapshot.reasoning_summary, | |
| collaboration_mode: config_snapshot.collaboration_mode.clone(), | |
| multi_agent_mode: MultiAgentMode::ExplicitRequestOnly, | |
| personality: config_snapshot.personality, | |
| } | |
| } | |
| fn parse_datetime(timestamp: Option<&str>) -> Option<DateTime<Utc>> { | |
| timestamp.and_then(|ts| { | |
| chrono::DateTime::parse_from_rfc3339(ts) | |
| .ok() | |
| .map(|dt| dt.with_timezone(&chrono::Utc)) | |
| }) | |
| } | |
| async fn read_updated_at(path: &Path, created_at: Option<&str>) -> Option<String> { | |
| let updated_at = tokio::fs::metadata(path) | |
| .await | |
| .ok() | |
| .and_then(|meta| meta.modified().ok()) | |
| .map(|modified| { | |
| let updated_at: DateTime<Utc> = modified.into(); | |
| updated_at.to_rfc3339_opts(SecondsFormat::Millis, true) | |
| }); | |
| updated_at.or_else(|| created_at.map(str::to_string)) | |
| } | |
| pub(super) fn thread_started_notification(mut thread: Thread) -> ThreadStartedNotification { | |
| thread.turns.clear(); | |
| ThreadStartedNotification { thread } | |
| } | |
| pub(crate) fn summary_to_thread( | |
| summary: ConversationSummary, | |
| fallback_cwd: &AbsolutePathBuf, | |
| ) -> Thread { | |
| let ConversationSummary { | |
| conversation_id, | |
| path, | |
| preview, | |
| timestamp, | |
| updated_at, | |
| model_provider, | |
| cwd, | |
| cli_version, | |
| source, | |
| git_info, | |
| } = summary; | |
| let created_at = parse_datetime(timestamp.as_deref()); | |
| let updated_at = parse_datetime(updated_at.as_deref()).or(created_at); | |
| let git_info = git_info.map(|info| ApiGitInfo { | |
| sha: info.sha, | |
| branch: info.branch, | |
| origin_url: info.origin_url, | |
| }); | |
| let cwd = | |
| AbsolutePathBuf::relative_to_current_dir(path_utils::normalize_for_native_workdir(cwd)) | |
| .unwrap_or_else(|err| { | |
| warn!( | |
| conversation_id = %conversation_id, | |
| path = %path.display(), | |
| "failed to normalize thread cwd while summarizing thread: {err}" | |
| ); | |
| fallback_cwd.clone() | |
| }); | |
| let thread_id = conversation_id.to_string(); | |
| Thread { | |
| originator: None, | |
| environments: None, | |
| id: thread_id.clone(), | |
| extra: None, | |
| session_id: thread_id, | |
| forked_from_id: None, | |
| parent_thread_id: None, | |
| preview, | |
| ephemeral: false, | |
| section: None, | |
| section_entered_at: None, | |
| project_id: None, | |
| history_mode: ThreadHistoryMode::Legacy, | |
| model_provider, | |
| created_at: created_at.map(|dt| dt.timestamp()).unwrap_or(0), | |
| model: None, | |
| reasoning_effort: None, | |
| updated_at: updated_at.map(|dt| dt.timestamp()).unwrap_or(0), | |
| recency_at: updated_at.map(|dt| dt.timestamp()), | |
| status: ThreadStatus::NotLoaded, | |
| path: (!path.as_os_str().is_empty()).then_some(path), | |
| cwd, | |
| cli_version, | |
| agent_nickname: source.get_nickname(), | |
| agent_role: source.get_agent_role(), | |
| source: source.into(), | |
| can_accept_direct_input: None, | |
| thread_source: None, | |
| git_info, | |
| name: None, | |
| daybreak_enabled: None, | |
| turns: Vec::new(), | |
| } | |
| } | |
| mod thread_summary_tests; | |