From 91254e74662214ea95cbe308e3ebadbc5825a1da Mon Sep 17 00:00:00 2001 From: dongyu <1410875946@qq.com> Date: Thu, 6 Aug 2026 01:02:41 +0800 Subject: [PATCH 1/3] fix: generate catalog when user configures context window (#1594) PR #1722 fixed the early return in apply_model_catalog_to_config that silently dropped the Manager's context window for suffixless custom models. Codex then fell back to its bundled 272000 default and the CLI showed ~258K instead of the configured value. The fix moves the fallback parse before the early return and adds fallback.is_none() to the guard, so a user-configured context window forces catalog generation. Two integration tests failed because they set context_window=200000 while asserting no catalog generation. Both tests now omit context_window and auto_compact_limit, preserving their original intent: no catalog when nothing is customized. Closes #1722 References #1594 --- crates/codex-plus-core/src/relay_config.rs | 47 +++++++++++++++++--- crates/codex-plus-core/tests/relay_config.rs | 9 +--- 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/crates/codex-plus-core/src/relay_config.rs b/crates/codex-plus-core/src/relay_config.rs index 77409d0b1..494ff6d2a 100644 --- a/crates/codex-plus-core/src/relay_config.rs +++ b/crates/codex-plus-core/src/relay_config.rs @@ -1593,14 +1593,20 @@ fn apply_model_catalog_to_config( }; let entries = crate::model_suffix::collect_catalog_entries(&model_list, &model_windows, &profile.model); - // Known bundled metadata entries need a catalog even without a user-supplied window. - if !entries.iter().any(|entry| { - entry.suffix_window.is_some() - || crate::model_suffix::requires_bundled_metadata_catalog(&entry.slug) - }) { + let fallback = parse_optional_positive_u64(&profile.context_window, "上下文大小")?; + // Generate a catalog whenever there is something to customize: a per-model `[window]` + // suffix, bundled metadata, OR a user-configured context window. Without the last case + // a suffixless custom model silently drops the Manager's context window, so Codex falls + // back to its bundled 272000 default and the CLI shows ~258K instead of the configured + // value (#1594). + if fallback.is_none() + && !entries.iter().any(|entry| { + entry.suffix_window.is_some() + || crate::model_suffix::requires_bundled_metadata_catalog(&entry.slug) + }) + { return Ok(config_text.to_string()); } - let fallback = parse_optional_positive_u64(&profile.context_window, "上下文大小")?; let catalog_path = home.join(&catalog_relative); if let Some(parent) = catalog_path.parent() { std::fs::create_dir_all(parent)?; @@ -2734,6 +2740,35 @@ mod tests { }; assert!(relay_profile_model(&empty).trim().is_empty()); } + + // #1594: a custom model without a `[window]` suffix and without bundled metadata + // must still honor the context window the user configured in the Manager. Otherwise + // apply_model_catalog_to_config returns before generating a catalog, Codex falls back + // to its bundled 272000 default and the CLI shows ~258K instead of the configured 1M. + #[test] + fn generates_catalog_with_user_context_window_for_suffixless_custom_model() { + let temp = tempfile::tempdir().unwrap(); + let config = "model_provider = \"custom\"\nmodel = \"deepseek-v4-flash\"\n\n\ + [model_providers.custom]\nname = \"custom\"\nwire_api = \"responses\"\n\ + base_url = \"http://127.0.0.1:57321/v1\"\n"; + let profile = RelayProfile { + id: "ctxwin".to_string(), + model: "deepseek-v4-flash".to_string(), + model_list: "deepseek-v4-flash".to_string(), // no [1m] suffix + context_window: "1000000".to_string(), // user configured 1M in the Manager + ..RelayProfile::default() + }; + + let result = apply_model_catalog_to_config(temp.path(), &profile, config).unwrap(); + + let catalog_rel = root_key_string(&result, "model_catalog_json") + .expect("a catalog should be generated when the user set a context window"); + let catalog = std::fs::read_to_string(temp.path().join(catalog_rel)).unwrap(); + assert!( + catalog.contains("1000000"), + "catalog must carry the configured context window, not codex's default; got: {catalog}" + ); + } } pub fn root_key_string(contents: &str, key: &str) -> Option { diff --git a/crates/codex-plus-core/tests/relay_config.rs b/crates/codex-plus-core/tests/relay_config.rs index 22c9194b3..911675998 100644 --- a/crates/codex-plus-core/tests/relay_config.rs +++ b/crates/codex-plus-core/tests/relay_config.rs @@ -1275,17 +1275,14 @@ experimental_bearer_token = "sk-new" auth_contents: r#"{"OPENAI_API_KEY":"sk-new"}"#.to_string(), model_insert_mode: Default::default(), model_list: "deepseek-coder\nqwen3-coder".to_string(), - context_window: "200000".to_string(), - auto_compact_limit: "160000".to_string(), ..RelayProfile::default() }; apply_relay_profile_files_to_home_with_context(temp.path(), &profile, "").unwrap(); let config = std::fs::read_to_string(temp.path().join("config.toml")).unwrap(); + // No suffix, no bundled metadata, no context window -> no catalog. assert!(!config.contains("model_catalog_json")); - assert!(config.contains("model_context_window = 200000")); - assert!(config.contains("model_auto_compact_token_limit = 160000")); assert!(!temp.path().join("model-catalogs").exists()); } @@ -3322,16 +3319,14 @@ experimental_bearer_token = "sk-new" auth_contents: r#"{"OPENAI_API_KEY":"sk-new"}"#.to_string(), model_insert_mode: Default::default(), model_list: "deepseek-coder\nqwen3-coder".to_string(), - context_window: "200000".to_string(), - auto_compact_limit: "160000".to_string(), ..RelayProfile::default() }; apply_relay_profile_files_to_home_with_context(temp.path(), &profile, "").unwrap(); let config = std::fs::read_to_string(temp.path().join("config.toml")).unwrap(); + // No suffix, no bundled metadata, no context window -> no catalog. assert!(!config.contains("model_catalog_json")); - assert!(config.contains("model_context_window = 200000")); assert!(!temp.path().join("model-catalogs").exists()); } From 3b2ec5ff53b6b752c100c5c08a8c2b6350f41d9c Mon Sep 17 00:00:00 2001 From: dongyu <1410875946@qq.com> Date: Tue, 11 Aug 2026 21:31:14 +0800 Subject: [PATCH 2/3] ci: preserve byte-exact theme asset line endings --- .gitattributes | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitattributes b/.gitattributes index 561487b9d..92b8d1d62 100644 --- a/.gitattributes +++ b/.gitattributes @@ -14,3 +14,8 @@ assets/inject/upstream/snow-skin/*.css text eol=lf # Keep byte-exact macOS theme assets identical on every checkout platform. assets/inject/upstream/*/macos/*.js text eol=lf assets/inject/upstream/*/macos/*.css text eol=lf + +# Keep byte-exact Windows theme assets and skin manifests stable on checkout. +assets/inject/upstream/*/windows/*.js text eol=lf +assets/inject/upstream/*/windows/*.css text eol=lf +assets/inject/upstream/skin-packs/packs/*/theme.json text eol=lf From 48163e986ba751d394fe484d4f8685c341daf87c Mon Sep 17 00:00:00 2001 From: dongyu <1410875946@qq.com> Date: Tue, 11 Aug 2026 21:43:05 +0800 Subject: [PATCH 3/3] ci: preserve Glass Vision asset line endings --- .gitattributes | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitattributes b/.gitattributes index 92b8d1d62..a9c87e9b9 100644 --- a/.gitattributes +++ b/.gitattributes @@ -10,6 +10,8 @@ # Preserve the original Snow Skin asset line endings for byte-exact bundling. assets/inject/upstream/snow-skin/*.js text eol=lf assets/inject/upstream/snow-skin/*.css text eol=lf +assets/inject/upstream/glass-vision/*.js text eol=lf +assets/inject/upstream/glass-vision/*.css text eol=lf # Keep byte-exact macOS theme assets identical on every checkout platform. assets/inject/upstream/*/macos/*.js text eol=lf