mirror of
https://github.com/openai/codex.git
synced 2026-05-23 12:34:25 +00:00
## Why The client and tool pipeline still carried compatibility code for legacy structured shell output. Current shell and apply_patch responses are already plain text for model consumption, so keeping a JSON-serialization path plus shell-item rewrite logic makes the request formatter and tests preserve a format we do not need anymore. ## What Changed - Removed the client-side shell output rewrite from `core/src/client_common.rs`. - Removed the structured exec-output formatter and the shell `freeform` switch so tool emitters use one model-facing formatter. - Collapsed apply_patch/shell serialization tests around the remaining plain-text output expectations and removed duplicate one-variant parameterized cases. - Kept the `ApplyPatchModelOutput::ShellCommandViaHeredoc` compatibility input shape, but no longer treats it as a separate output-format mode. ## Validation - `cargo test -p codex-core client_common` - `cargo test -p codex-core shell_serialization` - `cargo test -p codex-core apply_patch_cli` - `just fix -p codex-core` ## Documentation No external Codex documentation update is needed.
168 lines
4.6 KiB
Rust
168 lines
4.6 KiB
Rust
use codex_api::OpenAiVerbosity;
|
|
use codex_api::ResponsesApiRequest;
|
|
use codex_api::TextControls;
|
|
use codex_api::create_text_param_for_request;
|
|
use codex_protocol::config_types::ServiceTier;
|
|
use pretty_assertions::assert_eq;
|
|
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn serializes_text_verbosity_when_set() {
|
|
let input: Vec<ResponseItem> = vec![];
|
|
let tools: Vec<serde_json::Value> = vec![];
|
|
let req = ResponsesApiRequest {
|
|
model: "gpt-5.4".to_string(),
|
|
instructions: "i".to_string(),
|
|
input,
|
|
tools,
|
|
tool_choice: "auto".to_string(),
|
|
parallel_tool_calls: true,
|
|
reasoning: None,
|
|
store: false,
|
|
stream: true,
|
|
include: vec![],
|
|
prompt_cache_key: None,
|
|
service_tier: None,
|
|
text: Some(TextControls {
|
|
verbosity: Some(OpenAiVerbosity::Low),
|
|
format: None,
|
|
}),
|
|
client_metadata: None,
|
|
};
|
|
|
|
let v = serde_json::to_value(&req).expect("json");
|
|
assert_eq!(
|
|
v.get("text")
|
|
.and_then(|t| t.get("verbosity"))
|
|
.and_then(|s| s.as_str()),
|
|
Some("low")
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn serializes_text_schema_with_strict_format() {
|
|
let input: Vec<ResponseItem> = vec![];
|
|
let tools: Vec<serde_json::Value> = vec![];
|
|
let schema = serde_json::json!({
|
|
"type": "object",
|
|
"properties": {
|
|
"answer": {"type": "string"}
|
|
},
|
|
"required": ["answer"],
|
|
});
|
|
let text_controls = create_text_param_for_request(
|
|
/*verbosity*/ None,
|
|
&Some(schema.clone()),
|
|
/*output_schema_strict*/ true,
|
|
)
|
|
.expect("text controls");
|
|
|
|
let req = ResponsesApiRequest {
|
|
model: "gpt-5.4".to_string(),
|
|
instructions: "i".to_string(),
|
|
input,
|
|
tools,
|
|
tool_choice: "auto".to_string(),
|
|
parallel_tool_calls: true,
|
|
reasoning: None,
|
|
store: false,
|
|
stream: true,
|
|
include: vec![],
|
|
prompt_cache_key: None,
|
|
service_tier: None,
|
|
text: Some(text_controls),
|
|
client_metadata: None,
|
|
};
|
|
|
|
let v = serde_json::to_value(&req).expect("json");
|
|
let text = v.get("text").expect("text field");
|
|
assert!(text.get("verbosity").is_none());
|
|
let format = text.get("format").expect("format field");
|
|
|
|
assert_eq!(
|
|
format.get("name"),
|
|
Some(&serde_json::Value::String("codex_output_schema".into()))
|
|
);
|
|
assert_eq!(
|
|
format.get("type"),
|
|
Some(&serde_json::Value::String("json_schema".into()))
|
|
);
|
|
assert_eq!(format.get("strict"), Some(&serde_json::Value::Bool(true)));
|
|
assert_eq!(format.get("schema"), Some(&schema));
|
|
}
|
|
|
|
#[test]
|
|
fn serializes_text_schema_with_non_strict_format() {
|
|
let schema = serde_json::json!({
|
|
"type": "object",
|
|
"properties": {
|
|
"answer": {"type": "string"},
|
|
"rationale": {"type": "string"}
|
|
},
|
|
"required": ["answer"],
|
|
"additionalProperties": false
|
|
});
|
|
let text_controls = create_text_param_for_request(
|
|
/*verbosity*/ None,
|
|
&Some(schema.clone()),
|
|
/*output_schema_strict*/ false,
|
|
)
|
|
.expect("text controls");
|
|
|
|
let format = text_controls.format.expect("format field");
|
|
assert!(!format.strict);
|
|
assert_eq!(format.schema, schema);
|
|
}
|
|
|
|
#[test]
|
|
fn omits_text_when_not_set() {
|
|
let input: Vec<ResponseItem> = vec![];
|
|
let tools: Vec<serde_json::Value> = vec![];
|
|
let req = ResponsesApiRequest {
|
|
model: "gpt-5.4".to_string(),
|
|
instructions: "i".to_string(),
|
|
input,
|
|
tools,
|
|
tool_choice: "auto".to_string(),
|
|
parallel_tool_calls: true,
|
|
reasoning: None,
|
|
store: false,
|
|
stream: true,
|
|
include: vec![],
|
|
prompt_cache_key: None,
|
|
service_tier: None,
|
|
text: None,
|
|
client_metadata: None,
|
|
};
|
|
|
|
let v = serde_json::to_value(&req).expect("json");
|
|
assert!(v.get("text").is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn serializes_flex_service_tier_when_set() {
|
|
let req = ResponsesApiRequest {
|
|
model: "gpt-5.4".to_string(),
|
|
instructions: "i".to_string(),
|
|
input: vec![],
|
|
tools: vec![],
|
|
tool_choice: "auto".to_string(),
|
|
parallel_tool_calls: true,
|
|
reasoning: None,
|
|
store: false,
|
|
stream: true,
|
|
include: vec![],
|
|
prompt_cache_key: None,
|
|
service_tier: Some(ServiceTier::Flex.to_string()),
|
|
text: None,
|
|
client_metadata: None,
|
|
};
|
|
|
|
let v = serde_json::to_value(&req).expect("json");
|
|
assert_eq!(
|
|
v.get("service_tier").and_then(|tier| tier.as_str()),
|
|
Some("flex")
|
|
);
|
|
}
|