mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
0c8a36676a
## Why PR #13783 moved the `codex.rs` unit tests into `codex_tests.rs`. This applies the same extraction pattern across the rest of `codex-rs/core` so the production modules stay focused on runtime code instead of large inline test blocks. Keeping the tests in sibling files also makes follow-up edits easier to review because product changes no longer have to share a file with hundreds or thousands of lines of test scaffolding. ## What changed - replaced each inline `mod tests { ... }` in `codex-rs/core/src/**` with a path-based module declaration - moved each extracted unit test module into a sibling `*_tests.rs` file, using `mod_tests.rs` for `mod.rs` modules - preserved the existing `cfg(...)` guards and module-local structure so the refactor remains structural rather than behavioral ## Testing - `cargo test -p codex-core --lib` (`1653 passed; 0 failed; 5 ignored`) - `just fix -p codex-core` - `cargo fmt --check` - `cargo shear`
166 lines
5.6 KiB
Rust
166 lines
5.6 KiB
Rust
use codex_protocol::items::AgentMessageContent;
|
|
use codex_protocol::items::AgentMessageItem;
|
|
use codex_protocol::items::ReasoningItem;
|
|
use codex_protocol::items::TurnItem;
|
|
use codex_protocol::items::UserMessageItem;
|
|
use codex_protocol::items::WebSearchItem;
|
|
use codex_protocol::models::ContentItem;
|
|
use codex_protocol::models::MessagePhase;
|
|
use codex_protocol::models::ReasoningItemContent;
|
|
use codex_protocol::models::ReasoningItemReasoningSummary;
|
|
use codex_protocol::models::ResponseItem;
|
|
use codex_protocol::models::WebSearchAction;
|
|
use codex_protocol::models::is_image_close_tag_text;
|
|
use codex_protocol::models::is_image_open_tag_text;
|
|
use codex_protocol::models::is_local_image_close_tag_text;
|
|
use codex_protocol::models::is_local_image_open_tag_text;
|
|
use codex_protocol::user_input::UserInput;
|
|
use tracing::warn;
|
|
use uuid::Uuid;
|
|
|
|
use crate::contextual_user_message::is_contextual_user_fragment;
|
|
use crate::web_search::web_search_action_detail;
|
|
|
|
pub(crate) fn is_contextual_user_message_content(message: &[ContentItem]) -> bool {
|
|
message.iter().any(is_contextual_user_fragment)
|
|
}
|
|
|
|
fn parse_user_message(message: &[ContentItem]) -> Option<UserMessageItem> {
|
|
if is_contextual_user_message_content(message) {
|
|
return None;
|
|
}
|
|
|
|
let mut content: Vec<UserInput> = Vec::new();
|
|
|
|
for (idx, content_item) in message.iter().enumerate() {
|
|
match content_item {
|
|
ContentItem::InputText { text } => {
|
|
if (is_local_image_open_tag_text(text) || is_image_open_tag_text(text))
|
|
&& (matches!(message.get(idx + 1), Some(ContentItem::InputImage { .. })))
|
|
|| (idx > 0
|
|
&& (is_local_image_close_tag_text(text) || is_image_close_tag_text(text))
|
|
&& matches!(message.get(idx - 1), Some(ContentItem::InputImage { .. })))
|
|
{
|
|
continue;
|
|
}
|
|
content.push(UserInput::Text {
|
|
text: text.clone(),
|
|
// Model input content does not carry UI element ranges.
|
|
text_elements: Vec::new(),
|
|
});
|
|
}
|
|
ContentItem::InputImage { image_url } => {
|
|
content.push(UserInput::Image {
|
|
image_url: image_url.clone(),
|
|
});
|
|
}
|
|
ContentItem::OutputText { text } => {
|
|
warn!("Output text in user message: {}", text);
|
|
}
|
|
}
|
|
}
|
|
|
|
Some(UserMessageItem::new(&content))
|
|
}
|
|
|
|
fn parse_agent_message(
|
|
id: Option<&String>,
|
|
message: &[ContentItem],
|
|
phase: Option<MessagePhase>,
|
|
) -> AgentMessageItem {
|
|
let mut content: Vec<AgentMessageContent> = Vec::new();
|
|
for content_item in message.iter() {
|
|
match content_item {
|
|
ContentItem::OutputText { text } => {
|
|
content.push(AgentMessageContent::Text { text: text.clone() });
|
|
}
|
|
_ => {
|
|
warn!(
|
|
"Unexpected content item in agent message: {:?}",
|
|
content_item
|
|
);
|
|
}
|
|
}
|
|
}
|
|
let id = id.cloned().unwrap_or_else(|| Uuid::new_v4().to_string());
|
|
AgentMessageItem { id, content, phase }
|
|
}
|
|
|
|
pub fn parse_turn_item(item: &ResponseItem) -> Option<TurnItem> {
|
|
match item {
|
|
ResponseItem::Message {
|
|
role,
|
|
content,
|
|
id,
|
|
phase,
|
|
..
|
|
} => match role.as_str() {
|
|
"user" => parse_user_message(content).map(TurnItem::UserMessage),
|
|
"assistant" => Some(TurnItem::AgentMessage(parse_agent_message(
|
|
id.as_ref(),
|
|
content,
|
|
phase.clone(),
|
|
))),
|
|
"system" => None,
|
|
_ => None,
|
|
},
|
|
ResponseItem::Reasoning {
|
|
id,
|
|
summary,
|
|
content,
|
|
..
|
|
} => {
|
|
let summary_text = summary
|
|
.iter()
|
|
.map(|entry| match entry {
|
|
ReasoningItemReasoningSummary::SummaryText { text } => text.clone(),
|
|
})
|
|
.collect();
|
|
let raw_content = content
|
|
.clone()
|
|
.unwrap_or_default()
|
|
.into_iter()
|
|
.map(|entry| match entry {
|
|
ReasoningItemContent::ReasoningText { text }
|
|
| ReasoningItemContent::Text { text } => text,
|
|
})
|
|
.collect();
|
|
Some(TurnItem::Reasoning(ReasoningItem {
|
|
id: id.clone(),
|
|
summary_text,
|
|
raw_content,
|
|
}))
|
|
}
|
|
ResponseItem::WebSearchCall { id, action, .. } => {
|
|
let (action, query) = match action {
|
|
Some(action) => (action.clone(), web_search_action_detail(action)),
|
|
None => (WebSearchAction::Other, String::new()),
|
|
};
|
|
Some(TurnItem::WebSearch(WebSearchItem {
|
|
id: id.clone().unwrap_or_default(),
|
|
query,
|
|
action,
|
|
}))
|
|
}
|
|
ResponseItem::ImageGenerationCall {
|
|
id,
|
|
status,
|
|
revised_prompt,
|
|
result,
|
|
} => Some(TurnItem::ImageGeneration(
|
|
codex_protocol::items::ImageGenerationItem {
|
|
id: id.clone(),
|
|
status: status.clone(),
|
|
revised_prompt: revised_prompt.clone(),
|
|
result: result.clone(),
|
|
saved_path: None,
|
|
},
|
|
)),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
#[path = "event_mapping_tests.rs"]
|
|
mod tests;
|