mirror of
https://github.com/openai/codex.git
synced 2026-10-30 19:18:49 +00:00
Persist service tier ids compatibly
This commit is contained in:
@@ -304,7 +304,13 @@ pub struct ConfigToml {
|
||||
/// Optionally specify a personality for the model
|
||||
pub personality: Option<Personality>,
|
||||
|
||||
/// Optional explicit service tier preference for new turns (`fast` or `flex`).
|
||||
/// Optional explicit service tier id preference for new turns.
|
||||
///
|
||||
/// Takes precedence over deprecated `service_tier`.
|
||||
pub service_tier_id: Option<ServiceTier>,
|
||||
|
||||
/// Deprecated legacy service tier preference for new turns (`fast` or `flex`).
|
||||
#[deprecated(note = "use service_tier_id instead")]
|
||||
pub service_tier: Option<ServiceTier>,
|
||||
|
||||
/// Base URL for requests to ChatGPT (as opposed to the OpenAI API).
|
||||
|
||||
@@ -23,7 +23,12 @@ use codex_protocol::protocol::AskForApproval;
|
||||
#[schemars(deny_unknown_fields)]
|
||||
pub struct ConfigProfile {
|
||||
pub model: Option<String>,
|
||||
/// Optional explicit service tier preference for new turns (`fast` or `flex`).
|
||||
/// Optional explicit service tier id preference for new turns.
|
||||
///
|
||||
/// Takes precedence over deprecated `service_tier`.
|
||||
pub service_tier_id: Option<ServiceTier>,
|
||||
/// Deprecated legacy service tier preference for new turns (`fast` or `flex`).
|
||||
#[deprecated(note = "use service_tier_id instead")]
|
||||
pub service_tier: Option<ServiceTier>,
|
||||
/// The key in the `model_providers` map identifying the
|
||||
/// [`ModelProviderInfo`] to use.
|
||||
|
||||
@@ -663,7 +663,16 @@
|
||||
"$ref": "#/definitions/ServiceTier"
|
||||
}
|
||||
],
|
||||
"description": "Optional explicit service tier preference for new turns (`fast` or `flex`)."
|
||||
"deprecated": true,
|
||||
"description": "Deprecated legacy service tier preference for new turns (`fast` or `flex`)."
|
||||
},
|
||||
"service_tier_id": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ServiceTier"
|
||||
}
|
||||
],
|
||||
"description": "Optional explicit service tier id preference for new turns.\n\nTakes precedence over deprecated `service_tier`."
|
||||
},
|
||||
"tools": {
|
||||
"$ref": "#/definitions/ToolsToml"
|
||||
@@ -4391,7 +4400,16 @@
|
||||
"$ref": "#/definitions/ServiceTier"
|
||||
}
|
||||
],
|
||||
"description": "Optional explicit service tier preference for new turns (`fast` or `flex`)."
|
||||
"deprecated": true,
|
||||
"description": "Deprecated legacy service tier preference for new turns (`fast` or `flex`)."
|
||||
},
|
||||
"service_tier_id": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ServiceTier"
|
||||
}
|
||||
],
|
||||
"description": "Optional explicit service tier id preference for new turns.\n\nTakes precedence over deprecated `service_tier`."
|
||||
},
|
||||
"shell_environment_policy": {
|
||||
"allOf": [
|
||||
@@ -4494,4 +4512,4 @@
|
||||
},
|
||||
"title": "ConfigToml",
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
@@ -60,6 +60,9 @@ use codex_model_provider_info::LMSTUDIO_OSS_PROVIDER_ID;
|
||||
use codex_model_provider_info::OLLAMA_OSS_PROVIDER_ID;
|
||||
use codex_model_provider_info::WireApi;
|
||||
use codex_models_manager::bundled_models_response;
|
||||
use codex_protocol::config_types::SERVICE_TIER_FAST_LEGACY;
|
||||
use codex_protocol::config_types::SERVICE_TIER_PRIORITY;
|
||||
use codex_protocol::config_types::ServiceTier;
|
||||
use codex_protocol::models::ActivePermissionProfile;
|
||||
use codex_protocol::models::ActivePermissionProfileModification;
|
||||
use codex_protocol::models::ManagedFileSystemPermissions;
|
||||
@@ -6506,6 +6509,56 @@ async fn explicit_null_service_tier_override_sets_fast_default_opt_out() -> std:
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn service_tier_id_takes_precedence_over_deprecated_service_tier() -> std::io::Result<()> {
|
||||
let fixture = create_test_fixture()?;
|
||||
let mut cfg = fixture.cfg.clone();
|
||||
cfg.service_tier_id = Some(ServiceTier::from("premium"));
|
||||
#[allow(deprecated)]
|
||||
{
|
||||
cfg.service_tier = Some(ServiceTier::from(SERVICE_TIER_FAST_LEGACY));
|
||||
}
|
||||
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
cfg,
|
||||
ConfigOverrides {
|
||||
cwd: Some(fixture.cwd_path()),
|
||||
..Default::default()
|
||||
},
|
||||
fixture.codex_home(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
assert_eq!(config.service_tier, Some(ServiceTier::from("premium")));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn deprecated_fast_service_tier_maps_to_priority_id() -> std::io::Result<()> {
|
||||
let fixture = create_test_fixture()?;
|
||||
let mut cfg = fixture.cfg.clone();
|
||||
#[allow(deprecated)]
|
||||
{
|
||||
cfg.service_tier = Some(ServiceTier::from(SERVICE_TIER_FAST_LEGACY));
|
||||
}
|
||||
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
cfg,
|
||||
ConfigOverrides {
|
||||
cwd: Some(fixture.cwd_path()),
|
||||
..Default::default()
|
||||
},
|
||||
fixture.codex_home(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
assert_eq!(
|
||||
config.service_tier,
|
||||
Some(ServiceTier::from(SERVICE_TIER_PRIORITY))
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn fast_default_opt_out_notice_config_is_respected() -> std::io::Result<()> {
|
||||
let fixture = create_test_fixture()?;
|
||||
|
||||
@@ -6,6 +6,9 @@ use codex_config::types::McpServerConfig;
|
||||
use codex_config::types::ToolSuggestDisabledTool;
|
||||
use codex_features::FEATURES;
|
||||
use codex_protocol::config_types::Personality;
|
||||
use codex_protocol::config_types::SERVICE_TIER_FAST_LEGACY;
|
||||
use codex_protocol::config_types::SERVICE_TIER_FLEX;
|
||||
use codex_protocol::config_types::SERVICE_TIER_PRIORITY;
|
||||
use codex_protocol::config_types::ServiceTier;
|
||||
use codex_protocol::config_types::TrustLevel;
|
||||
use codex_protocol::openai_models::ReasoningEffort;
|
||||
@@ -525,12 +528,43 @@ impl ConfigDocument {
|
||||
);
|
||||
mutated
|
||||
}),
|
||||
ConfigEdit::SetServiceTier { service_tier } => Ok(self.write_profile_value(
|
||||
&["service_tier"],
|
||||
service_tier
|
||||
.as_ref()
|
||||
.map(|service_tier| value(service_tier.to_string())),
|
||||
)),
|
||||
ConfigEdit::SetServiceTier { service_tier } => {
|
||||
let mut mutated = false;
|
||||
match service_tier.as_ref().map(AsRef::as_ref) {
|
||||
Some(service_tier_id) if service_tier_id == SERVICE_TIER_PRIORITY => {
|
||||
mutated |= self.write_profile_value(
|
||||
&["service_tier_id"],
|
||||
Some(value(SERVICE_TIER_PRIORITY)),
|
||||
);
|
||||
mutated |= self.write_profile_value(
|
||||
&["service_tier"],
|
||||
Some(value(SERVICE_TIER_FAST_LEGACY)),
|
||||
);
|
||||
}
|
||||
Some(service_tier_id) if service_tier_id == SERVICE_TIER_FLEX => {
|
||||
mutated |= self.write_profile_value(
|
||||
&["service_tier_id"],
|
||||
Some(value(SERVICE_TIER_FLEX)),
|
||||
);
|
||||
mutated |= self
|
||||
.write_profile_value(&["service_tier"], Some(value(SERVICE_TIER_FLEX)));
|
||||
}
|
||||
Some(_) => {
|
||||
mutated |= self.write_profile_value(
|
||||
&["service_tier_id"],
|
||||
service_tier
|
||||
.as_ref()
|
||||
.map(|service_tier| value(service_tier.to_string())),
|
||||
);
|
||||
mutated |= self.write_profile_value(&["service_tier"], None);
|
||||
}
|
||||
None => {
|
||||
mutated |= self.write_profile_value(&["service_tier_id"], None);
|
||||
mutated |= self.write_profile_value(&["service_tier"], None);
|
||||
}
|
||||
}
|
||||
Ok(mutated)
|
||||
}
|
||||
ConfigEdit::SetModelPersonality { personality } => Ok(self.write_profile_value(
|
||||
&["personality"],
|
||||
personality.map(|personality| value(personality.to_string())),
|
||||
|
||||
@@ -2,6 +2,9 @@ use super::*;
|
||||
use codex_config::types::AppToolApproval;
|
||||
use codex_config::types::McpServerToolConfig;
|
||||
use codex_config::types::McpServerTransportConfig;
|
||||
use codex_protocol::config_types::SERVICE_TIER_FLEX;
|
||||
use codex_protocol::config_types::SERVICE_TIER_PRIORITY;
|
||||
use codex_protocol::config_types::ServiceTier;
|
||||
use codex_protocol::openai_models::ReasoningEffort;
|
||||
use pretty_assertions::assert_eq;
|
||||
#[cfg(unix)]
|
||||
@@ -31,6 +34,89 @@ model_reasoning_effort = "high"
|
||||
assert_eq!(contents, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_service_tier_writes_legacy_fast_for_priority_id() {
|
||||
let tmp = tempdir().expect("tmpdir");
|
||||
let codex_home = tmp.path();
|
||||
std::fs::write(
|
||||
codex_home.join(CONFIG_TOML_FILE),
|
||||
r#"service_tier_id = "premium"
|
||||
"#,
|
||||
)
|
||||
.expect("seed config");
|
||||
|
||||
ConfigEditsBuilder::new(codex_home)
|
||||
.set_service_tier(Some(SERVICE_TIER_PRIORITY.into()))
|
||||
.apply_blocking()
|
||||
.expect("persist");
|
||||
|
||||
let contents = std::fs::read_to_string(codex_home.join(CONFIG_TOML_FILE)).expect("read config");
|
||||
let expected = r#"service_tier_id = "priority"
|
||||
service_tier = "fast"
|
||||
"#;
|
||||
assert_eq!(contents, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_service_tier_writes_both_keys_for_flex_id() {
|
||||
let tmp = tempdir().expect("tmpdir");
|
||||
let codex_home = tmp.path();
|
||||
|
||||
ConfigEditsBuilder::new(codex_home)
|
||||
.set_service_tier(Some(SERVICE_TIER_FLEX.into()))
|
||||
.apply_blocking()
|
||||
.expect("persist");
|
||||
|
||||
let contents = std::fs::read_to_string(codex_home.join(CONFIG_TOML_FILE)).expect("read config");
|
||||
let expected = r#"service_tier_id = "flex"
|
||||
service_tier = "flex"
|
||||
"#;
|
||||
assert_eq!(contents, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_service_tier_writes_service_tier_id_for_arbitrary_id() {
|
||||
let tmp = tempdir().expect("tmpdir");
|
||||
let codex_home = tmp.path();
|
||||
std::fs::write(
|
||||
codex_home.join(CONFIG_TOML_FILE),
|
||||
r#"service_tier = "fast"
|
||||
"#,
|
||||
)
|
||||
.expect("seed config");
|
||||
|
||||
ConfigEditsBuilder::new(codex_home)
|
||||
.set_service_tier(Some(ServiceTier::from("premium")))
|
||||
.apply_blocking()
|
||||
.expect("persist");
|
||||
|
||||
let contents = std::fs::read_to_string(codex_home.join(CONFIG_TOML_FILE)).expect("read config");
|
||||
let expected = r#"service_tier_id = "premium"
|
||||
"#;
|
||||
assert_eq!(contents, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clear_service_tier_removes_legacy_and_id_keys() {
|
||||
let tmp = tempdir().expect("tmpdir");
|
||||
let codex_home = tmp.path();
|
||||
std::fs::write(
|
||||
codex_home.join(CONFIG_TOML_FILE),
|
||||
r#"service_tier_id = "premium"
|
||||
service_tier = "flex"
|
||||
"#,
|
||||
)
|
||||
.expect("seed config");
|
||||
|
||||
ConfigEditsBuilder::new(codex_home)
|
||||
.set_service_tier(None)
|
||||
.apply_blocking()
|
||||
.expect("persist");
|
||||
|
||||
let contents = std::fs::read_to_string(codex_home.join(CONFIG_TOML_FILE)).expect("read config");
|
||||
assert_eq!(contents, "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builder_with_edits_applies_custom_paths() {
|
||||
let tmp = tempdir().expect("tmpdir");
|
||||
|
||||
@@ -81,6 +81,7 @@ use codex_protocol::config_types::AltScreenMode;
|
||||
use codex_protocol::config_types::ForcedLoginMethod;
|
||||
use codex_protocol::config_types::Personality;
|
||||
use codex_protocol::config_types::ReasoningSummary;
|
||||
use codex_protocol::config_types::SERVICE_TIER_FAST_LEGACY;
|
||||
use codex_protocol::config_types::SERVICE_TIER_PRIORITY;
|
||||
use codex_protocol::config_types::SandboxMode;
|
||||
use codex_protocol::config_types::ServiceTier;
|
||||
@@ -1991,6 +1992,14 @@ pub(crate) fn resolve_web_search_mode_for_turn(
|
||||
WebSearchMode::Disabled
|
||||
}
|
||||
|
||||
fn normalize_deprecated_service_tier(service_tier: ServiceTier) -> ServiceTier {
|
||||
if service_tier.as_ref() == SERVICE_TIER_FAST_LEGACY {
|
||||
SERVICE_TIER_PRIORITY.into()
|
||||
} else {
|
||||
service_tier
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
#[cfg(test)]
|
||||
async fn load_from_base_config_with_overrides(
|
||||
@@ -2649,6 +2658,14 @@ impl Config {
|
||||
|
||||
let model = model.or(config_profile.model).or(cfg.model);
|
||||
let mut notices = cfg.notice.unwrap_or_default();
|
||||
#[allow(deprecated)]
|
||||
let deprecated_profile_service_tier = config_profile
|
||||
.service_tier
|
||||
.clone()
|
||||
.map(normalize_deprecated_service_tier);
|
||||
#[allow(deprecated)]
|
||||
let deprecated_config_service_tier =
|
||||
cfg.service_tier.clone().map(normalize_deprecated_service_tier);
|
||||
let service_tier = match service_tier_override {
|
||||
Some(Some(service_tier)) => Some(service_tier),
|
||||
Some(None) => {
|
||||
@@ -2657,7 +2674,11 @@ impl Config {
|
||||
notices.fast_default_opt_out = Some(true);
|
||||
None
|
||||
}
|
||||
None => config_profile.service_tier.or(cfg.service_tier),
|
||||
None => config_profile
|
||||
.service_tier_id
|
||||
.or(cfg.service_tier_id)
|
||||
.or(deprecated_profile_service_tier)
|
||||
.or(deprecated_config_service_tier),
|
||||
};
|
||||
let service_tier = match service_tier {
|
||||
Some(service_tier)
|
||||
|
||||
@@ -349,6 +349,9 @@ impl From<WebSearchToolConfig> for WebSearchConfig {
|
||||
|
||||
pub const SERVICE_TIER_PRIORITY: &str = "priority";
|
||||
pub const SERVICE_TIER_FLEX: &str = "flex";
|
||||
/// Legacy config value accepted by older clients. New clients map this to
|
||||
/// `priority` when reading deprecated `service_tier`.
|
||||
pub const SERVICE_TIER_FAST_LEGACY: &str = "fast";
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, TS)]
|
||||
#[serde(transparent)]
|
||||
|
||||
@@ -17,6 +17,7 @@ use ts_rs::TS;
|
||||
|
||||
use crate::config_types::Personality;
|
||||
use crate::config_types::ReasoningSummary;
|
||||
#[cfg(test)]
|
||||
use crate::config_types::SERVICE_TIER_PRIORITY;
|
||||
use crate::config_types::ServiceTier;
|
||||
use crate::config_types::Verbosity;
|
||||
|
||||
Reference in New Issue
Block a user