Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions codex-rs/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion codex-rs/app-server/tests/suite/v2/imagegen_extension.rs
Original file line number Diff line number Diff line change
Expand Up @@ -588,7 +588,6 @@ model_provider = "openai-custom"
chatgpt_base_url = "{server_uri}"

[features]
imagegenext = true
{code_mode_only}

[model_providers.openai-custom]
Expand Down
1 change: 1 addition & 0 deletions codex-rs/core-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ codex-config = { workspace = true }
codex-core = { workspace = true }
codex-extension-api = { workspace = true }
codex-home = { workspace = true }
codex-image-generation-extension = { workspace = true }
codex-exec-server = { workspace = true }
codex-features = { workspace = true }
codex-login = { workspace = true }
Expand Down
2 changes: 2 additions & 0 deletions codex-rs/core-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ pub use codex_exec_server::NoiseChannelIdentity;
pub use codex_exec_server::NoiseChannelPublicKey;
pub use codex_exec_server::NoiseRendezvousConnectBundle;
pub use codex_exec_server::NoiseRendezvousConnectProvider;
pub use codex_extension_api::ExtensionRegistryBuilder;
pub use codex_extension_api::LoadUserInstructionsFuture;
pub use codex_extension_api::LoadedUserInstructions;
pub use codex_extension_api::UserInstructions;
Expand All @@ -69,6 +70,7 @@ pub use codex_extension_api::empty_extension_registry;
pub use codex_features::Feature;
pub use codex_features::Features;
pub use codex_home::CodexHomeUserInstructionsProvider;
pub use codex_image_generation_extension::install as install_image_generation_extension;
pub use codex_login::AuthHeaders;
pub use codex_login::AuthManager;
pub use codex_login::CodexAuth;
Expand Down
34 changes: 0 additions & 34 deletions codex-rs/core/src/context/image_generation_instructions.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use super::ContextualUserFragment;
use std::fmt::Display;

/// Maximum size of the extension's model-facing generated-image path hint.
Expand All @@ -21,36 +20,3 @@ fn image_generation_hint(
"Generated images are saved to {image_output_dir} as {image_output_path} by default.\nIf you need to use a generated image at another path, copy it and leave the original in place unless the user explicitly asks you to delete it."
)
}

#[derive(Debug, Clone, PartialEq)]
pub(crate) struct ImageGenerationInstructions {
image_output_dir: String,
image_output_path: String,
}

impl ImageGenerationInstructions {
pub(crate) fn new(image_output_dir: impl Display, image_output_path: impl Display) -> Self {
Self {
image_output_dir: image_output_dir.to_string(),
image_output_path: image_output_path.to_string(),
}
}
}

impl ContextualUserFragment for ImageGenerationInstructions {
fn role(&self) -> &'static str {
"developer"
}

fn markers(&self) -> (&'static str, &'static str) {
Self::type_markers()
}

fn type_markers() -> (&'static str, &'static str) {
("", "")
}

fn body(&self) -> String {
image_generation_hint(&self.image_output_dir, &self.image_output_path)
}
}
1 change: 0 additions & 1 deletion codex-rs/core/src/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ pub(crate) use contextual_user_message::parse_visible_hook_prompt_message;
pub(crate) use current_time_reminder::CurrentTimeReminder;
pub(crate) use guardian_followup_review_reminder::GuardianFollowupReviewReminder;
pub(crate) use hook_additional_context::HookAdditionalContext;
pub(crate) use image_generation_instructions::ImageGenerationInstructions;
pub use image_generation_instructions::extension_image_generation_output_hint;
pub(crate) use inter_agent_completion_message::InterAgentCompletionMessage;
pub use internal_model_context::InternalContextSource;
Expand Down
139 changes: 0 additions & 139 deletions codex-rs/core/src/session/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8512,49 +8512,6 @@ async fn build_initial_context_omits_multi_agent_v2_usage_hints_when_hint_is_emp
);
}

#[tokio::test]
async fn build_initial_context_omits_default_image_save_location_with_image_history() {
let (session, turn_context) = make_session_and_context().await;
session
.replace_history(
vec![ResponseItem::ImageGenerationCall {
id: Some("ig-test".to_string()),
status: "completed".to_string(),
revised_prompt: Some("a tiny blue square".to_string()),
result: "Zm9v".to_string(),
internal_chat_message_metadata_passthrough: None,
}],
/*reference_context_item*/ None,
)
.await;
let turn_context = Arc::new(turn_context);

let initial_context = build_initial_context(&session, &turn_context).await;
let developer_texts = developer_input_texts(&initial_context);
assert!(
!developer_texts
.iter()
.any(|text| text.contains("Generated images are saved to")),
"expected initial context to omit image save instructions even with image history, got {developer_texts:?}"
);
}

#[tokio::test]
async fn build_initial_context_omits_default_image_save_location_without_image_history() {
let (session, turn_context) = make_session_and_context().await;
let turn_context = Arc::new(turn_context);

let initial_context = build_initial_context(&session, &turn_context).await;
let developer_texts = developer_input_texts(&initial_context);

assert!(
!developer_texts
.iter()
.any(|text| text.contains("Generated images are saved to")),
"expected initial context to omit image save instructions without image history, got {developer_texts:?}"
);
}

#[tokio::test]
async fn build_initial_context_trims_skill_metadata_from_context_window_budget() {
let (session, mut turn_context) = make_session_and_context().await;
Expand Down Expand Up @@ -8762,102 +8719,6 @@ async fn build_initial_context_emits_thread_start_skill_warning_on_repeated_buil
));
}

#[tokio::test]
async fn handle_output_item_done_records_image_save_history_message() {
let (session, turn_context) = make_session_and_context().await;
let session = Arc::new(session);
let turn_context = Arc::new(turn_context);
let call_id = "ig_history_records_message";
let expected_saved_path = crate::stream_events_utils::image_generation_artifact_path(
&turn_context.config.codex_home,
&session.thread_id.to_string(),
call_id,
);
let _ = std::fs::remove_file(&expected_saved_path);
let item = ResponseItem::ImageGenerationCall {
id: Some(call_id.to_string()),
status: "completed".to_string(),
revised_prompt: Some("a tiny blue square".to_string()),
result: "Zm9v".to_string(),
internal_chat_message_metadata_passthrough: None,
};

let mut ctx = HandleOutputCtx {
sess: Arc::clone(&session),
turn_context: Arc::clone(&turn_context),
turn_store: Arc::new(codex_extension_api::ExtensionData::new(
turn_context.sub_id.clone(),
)),
tool_runtime: test_tool_runtime(Arc::clone(&session), Arc::clone(&turn_context)),
cancellation_token: CancellationToken::new(),
};
handle_output_item_done(&mut ctx, item.clone(), /*previously_active_item*/ None)
.await
.expect("image generation item should succeed");

let history = session.clone_history().await;
let image_output_path = crate::stream_events_utils::image_generation_artifact_path(
&turn_context.config.codex_home,
&session.thread_id.to_string(),
"<image_id>",
);
let image_output_dir = image_output_path
.parent()
.expect("generated image path should have a parent");
let image_message: ResponseItem = crate::context::ContextualUserFragment::into(
crate::context::ImageGenerationInstructions::new(
image_output_dir.display(),
image_output_path.display(),
),
);
let expected = vec![image_message, item];
assert_eq!(strip_metadata_from_items(history.raw_items()), expected);
assert_eq!(
std::fs::read(&expected_saved_path).expect("saved file"),
b"foo"
);
let _ = std::fs::remove_file(&expected_saved_path);
}

#[tokio::test]
async fn handle_output_item_done_skips_image_save_message_when_save_fails() {
let (session, turn_context) = make_session_and_context().await;
let session = Arc::new(session);
let turn_context = Arc::new(turn_context);
let call_id = "ig_history_no_message";
let expected_saved_path = crate::stream_events_utils::image_generation_artifact_path(
&turn_context.config.codex_home,
&session.thread_id.to_string(),
call_id,
);
let _ = std::fs::remove_file(&expected_saved_path);
let item = ResponseItem::ImageGenerationCall {
id: Some(call_id.to_string()),
status: "completed".to_string(),
revised_prompt: Some("broken payload".to_string()),
result: "_-8".to_string(),
internal_chat_message_metadata_passthrough: None,
};

let mut ctx = HandleOutputCtx {
sess: Arc::clone(&session),
turn_context: Arc::clone(&turn_context),
turn_store: Arc::new(codex_extension_api::ExtensionData::new(
turn_context.sub_id.clone(),
)),
tool_runtime: test_tool_runtime(Arc::clone(&session), Arc::clone(&turn_context)),
cancellation_token: CancellationToken::new(),
};
handle_output_item_done(&mut ctx, item.clone(), /*previously_active_item*/ None)
.await
.expect("image generation item should still complete");

let history = session.clone_history().await;
let expected = vec![item];
assert_eq!(strip_metadata_from_items(history.raw_items()), expected);
assert!(!expected_saved_path.exists());
}

#[tokio::test]
async fn build_initial_context_uses_previous_turn_settings_for_realtime_end() {
let (session, turn_context) = make_session_and_context().await;
Expand Down
2 changes: 0 additions & 2 deletions codex-rs/core/src/session/turn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1853,7 +1853,6 @@ async fn handle_assistant_item_done_in_plan_mode(
let mut finalized_facts = None;
if let Some(finalized_turn_item) = finalize_non_tool_response_item(
sess,
turn_context,
TurnItemContributorPolicy::Run(turn_store),
item,
/*plan_mode*/ true,
Expand Down Expand Up @@ -2141,7 +2140,6 @@ async fn try_run_sampling_request(
}
if let Some(turn_item) = handle_non_tool_response_item(
sess.as_ref(),
turn_context.as_ref(),
TurnItemContributorPolicy::Skip,
&item,
plan_mode,
Expand Down
Loading
Loading