mirror of
https://github.com/openai/codex.git
synced 2026-10-21 09:54:56 +00:00
Gate reasoning effort updates on explicit model support (#46530)
## Why Using Responses Lite does not establish whether a model accepts reasoning-effort `configuration_update` items. Unsupported models need to use the selected request-level effort, including when resuming history containing saved updates. ## What changed - Add `supports_reasoning_effort_updates` model metadata, defaulting to `false`. - Require the override feature, an OpenAI provider, and explicit model support for reasoning effort updates, independently of `use_responses_lite`. - Filter saved configuration updates from unsupported requests without changing persisted history. - Clear the pinned effort baseline when sampling with an unsupported model so returning to a supported model establishes a fresh baseline. Compaction uses the selected effort without clearing the pin. ## Testing Add coverage for supported models with either Responses mode, unsupported-model history filtering over HTTP and WebSocket, switching to an unsupported model and back, and compaction versus sampling pin behavior. Verify missing capability metadata defaults to `false`. GitOrigin-RevId: 8a0fbc141964bd854dbcdd6f31e1c8e97697cf22
This commit is contained in:
@@ -67,6 +67,7 @@ fn preset_to_info(preset: &ModelPreset, priority: i32) -> ModelInfo {
|
||||
supports_search_tool: false,
|
||||
supports_experimental_context: false,
|
||||
use_responses_lite: false,
|
||||
supports_reasoning_effort_updates: false,
|
||||
guardian: None,
|
||||
node_repl_auto_review_required: false,
|
||||
node_repl_disabled: false,
|
||||
|
||||
@@ -155,6 +155,7 @@ async fn models_client_hits_models_endpoint() {
|
||||
supports_search_tool: false,
|
||||
supports_experimental_context: false,
|
||||
use_responses_lite: false,
|
||||
supports_reasoning_effort_updates: true,
|
||||
guardian: None,
|
||||
node_repl_auto_review_required: true,
|
||||
node_repl_disabled: true,
|
||||
|
||||
@@ -528,8 +528,10 @@ impl ModelClient {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn reasoning_effort_override_enabled(&self) -> bool {
|
||||
pub(crate) fn reasoning_effort_override_enabled(&self, model_info: &ModelInfo) -> bool {
|
||||
self.state.reasoning_effort_override_enabled
|
||||
&& self.state.provider.info().is_openai()
|
||||
&& model_info.supports_reasoning_effort_updates
|
||||
}
|
||||
|
||||
pub(crate) fn with_restored_history(mut self, restored_history: bool) -> Self {
|
||||
@@ -867,8 +869,8 @@ impl ModelClient {
|
||||
responses_metadata: &CodexResponsesMetadata,
|
||||
) -> Result<ResponsesApiRequest> {
|
||||
let mut input = prompt.get_formatted_input_for_request(model_info);
|
||||
if !self.state.reasoning_effort_override_enabled {
|
||||
// Disabling overrides must also recover threads with saved updates.
|
||||
if !self.reasoning_effort_override_enabled(model_info) {
|
||||
// Unsupported models and disabled overrides must also accept saved history.
|
||||
// Filter only the request copy; persisted history remains unchanged.
|
||||
input.retain(|item| !matches!(item, ResponseItem::ConfigurationUpdate { .. }));
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
//! baseline while it is retained.
|
||||
//! Successful compaction retires the overrides and allows a fresh request baseline.
|
||||
//! Synchronous Guardian reviewers always use their selected request-level effort.
|
||||
//! Unsupported models use selected request effort without rewriting saved updates.
|
||||
|
||||
use super::session::Session;
|
||||
use super::step_context::StepContext;
|
||||
@@ -26,7 +27,7 @@ impl Session {
|
||||
/// Establishes the selected effort in surviving history, independent of replayed settings.
|
||||
pub(crate) async fn record_reasoning_effort_override(&self, step_context: &StepContext) {
|
||||
let settings = &step_context.settings;
|
||||
let Some(effort) = self.effort_for_configuration_update(settings).await else {
|
||||
let Some(effort) = self.effort_for_configuration_update(settings) else {
|
||||
return;
|
||||
};
|
||||
let should_skip = {
|
||||
@@ -99,8 +100,11 @@ impl Session {
|
||||
if !self
|
||||
.services
|
||||
.model_client
|
||||
.reasoning_effort_override_enabled()
|
||||
.reasoning_effort_override_enabled(&settings.model_info)
|
||||
{
|
||||
if usage == RequestEffortUsage::Sampling {
|
||||
self.state.lock().await.reasoning_effort_pin = ReasoningEffortPin::Unset;
|
||||
}
|
||||
return selected_effort;
|
||||
}
|
||||
if usage == RequestEffortUsage::Compaction
|
||||
@@ -113,7 +117,7 @@ impl Session {
|
||||
{
|
||||
return Some(pinned);
|
||||
}
|
||||
let effort = self.effort_for_configuration_update(settings).await;
|
||||
let effort = self.effort_for_configuration_update(settings);
|
||||
let mut state = self.state.lock().await;
|
||||
let Some(effort) = effort else {
|
||||
if usage == RequestEffortUsage::Sampling {
|
||||
@@ -130,16 +134,14 @@ impl Session {
|
||||
})
|
||||
}
|
||||
|
||||
async fn effort_for_configuration_update(
|
||||
fn effort_for_configuration_update(
|
||||
&self,
|
||||
settings: &ResolvedStepSettings,
|
||||
) -> Option<ReasoningEffort> {
|
||||
if !self
|
||||
.services
|
||||
.model_client
|
||||
.reasoning_effort_override_enabled()
|
||||
|| !settings.model_info.use_responses_lite
|
||||
|| !self.provider().await.is_openai()
|
||||
.reasoning_effort_override_enabled(&settings.model_info)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ async fn initial_replay_preserves_prewarmed_effort(history: InitialHistory) {
|
||||
)
|
||||
.await;
|
||||
let mut model_info = Arc::clone(&turn_context.initial_settings.model_info);
|
||||
Arc::make_mut(&mut model_info).use_responses_lite = true;
|
||||
Arc::make_mut(&mut model_info).supports_reasoning_effort_updates = true;
|
||||
let mut selected = turn_context.initial_settings.selected().clone();
|
||||
selected.collaboration_mode.settings.reasoning_effort = Some(ReasoningEffort::Medium);
|
||||
let prewarm_settings = ResolvedStepSettings::new(
|
||||
@@ -87,7 +87,7 @@ async fn compaction_effort_lookup_preserves_pin_for_fallback_models() {
|
||||
let effort = ReasoningEffort::Medium;
|
||||
let model = Arc::make_mut(&mut settings.model_info);
|
||||
model.slug = "fallback".to_string();
|
||||
model.use_responses_lite = true;
|
||||
model.supports_reasoning_effort_updates = true;
|
||||
model.default_reasoning_level = Some(effort.clone());
|
||||
|
||||
assert_eq!(
|
||||
@@ -106,3 +106,57 @@ async fn compaction_effort_lookup_preserves_pin_for_fallback_models() {
|
||||
Some(ReasoningEffort::Low)
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn unsupported_model_compaction_uses_selected_effort_without_mutating_pin() {
|
||||
let (session, turn_context, _events) = make_session_and_context_with_auth_and_config_and_rx(
|
||||
CodexAuth::from_api_key("Test API Key"),
|
||||
Vec::new(),
|
||||
|config| {
|
||||
config
|
||||
.features
|
||||
.enable(Feature::ReasoningEffortOverride)
|
||||
.unwrap();
|
||||
config.model_reasoning_effort = Some(ReasoningEffort::High);
|
||||
},
|
||||
)
|
||||
.await;
|
||||
let mut settings = (*turn_context.initial_settings).clone();
|
||||
Arc::make_mut(&mut settings.model_info).supports_reasoning_effort_updates = false;
|
||||
session
|
||||
.state
|
||||
.lock()
|
||||
.await
|
||||
.reasoning_effort_pin
|
||||
.pin(&settings.model_info.slug, ReasoningEffort::Low);
|
||||
|
||||
assert_eq!(
|
||||
session
|
||||
.reasoning_effort_for_request(&settings, RequestEffortUsage::Compaction)
|
||||
.await,
|
||||
Some(ReasoningEffort::High),
|
||||
);
|
||||
assert_eq!(
|
||||
session
|
||||
.state
|
||||
.lock()
|
||||
.await
|
||||
.reasoning_effort_pin
|
||||
.get(&settings.model_info.slug),
|
||||
Some(ReasoningEffort::Low),
|
||||
);
|
||||
// Sampling retires the old baseline, so a later supported request starts afresh.
|
||||
assert_eq!(
|
||||
session
|
||||
.reasoning_effort_for_request(&settings, RequestEffortUsage::Sampling)
|
||||
.await,
|
||||
Some(ReasoningEffort::High),
|
||||
);
|
||||
Arc::make_mut(&mut settings.model_info).supports_reasoning_effort_updates = true;
|
||||
assert_eq!(
|
||||
session
|
||||
.reasoning_effort_for_request(&settings, RequestEffortUsage::Sampling)
|
||||
.await,
|
||||
Some(ReasoningEffort::High),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -615,6 +615,7 @@ fn remote_model_with_auto_review_override(slug: &str, review_model: &str) -> Mod
|
||||
supports_search_tool: false,
|
||||
supports_experimental_context: false,
|
||||
use_responses_lite: false,
|
||||
supports_reasoning_effort_updates: false,
|
||||
node_repl_auto_review_required: false,
|
||||
node_repl_disabled: false,
|
||||
auto_review_model_override: Some(review_model.to_string()),
|
||||
|
||||
@@ -1038,6 +1038,7 @@ async fn reasoning_effort_override_remote_v2_compaction_resets_pinned_effort(
|
||||
.with_auth(CodexAuth::create_dummy_chatgpt_auth_for_testing())
|
||||
.with_model_info_override("gpt-5.4", |model| {
|
||||
model.use_responses_lite = true;
|
||||
model.supports_reasoning_effort_updates = true;
|
||||
})
|
||||
.with_config(move |config| {
|
||||
config.model_provider.stream_max_retries = Some(0);
|
||||
|
||||
@@ -126,6 +126,7 @@ fn test_model_info(
|
||||
supports_search_tool: false,
|
||||
supports_experimental_context: false,
|
||||
use_responses_lite: false,
|
||||
supports_reasoning_effort_updates: false,
|
||||
guardian: None,
|
||||
node_repl_auto_review_required: false,
|
||||
node_repl_disabled: false,
|
||||
@@ -1253,6 +1254,7 @@ async fn model_switch_to_smaller_model_updates_token_context_window() -> Result<
|
||||
supports_search_tool: false,
|
||||
supports_experimental_context: false,
|
||||
use_responses_lite: false,
|
||||
supports_reasoning_effort_updates: false,
|
||||
guardian: None,
|
||||
node_repl_auto_review_required: false,
|
||||
node_repl_disabled: false,
|
||||
|
||||
@@ -679,6 +679,7 @@ fn test_remote_model(slug: &str, priority: i32) -> ModelInfo {
|
||||
supports_search_tool: false,
|
||||
supports_experimental_context: false,
|
||||
use_responses_lite: false,
|
||||
supports_reasoning_effort_updates: false,
|
||||
guardian: None,
|
||||
node_repl_auto_review_required: false,
|
||||
node_repl_disabled: false,
|
||||
|
||||
@@ -43,6 +43,7 @@ fn override_builder() -> TestCodexBuilder {
|
||||
test_codex()
|
||||
.with_model_info_override("gpt-5.4", |model| {
|
||||
model.use_responses_lite = true;
|
||||
model.supports_reasoning_effort_updates = true;
|
||||
})
|
||||
.with_config(|config| {
|
||||
config
|
||||
@@ -354,9 +355,12 @@ async fn reasoning_effort_override_persistent_transitions(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test_case(true; "responses lite")]
|
||||
#[test_case(false; "ordinary responses")]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn reasoning_effort_override_preserves_prefix_and_only_appends_on_change()
|
||||
-> anyhow::Result<()> {
|
||||
async fn reasoning_effort_override_preserves_prefix_and_only_appends_on_change(
|
||||
use_responses_lite: bool,
|
||||
) -> anyhow::Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
let server = responses::start_mock_server().await;
|
||||
let mut mocks = Vec::new();
|
||||
@@ -366,7 +370,12 @@ async fn reasoning_effort_override_preserves_prefix_and_only_appends_on_change()
|
||||
.await,
|
||||
);
|
||||
}
|
||||
let test = override_builder().build_with_auto_env(&server).await?;
|
||||
let test = override_builder()
|
||||
.with_model_info_override("gpt-5.4", move |model| {
|
||||
model.use_responses_lite = use_responses_lite;
|
||||
})
|
||||
.build_with_auto_env(&server)
|
||||
.await?;
|
||||
test.submit_text_turn("first message").await?;
|
||||
submit_thread_settings(
|
||||
&test.codex,
|
||||
@@ -718,8 +727,12 @@ async fn reasoning_effort_override_websocket_appends_then_replays_after_reconnec
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test_case(OverrideUnavailable::FeatureDisabled; "feature disabled")]
|
||||
#[test_case(OverrideUnavailable::ModelUnsupported; "unsupported model")]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn reasoning_effort_override_disabled_filters_websocket_history() -> anyhow::Result<()> {
|
||||
async fn reasoning_effort_override_unavailable_filters_websocket_history(
|
||||
unavailable: OverrideUnavailable,
|
||||
) -> anyhow::Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
let server = responses::start_mock_server().await;
|
||||
let initial_mock = responses::mount_sse_once(
|
||||
@@ -747,11 +760,7 @@ async fn reasoning_effort_override_disabled_filters_websocket_history() -> anyho
|
||||
]])
|
||||
.await;
|
||||
let base_url = format!("{}/v1", websocket.uri());
|
||||
let mut builder = override_builder().with_config(move |config| {
|
||||
config
|
||||
.features
|
||||
.disable(Feature::ReasoningEffortOverride)
|
||||
.expect("disable overrides");
|
||||
let mut builder = unavailable.builder().with_config(move |config| {
|
||||
config.model_reasoning_effort = Some(ReasoningEffort::High);
|
||||
config.model_provider.base_url = Some(base_url);
|
||||
config.model_provider.supports_websockets = true;
|
||||
@@ -788,12 +797,33 @@ async fn reasoning_effort_override_disabled_filters_websocket_history() -> anyho
|
||||
enum OverrideUnavailable {
|
||||
FeatureDisabled,
|
||||
NonOpenAiProvider,
|
||||
ResponsesLiteDisabled,
|
||||
ModelUnsupported,
|
||||
}
|
||||
|
||||
impl OverrideUnavailable {
|
||||
fn builder(self) -> TestCodexBuilder {
|
||||
match self {
|
||||
Self::FeatureDisabled => override_builder().with_config(|config| {
|
||||
config
|
||||
.features
|
||||
.disable(Feature::ReasoningEffortOverride)
|
||||
.expect("disable overrides");
|
||||
}),
|
||||
Self::NonOpenAiProvider => override_builder().with_config(|config| {
|
||||
config.model_provider.name = "unsupported provider".into();
|
||||
}),
|
||||
Self::ModelUnsupported => {
|
||||
override_builder().with_model_info_override("gpt-5.4", |model| {
|
||||
model.supports_reasoning_effort_updates = false;
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test_case(OverrideUnavailable::FeatureDisabled; "feature disabled")]
|
||||
#[test_case(OverrideUnavailable::NonOpenAiProvider; "unsupported provider")]
|
||||
#[test_case(OverrideUnavailable::ResponsesLiteDisabled; "responses lite disabled")]
|
||||
#[test_case(OverrideUnavailable::ModelUnsupported; "unsupported model")]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn reasoning_effort_override_unavailable_uses_request_effort(
|
||||
unavailable: OverrideUnavailable,
|
||||
@@ -810,20 +840,7 @@ async fn reasoning_effort_override_unavailable_uses_request_effort(
|
||||
responses::sse(vec![responses::ev_completed("second")]),
|
||||
)
|
||||
.await;
|
||||
let mut builder = match unavailable {
|
||||
OverrideUnavailable::FeatureDisabled => override_builder().with_config(|config| {
|
||||
config
|
||||
.features
|
||||
.disable(Feature::ReasoningEffortOverride)
|
||||
.expect("disable overrides");
|
||||
}),
|
||||
OverrideUnavailable::NonOpenAiProvider => override_builder().with_config(|config| {
|
||||
config.model_provider.name = "unsupported provider".into();
|
||||
}),
|
||||
OverrideUnavailable::ResponsesLiteDisabled => override_builder()
|
||||
.with_model_info_override("gpt-5.4", |model| model.use_responses_lite = false),
|
||||
};
|
||||
let test = builder.build_with_auto_env(&server).await?;
|
||||
let test = unavailable.builder().build_with_auto_env(&server).await?;
|
||||
test.submit_text_turn("first").await?;
|
||||
submit_thread_settings(
|
||||
&test.codex,
|
||||
@@ -846,8 +863,12 @@ async fn reasoning_effort_override_unavailable_uses_request_effort(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test_case(OverrideUnavailable::FeatureDisabled; "feature disabled")]
|
||||
#[test_case(OverrideUnavailable::ModelUnsupported; "unsupported model")]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn reasoning_effort_override_disabled_recovers_saved_history() -> anyhow::Result<()> {
|
||||
async fn reasoning_effort_override_unavailable_recovers_saved_history(
|
||||
unavailable: OverrideUnavailable,
|
||||
) -> anyhow::Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
let server = responses::start_mock_server().await;
|
||||
let reply = |id, text| {
|
||||
@@ -891,13 +912,10 @@ async fn reasoning_effort_override_disabled_recovers_saved_history() -> anyhow::
|
||||
})
|
||||
.await;
|
||||
let chatgpt_base_url = format!("{}/backend-api", server.uri());
|
||||
let mut builder = override_builder()
|
||||
let mut builder = unavailable
|
||||
.builder()
|
||||
.with_auth(CodexAuth::create_dummy_chatgpt_auth_for_testing())
|
||||
.with_config(move |config| {
|
||||
config
|
||||
.features
|
||||
.disable(Feature::ReasoningEffortOverride)
|
||||
.expect("disable overrides");
|
||||
config.model_reasoning_effort = Some(ReasoningEffort::High);
|
||||
config.chatgpt_base_url = chatgpt_base_url;
|
||||
});
|
||||
@@ -984,6 +1002,7 @@ async fn reasoning_effort_override_model_switch_reestablishes_selected_effort(
|
||||
.with_model_info_override("gpt-5.5", |model| {
|
||||
model.comp_hash = None;
|
||||
model.use_responses_lite = true;
|
||||
model.supports_reasoning_effort_updates = true;
|
||||
})
|
||||
.with_model("gpt-5.4")
|
||||
.build_with_auto_env(&server)
|
||||
@@ -1021,6 +1040,145 @@ async fn reasoning_effort_override_model_switch_reestablishes_selected_effort(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn reasoning_effort_override_unsupported_model_round_trip() -> anyhow::Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
let server = responses::start_mock_server().await;
|
||||
let test = override_builder()
|
||||
.with_model_info_override("gpt-6-astra", |model| {
|
||||
model.comp_hash = None;
|
||||
model.use_responses_lite = true;
|
||||
model.supports_reasoning_effort_updates = true;
|
||||
})
|
||||
.with_model_info_override("gpt-5.5", |model| {
|
||||
model.comp_hash = None;
|
||||
// Lite alone is not sufficient: unsupported models must still filter updates.
|
||||
model.use_responses_lite = true;
|
||||
model.supports_reasoning_effort_updates = false;
|
||||
})
|
||||
.with_model("gpt-6-astra")
|
||||
.build_with_auto_env(&server)
|
||||
.await?;
|
||||
let turns = [
|
||||
("gpt-6-astra", ReasoningEffort::Medium, "Start at medium."),
|
||||
(
|
||||
"gpt-6-astra",
|
||||
ReasoningEffort::High,
|
||||
"Raise effort to high.",
|
||||
),
|
||||
(
|
||||
"gpt-5.5",
|
||||
ReasoningEffort::High,
|
||||
"Switch to the unsupported model.",
|
||||
),
|
||||
("gpt-5.5", ReasoningEffort::Low, "Lower effort to low."),
|
||||
(
|
||||
"gpt-6-astra",
|
||||
ReasoningEffort::Low,
|
||||
"Return to the supported model.",
|
||||
),
|
||||
("gpt-6-astra", ReasoningEffort::High, "Raise effort again."),
|
||||
];
|
||||
let mut requests = Vec::new();
|
||||
for (index, (model, effort, prompt)) in turns.iter().enumerate() {
|
||||
let id = format!("turn-{index}");
|
||||
let mock = responses::mount_sse_once(
|
||||
&server,
|
||||
responses::sse(vec![
|
||||
responses::ev_assistant_message(&id, "Acknowledged."),
|
||||
responses::ev_completed(&id),
|
||||
]),
|
||||
)
|
||||
.await;
|
||||
submit_thread_settings(
|
||||
&test.codex,
|
||||
ThreadSettingsOverrides {
|
||||
model: Some((*model).to_string()),
|
||||
effort: Some(Some(effort.clone())),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
test.submit_text_turn(prompt).await?;
|
||||
requests.push(mock.single_request());
|
||||
}
|
||||
let initial_updates = vec![
|
||||
effort_update(ReasoningEffort::Medium),
|
||||
effort_update(ReasoningEffort::High),
|
||||
];
|
||||
let mut returned_updates = initial_updates.clone();
|
||||
returned_updates.push(effort_update(ReasoningEffort::Low));
|
||||
let mut final_updates = returned_updates.clone();
|
||||
final_updates.push(effort_update(ReasoningEffort::High));
|
||||
assert_eq!(
|
||||
requests
|
||||
.iter()
|
||||
.map(|request| {
|
||||
let body = request.body_json();
|
||||
(
|
||||
body["model"].clone(),
|
||||
body["reasoning"]["effort"].clone(),
|
||||
effort_updates(request),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
vec![
|
||||
(
|
||||
Value::from("gpt-6-astra"),
|
||||
Value::from("medium"),
|
||||
vec![effort_update(ReasoningEffort::Medium)]
|
||||
),
|
||||
(
|
||||
Value::from("gpt-6-astra"),
|
||||
Value::from("medium"),
|
||||
initial_updates
|
||||
),
|
||||
(Value::from("gpt-5.5"), Value::from("high"), vec![]),
|
||||
(Value::from("gpt-5.5"), Value::from("low"), vec![]),
|
||||
(
|
||||
Value::from("gpt-6-astra"),
|
||||
Value::from("low"),
|
||||
returned_updates
|
||||
),
|
||||
(
|
||||
Value::from("gpt-6-astra"),
|
||||
Value::from("low"),
|
||||
final_updates.clone()
|
||||
),
|
||||
],
|
||||
);
|
||||
for (index, request) in requests.iter().enumerate() {
|
||||
assert_eq!(
|
||||
request
|
||||
.message_input_texts("user")
|
||||
.into_iter()
|
||||
.filter(|text| { turns.iter().any(|(_, _, prompt)| text.as_str() == *prompt) })
|
||||
.collect::<Vec<_>>(),
|
||||
turns[..=index]
|
||||
.iter()
|
||||
.map(|(_, _, prompt)| prompt.to_string())
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
}
|
||||
test.codex.shutdown_and_wait().await?;
|
||||
let saved_updates = test
|
||||
.codex
|
||||
.load_history(/*include_archived*/ false)
|
||||
.await?
|
||||
.items
|
||||
.into_iter()
|
||||
.filter_map(|item| match item {
|
||||
RolloutItem::ResponseItem(ResponseItemEnvelope {
|
||||
item: item @ ResponseItem::ConfigurationUpdate { .. },
|
||||
..
|
||||
}) => Some(serde_json::to_value(item).expect("serialize saved update")),
|
||||
_ => None,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(saved_updates, final_updates);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test_case(ReasoningEffort::High; "unchanged effort")]
|
||||
#[test_case(ReasoningEffort::Medium; "changed effort")]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
@@ -1129,6 +1287,7 @@ async fn reasoning_effort_override_compaction_fallback_uses_each_models_effort()
|
||||
.with_model_info_override("gpt-5.2", |model| {
|
||||
model.comp_hash = Some("fallback".to_string());
|
||||
model.use_responses_lite = true;
|
||||
model.supports_reasoning_effort_updates = true;
|
||||
})
|
||||
.with_model("gpt-5.4")
|
||||
.with_config(move |config| {
|
||||
|
||||
@@ -584,6 +584,7 @@ async fn remote_models_remote_model_uses_unified_exec() -> Result<()> {
|
||||
supports_search_tool: false,
|
||||
supports_experimental_context: false,
|
||||
use_responses_lite: false,
|
||||
supports_reasoning_effort_updates: false,
|
||||
guardian: None,
|
||||
node_repl_auto_review_required: false,
|
||||
node_repl_disabled: false,
|
||||
@@ -858,6 +859,7 @@ async fn remote_models_apply_legacy_instructions(auth: CodexAuth) -> Result<()>
|
||||
supports_search_tool: false,
|
||||
supports_experimental_context: false,
|
||||
use_responses_lite: false,
|
||||
supports_reasoning_effort_updates: false,
|
||||
guardian: None,
|
||||
node_repl_auto_review_required: false,
|
||||
node_repl_disabled: false,
|
||||
@@ -1440,6 +1442,7 @@ fn test_remote_model_with_policy(
|
||||
supports_search_tool: false,
|
||||
supports_experimental_context: false,
|
||||
use_responses_lite: false,
|
||||
supports_reasoning_effort_updates: false,
|
||||
guardian: None,
|
||||
node_repl_auto_review_required: false,
|
||||
node_repl_disabled: false,
|
||||
|
||||
@@ -3008,6 +3008,7 @@ async fn stdio_image_responses_are_sanitized_for_text_only_model() -> anyhow::Re
|
||||
supports_search_tool: false,
|
||||
supports_experimental_context: false,
|
||||
use_responses_lite: false,
|
||||
supports_reasoning_effort_updates: false,
|
||||
guardian: None,
|
||||
node_repl_auto_review_required: false,
|
||||
node_repl_disabled: false,
|
||||
|
||||
@@ -92,6 +92,7 @@ fn test_model_info(
|
||||
supports_search_tool: false,
|
||||
supports_experimental_context: false,
|
||||
use_responses_lite: false,
|
||||
supports_reasoning_effort_updates: false,
|
||||
guardian: None,
|
||||
node_repl_auto_review_required: false,
|
||||
node_repl_disabled: false,
|
||||
|
||||
@@ -1624,6 +1624,7 @@ async fn view_image_tool_returns_unsupported_message_for_text_only_model() -> an
|
||||
supports_search_tool: false,
|
||||
supports_experimental_context: false,
|
||||
use_responses_lite: false,
|
||||
supports_reasoning_effort_updates: false,
|
||||
guardian: None,
|
||||
node_repl_auto_review_required: false,
|
||||
node_repl_disabled: false,
|
||||
|
||||
@@ -137,6 +137,7 @@ pub fn model_info_from_slug(slug: &str) -> ModelInfo {
|
||||
supports_search_tool: false,
|
||||
supports_experimental_context: false,
|
||||
use_responses_lite: false,
|
||||
supports_reasoning_effort_updates: false,
|
||||
guardian: None,
|
||||
node_repl_auto_review_required: false,
|
||||
node_repl_disabled: false,
|
||||
|
||||
@@ -48,6 +48,7 @@ fn test_model(model_messages: Option<ModelMessages>) -> ModelInfo {
|
||||
supports_search_tool: false,
|
||||
supports_experimental_context: false,
|
||||
use_responses_lite: false,
|
||||
supports_reasoning_effort_updates: false,
|
||||
guardian: None,
|
||||
node_repl_auto_review_required: false,
|
||||
node_repl_disabled: false,
|
||||
|
||||
@@ -481,6 +481,10 @@ pub struct ModelInfo {
|
||||
pub supports_experimental_context: bool,
|
||||
#[serde(default)]
|
||||
pub use_responses_lite: bool,
|
||||
/// Whether the model accepts reasoning-effort `configuration_update` items.
|
||||
/// Missing metadata keeps effort changes on the ordinary request parameter.
|
||||
#[serde(default)]
|
||||
pub supports_reasoning_effort_updates: bool,
|
||||
#[serde(default)]
|
||||
pub node_repl_auto_review_required: bool,
|
||||
#[serde(default)]
|
||||
@@ -989,6 +993,7 @@ mod tests {
|
||||
supports_search_tool: false,
|
||||
supports_experimental_context: false,
|
||||
use_responses_lite: false,
|
||||
supports_reasoning_effort_updates: false,
|
||||
guardian: None,
|
||||
node_repl_auto_review_required: false,
|
||||
node_repl_disabled: false,
|
||||
@@ -1543,6 +1548,7 @@ mod tests {
|
||||
assert!(!model.supports_search_tool);
|
||||
assert!(!model.supports_experimental_context);
|
||||
assert!(!model.use_responses_lite);
|
||||
assert!(!model.supports_reasoning_effort_updates);
|
||||
assert!(!model.node_repl_auto_review_required);
|
||||
assert!(!model.node_repl_disabled);
|
||||
assert_eq!(model.comp_hash, None);
|
||||
|
||||
Reference in New Issue
Block a user