mirror of
https://github.com/openai/codex.git
synced 2026-05-05 13:51:29 +03:00
Always enable original image detail on supported models (#17665)
## Summary This PR removes `image_detail_original` as a runtime experiment and makes original image detail available whenever the selected model supports it. Concretely, this change: - drops the `image_detail_original` feature flag from the feature registry and generated config schema - makes tool-emitted image detail depend only on `ModelInfo.supports_image_detail_original` - updates `view_image` and `code_mode`/`js_repl` image emission to use that capability check directly - removes now-redundant experiment-specific tests and instruction coverage - keeps backward compatibility for existing configs by silently ignoring a stale `features.image_detail_original` entry The net effect is that `detail: "original"` is always available on supported models, without requiring an experiment toggle.
This commit is contained in:
committed by
GitHub
parent
e6947f85f6
commit
f030ab62eb
@@ -1,19 +1,16 @@
|
||||
use codex_features::Feature;
|
||||
use codex_features::Features;
|
||||
use codex_protocol::models::ImageDetail;
|
||||
use codex_protocol::openai_models::ModelInfo;
|
||||
|
||||
pub fn can_request_original_image_detail(features: &Features, model_info: &ModelInfo) -> bool {
|
||||
model_info.supports_image_detail_original && features.enabled(Feature::ImageDetailOriginal)
|
||||
pub fn can_request_original_image_detail(model_info: &ModelInfo) -> bool {
|
||||
model_info.supports_image_detail_original
|
||||
}
|
||||
|
||||
pub fn normalize_output_image_detail(
|
||||
features: &Features,
|
||||
model_info: &ModelInfo,
|
||||
detail: Option<ImageDetail>,
|
||||
) -> Option<ImageDetail> {
|
||||
match detail {
|
||||
Some(ImageDetail::Original) if can_request_original_image_detail(features, model_info) => {
|
||||
Some(ImageDetail::Original) if can_request_original_image_detail(model_info) => {
|
||||
Some(ImageDetail::Original)
|
||||
}
|
||||
Some(ImageDetail::Original) | Some(_) | None => None,
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
use super::*;
|
||||
use codex_features::Feature;
|
||||
use codex_features::Features;
|
||||
use codex_protocol::models::ImageDetail;
|
||||
use codex_protocol::openai_models::ModelInfo;
|
||||
use pretty_assertions::assert_eq;
|
||||
@@ -42,37 +40,26 @@ fn model_info() -> ModelInfo {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn image_detail_original_feature_enables_explicit_original_without_force() {
|
||||
fn explicit_original_is_allowed_when_model_supports_it() {
|
||||
let model_info = model_info();
|
||||
let mut features = Features::with_defaults();
|
||||
features.enable(Feature::ImageDetailOriginal);
|
||||
|
||||
assert!(can_request_original_image_detail(&features, &model_info));
|
||||
assert!(can_request_original_image_detail(&model_info));
|
||||
assert_eq!(
|
||||
normalize_output_image_detail(&features, &model_info, Some(ImageDetail::Original)),
|
||||
normalize_output_image_detail(&model_info, Some(ImageDetail::Original)),
|
||||
Some(ImageDetail::Original)
|
||||
);
|
||||
assert_eq!(
|
||||
normalize_output_image_detail(&features, &model_info, /*detail*/ None),
|
||||
normalize_output_image_detail(&model_info, /*detail*/ None),
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn explicit_original_is_dropped_without_feature_or_model_support() {
|
||||
fn explicit_original_is_dropped_without_model_support() {
|
||||
let mut model_info = model_info();
|
||||
let features = Features::with_defaults();
|
||||
|
||||
assert_eq!(
|
||||
normalize_output_image_detail(&features, &model_info, Some(ImageDetail::Original)),
|
||||
None
|
||||
);
|
||||
|
||||
let mut features = Features::with_defaults();
|
||||
features.enable(Feature::ImageDetailOriginal);
|
||||
model_info.supports_image_detail_original = false;
|
||||
assert_eq!(
|
||||
normalize_output_image_detail(&features, &model_info, Some(ImageDetail::Original)),
|
||||
normalize_output_image_detail(&model_info, Some(ImageDetail::Original)),
|
||||
None
|
||||
);
|
||||
}
|
||||
@@ -80,11 +67,9 @@ fn explicit_original_is_dropped_without_feature_or_model_support() {
|
||||
#[test]
|
||||
fn unsupported_non_original_detail_is_dropped() {
|
||||
let model_info = model_info();
|
||||
let mut features = Features::with_defaults();
|
||||
features.enable(Feature::ImageDetailOriginal);
|
||||
|
||||
assert_eq!(
|
||||
normalize_output_image_detail(&features, &model_info, Some(ImageDetail::Low)),
|
||||
normalize_output_image_detail(&model_info, Some(ImageDetail::Low)),
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ impl ToolsConfig {
|
||||
let include_tool_suggest = features.enabled(Feature::ToolSuggest)
|
||||
&& features.enabled(Feature::Apps)
|
||||
&& features.enabled(Feature::Plugins);
|
||||
let include_original_image_detail = can_request_original_image_detail(features, model_info);
|
||||
let include_original_image_detail = can_request_original_image_detail(model_info);
|
||||
// API-key auth bypasses Codex backend entitlement/tool normalization, so
|
||||
// callers must confirm ChatGPT auth before exposing the built-in tool.
|
||||
let include_image_gen_tool = *image_generation_tool_auth_allowed
|
||||
|
||||
@@ -362,9 +362,9 @@ fn test_build_specs_enable_fanout_enables_agent_jobs_and_collab_tools() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn view_image_tool_omits_detail_without_original_detail_feature() {
|
||||
fn view_image_tool_omits_detail_without_original_detail_support() {
|
||||
let mut model_info = model_info();
|
||||
model_info.supports_image_detail_original = true;
|
||||
model_info.supports_image_detail_original = false;
|
||||
let features = Features::with_defaults();
|
||||
let available_models = Vec::new();
|
||||
let tools_config = ToolsConfig::new(&ToolsConfigParams {
|
||||
@@ -392,11 +392,10 @@ fn view_image_tool_omits_detail_without_original_detail_feature() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn view_image_tool_includes_detail_with_original_detail_feature() {
|
||||
fn view_image_tool_includes_detail_with_original_detail_support() {
|
||||
let mut model_info = model_info();
|
||||
model_info.supports_image_detail_original = true;
|
||||
let mut features = Features::with_defaults();
|
||||
features.enable(Feature::ImageDetailOriginal);
|
||||
let features = Features::with_defaults();
|
||||
let available_models = Vec::new();
|
||||
let tools_config = ToolsConfig::new(&ToolsConfigParams {
|
||||
model_info: &model_info,
|
||||
|
||||
@@ -53,7 +53,3 @@ fn view_image_output_schema() -> Value {
|
||||
"additionalProperties": false
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "view_image_tests.rs"]
|
||||
mod tests;
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
use super::*;
|
||||
use crate::JsonSchema;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
#[test]
|
||||
fn view_image_tool_omits_detail_without_original_detail_feature() {
|
||||
assert_eq!(
|
||||
create_view_image_tool(ViewImageToolOptions {
|
||||
can_request_original_image_detail: false,
|
||||
}),
|
||||
ToolSpec::Function(ResponsesApiTool {
|
||||
name: "view_image".to_string(),
|
||||
description: "View a local image from the filesystem (only use if given a full filepath by the user, and the image isn't already attached to the thread context within <image ...> tags)."
|
||||
.to_string(),
|
||||
strict: false,
|
||||
defer_loading: None,
|
||||
parameters: JsonSchema::object(BTreeMap::from([(
|
||||
"path".to_string(),
|
||||
JsonSchema::string(Some("Local filesystem path to an image file".to_string()),),
|
||||
)]), Some(vec!["path".to_string()]), Some(false.into())),
|
||||
output_schema: Some(view_image_output_schema()),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn view_image_tool_includes_detail_with_original_detail_feature() {
|
||||
assert_eq!(
|
||||
create_view_image_tool(ViewImageToolOptions {
|
||||
can_request_original_image_detail: true,
|
||||
}),
|
||||
ToolSpec::Function(ResponsesApiTool {
|
||||
name: "view_image".to_string(),
|
||||
description: "View a local image from the filesystem (only use if given a full filepath by the user, and the image isn't already attached to the thread context within <image ...> tags)."
|
||||
.to_string(),
|
||||
strict: false,
|
||||
defer_loading: None,
|
||||
parameters: JsonSchema::object(BTreeMap::from([
|
||||
(
|
||||
"detail".to_string(),
|
||||
JsonSchema::string(Some(
|
||||
"Optional detail override. The only supported value is `original`; omit this field for default resized behavior. Use `original` to preserve the file's original resolution instead of resizing to fit. This is important when high-fidelity image perception or precise localization is needed, especially for CUA agents.".to_string(),
|
||||
),),
|
||||
),
|
||||
(
|
||||
"path".to_string(),
|
||||
JsonSchema::string(Some("Local filesystem path to an image file".to_string()),),
|
||||
),
|
||||
]), Some(vec!["path".to_string()]), Some(false.into())),
|
||||
output_schema: Some(view_image_output_schema()),
|
||||
})
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user