mirror of
https://github.com/openai/codex.git
synced 2026-11-08 08:37:30 +00:00
Add opt-in model discovery for OpenAI API keys (#44392)
## What changed Add the default-off `api_key_model_discovery` feature and expose it through app-server experimental feature enablement, respecting user configuration overrides. When enabled, fetch OpenAI API-key model metadata from the Codex backend, honoring explicit provider base URLs. Treat remote catalogs containing visible models as authoritative. When disabled at startup, skip discovery requests and cached catalogs and retain bundled models. ## Testing Add coverage for feature enablement and user overrides, cached catalogs across sessions, provider routing, and API-key `model/list` results. Extend coverage for remote instructions and auth-specific cache isolation. GitOrigin-RevId: 4b189c37c615a7f808cb6ff5f0858a9a55f36ebc
This commit is contained in:
1 parent
5a9eb145c4
commit
ea53c8d4f7
13 files changed
+465
-54
No files matched your search
@@ -72,11 +72,28 @@ async fn auth_and_provider_switches_do_not_reuse_chatgpt_catalog() -> Result<()>
|
||||
.models,
|
||||
bundled
|
||||
);
|
||||
assert_eq!(models_mock.requests().len(), 1);
|
||||
drop(chatgpt);
|
||||
server.reset().await;
|
||||
|
||||
let mut api_model = model.clone();
|
||||
api_model.default_service_tier = None;
|
||||
let api_models_mock = responses::mount_models_once(
|
||||
&server,
|
||||
ModelsResponse {
|
||||
models: vec![api_model],
|
||||
},
|
||||
)
|
||||
.await;
|
||||
let api = test_codex()
|
||||
.with_home(home.clone())
|
||||
.with_auth(CodexAuth::from_api_key("api-key"))
|
||||
.with_config(|config| {
|
||||
config
|
||||
.features
|
||||
.enable(codex_features::Feature::ApiKeyModelDiscovery)
|
||||
.expect("enable API-key model discovery");
|
||||
})
|
||||
.with_model("gpt-5.5")
|
||||
.build_with_auto_env(&server)
|
||||
.await?;
|
||||
@@ -108,7 +125,7 @@ async fn auth_and_provider_switches_do_not_reuse_chatgpt_catalog() -> Result<()>
|
||||
explicit.single_request().body_json()["service_tier"],
|
||||
"priority"
|
||||
);
|
||||
assert_eq!(models_mock.requests().len(), 1);
|
||||
assert_eq!(api_models_mock.requests().len(), 1);
|
||||
drop(api);
|
||||
|
||||
let other_server = wiremock::MockServer::start().await;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#![cfg(not(target_os = "windows"))]
|
||||
use anyhow::Result;
|
||||
use codex_core::TurnInputRequest;
|
||||
use codex_features::Feature;
|
||||
use codex_login::CodexAuth;
|
||||
use codex_model_provider_info::ModelProviderInfo;
|
||||
use codex_model_provider_info::built_in_model_providers;
|
||||
@@ -852,8 +853,10 @@ async fn remote_models_truncation_policy_with_tool_output_override() -> Result<(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test_case(CodexAuth::create_dummy_chatgpt_auth_for_testing(); "chatgpt")]
|
||||
#[test_case(CodexAuth::from_api_key("test-api-key"); "api key")]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn remote_models_apply_legacy_instructions() -> Result<()> {
|
||||
async fn remote_models_apply_legacy_instructions(auth: CodexAuth) -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
skip_if_sandbox!(Ok(()));
|
||||
|
||||
@@ -959,12 +962,14 @@ async fn remote_models_apply_legacy_instructions() -> Result<()> {
|
||||
)
|
||||
.await;
|
||||
|
||||
let mut builder = test_codex()
|
||||
.with_auth(CodexAuth::create_dummy_chatgpt_auth_for_testing())
|
||||
.with_config(|config| {
|
||||
config.update_plan_enabled = true;
|
||||
config.model = Some("gpt-5.2".to_string());
|
||||
});
|
||||
let mut builder = test_codex().with_auth(auth).with_config(|config| {
|
||||
config
|
||||
.features
|
||||
.enable(Feature::ApiKeyModelDiscovery)
|
||||
.expect("enable API-key model discovery");
|
||||
config.update_plan_enabled = true;
|
||||
config.model = Some("gpt-5.2".to_string());
|
||||
});
|
||||
let TestCodex {
|
||||
codex,
|
||||
cwd,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use anyhow::Result;
|
||||
use codex_features::Feature;
|
||||
use codex_models_manager::bundled_models_response;
|
||||
use core_test_support::responses::ev_completed;
|
||||
use core_test_support::responses::ev_response_created;
|
||||
use core_test_support::responses::sse;
|
||||
@@ -105,6 +106,9 @@ async fn regular_responses_turn_honors_respect_system_proxy() -> Result<()> {
|
||||
|
||||
let server = MockServer::start().await;
|
||||
let mut builder = test_codex().with_config(|config| {
|
||||
// The proxy serves one inference response; model discovery must not consume it.
|
||||
config.model_catalog =
|
||||
Some(bundled_models_response().expect("bundled models.json should parse"));
|
||||
config.model_provider.base_url = Some("http://responses-proxy.invalid/v1".to_string());
|
||||
config
|
||||
.features
|
||||
|
||||
@@ -5,6 +5,7 @@ use codex_client::run_with_retry;
|
||||
use codex_http_client::Request;
|
||||
use codex_http_client::TransportError;
|
||||
use codex_login::CodexAuth;
|
||||
use codex_models_manager::bundled_models_response;
|
||||
use codex_protocol::protocol::CodexErrorInfo;
|
||||
use codex_protocol::protocol::EventMsg;
|
||||
use codex_protocol::protocol::Op;
|
||||
@@ -1628,6 +1629,9 @@ async fn websocket_overload_with_nested_retry_after_is_terminal() -> Result<()>
|
||||
.await;
|
||||
let test = test_codex()
|
||||
.with_config(|config| {
|
||||
// Capture inference retries without unrelated startup model-discovery retries.
|
||||
config.model_catalog =
|
||||
Some(bundled_models_response().expect("bundled models.json should parse"));
|
||||
config.model_provider.request_max_retries = Some(2);
|
||||
config.model_provider.stream_max_retries = Some(2);
|
||||
})
|
||||
@@ -1709,6 +1713,9 @@ async fn websocket_overload_without_retry_after_is_terminal() -> Result<()> {
|
||||
.await;
|
||||
let test = test_codex()
|
||||
.with_config(|config| {
|
||||
// Capture inference retries without unrelated startup model-discovery retries.
|
||||
config.model_catalog =
|
||||
Some(bundled_models_response().expect("bundled models.json should parse"));
|
||||
config.model_provider.request_max_retries = Some(2);
|
||||
config.model_provider.stream_max_retries = Some(2);
|
||||
})
|
||||
|
||||
Reference in new issue
Block a user