fix: Codex model catalog WYSIWYG and config consolidation

- Remove mergeCodexDefaultCatalogModelForSave implicit injection (P1)
  The model mapping table is now the single source of truth; no hidden
  entries are prepended on save.

- Sync first catalog row model into config.toml on save
  Ensures Codex default request model matches the table's first entry
  instead of retaining a stale template value.

- Remove API Format selector from CodexFormFields (P3)
  wire_api is always 'responses'; the selector confused users into
  thinking they were changing the upstream protocol. Only the 'Needs
  Local Routing' toggle remains.

- Add restart hint to model mapping i18n text (P2)
  model_catalog_json is loaded at Codex startup; users are now informed
  that a restart is needed after changes.

- Unify write_codex_live_with_catalog helper (P4)
  Replaces three scattered prepare+write call sites in config.rs,
  provider/live.rs, and proxy.rs with a single entry point.

- Clean up useCodexConfigState dead state (P3 follow-up)
  Remove codexModelName, codexContextWindow, codexAutoCompactLimit and
  their handlers/effects since no component consumes them after the UI
  consolidation.
This commit is contained in:
Jason
2026-05-20 10:24:06 +08:00
parent 90b7f25111
commit 791ced0034
13 changed files with 959 additions and 207 deletions
@@ -2,8 +2,11 @@ import { describe, expect, it } from "vitest";
import {
extractCodexBaseUrl,
extractCodexModelName,
extractCodexTopLevelInt,
removeCodexTopLevelField,
setCodexBaseUrl,
setCodexModelName,
setCodexTopLevelInt,
} from "@/utils/providerConfigUtils";
describe("Codex TOML utils", () => {
@@ -148,4 +151,50 @@ describe("Codex TOML utils", () => {
expect(extractCodexBaseUrl(input)).toBe("https://api.example.com/v1");
expect(extractCodexModelName(input)).toBe("gpt-5");
});
it("reads, writes, and removes top-level integer metadata fields", () => {
const input = [
'model_provider = "custom"',
'model = "deepseek-v4-flash"',
"",
"[model_providers.custom]",
'name = "DeepSeek"',
"",
].join("\n");
const withContext = setCodexTopLevelInt(
input,
"model_context_window",
128000,
);
const withCompact = setCodexTopLevelInt(
withContext,
"model_auto_compact_token_limit",
90000,
);
expect(extractCodexTopLevelInt(withCompact, "model_context_window")).toBe(
128000,
);
expect(
extractCodexTopLevelInt(
withCompact,
"model_auto_compact_token_limit",
),
).toBe(90000);
expect(withCompact).toMatch(/^model_context_window = 128000$/m);
expect(withCompact).toMatch(
/^model_auto_compact_token_limit = 90000$/m,
);
const removed = removeCodexTopLevelField(
withCompact,
"model_context_window",
);
expect(
extractCodexTopLevelInt(removed, "model_context_window"),
).toBeUndefined();
expect(removed).toContain("[model_providers.custom]");
});
});