feat(proxy): add GET /v1/models endpoint for Codex CLI reachability check#3818
Conversation
…heck Codex CLI probes GET /v1/models at startup. Without this endpoint the proxy returns 404, causing Codex to fail before any request reaches the upstream LLM. Return an OpenAI-compatible model list derived from the cc-switch–managed model catalog file. Fixes farion1231#3812
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1fb0cbd542
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Codex deserializes the response as a catalog with a top-level `models`
field, not the OpenAI `{"object":"list","data":[...]}` envelope.
Return the catalog file content directly so the format matches what
Codex expects.
Co-authored-by: Codex review bot
实测验证Codex doctor 探测的路径用 probe-logger 抓包确认:codex doctor 启动时发送的唯一请求是 测试结果
结论
|
复现验证测试环境
复现步骤与结果第一步:按官方指南配置,直接连 cc-switch # ~/.codex/config.toml
base_url = "http://127.0.0.1:15721/v1"$ codex doctor
✗ reachability provider base URL route returned 404
custom API route probe http://127.0.0.1:15721/v1/<redacted> returned HTTP 404 (required)第二步:用 probe-logger 抓包确认 Codex 实际请求的路径 Codex doctor 只发送这一个请求。当前 cc-switch 第三步:模拟 PR 合入后的效果(test server 返回 catalog 格式) $ codex doctor
✓ reachability active provider endpoints are reachable over HTTP
custom API route probe route exists (HTTP 200)格式测试两种响应格式均通过 codex doctor(Codex 仅检查 HTTP 状态码,不校验 body),但选择直接返回 cc-switch 已有的 model catalog 文件内容(
改动效果
|
|
@farion1231 comment done |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f49c8323c2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Only return the model catalog when config.toml still references it via `model_catalog_json`. After switching to a provider without a custom catalog, the old file lingers on disk — serving it unconditionally would advertise the previous provider's models to Codex. Co-authored-by: Codex review bot
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bd02f22234
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
cc-switch writes `model_catalog_json = "cc-switch-model-catalog.json"` (relative) via set_codex_model_catalog_json_field. Match on the filename constant rather than the absolute path so the guard works with both relative and absolute paths. Co-authored-by: Codex review bot
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6f155a230c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Replace raw config_text.contains() with proper TOML field parsing so commented-out lines and stray mentions of the filename in other fields don't defeat the stale guard. Also switch from contains() to exact filename match (Path::new(val).file_name() == Some(...)) to stay consistent with resolve_cc_switch_catalog_path in codex_config.rs. Add log::debug! when the guard blocks serving so the operator can distinguish "no models configured" from "guard blocked stale catalog".
|
@codex review |
|
Codex Review: Didn't find any major issues. Keep it up! ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Replace the inline config.toml parsing and filename match in handle_models with the existing resolve_cc_switch_catalog_path helper (now pub(crate)). This removes the duplicated stale-guard logic, keeps a single source of truth for catalog-path ownership, and makes the handler honor absolute model_catalog_json paths the same way Codex live-setting import does.
farion1231
left a comment
There was a problem hiding this comment.
Thank you for your contribution!
…heck (farion1231#3818) * feat(proxy): add GET /v1/models endpoint for Codex CLI reachability check Codex CLI probes GET /v1/models at startup. Without this endpoint the proxy returns 404, causing Codex to fail before any request reaches the upstream LLM. Return an OpenAI-compatible model list derived from the cc-switch–managed model catalog file. Fixes farion1231#3812 * fix(proxy): return Codex catalog schema from /v1/models Codex deserializes the response as a catalog with a top-level `models` field, not the OpenAI `{"object":"list","data":[...]}` envelope. Return the catalog file content directly so the format matches what Codex expects. Co-authored-by: Codex review bot * fix(proxy): guard /v1/models against serving stale catalog Only return the model catalog when config.toml still references it via `model_catalog_json`. After switching to a provider without a custom catalog, the old file lingers on disk — serving it unconditionally would advertise the previous provider's models to Codex. Co-authored-by: Codex review bot * fix(proxy): match relative model_catalog_json in stale-guard cc-switch writes `model_catalog_json = "cc-switch-model-catalog.json"` (relative) via set_codex_model_catalog_json_field. Match on the filename constant rather than the absolute path so the guard works with both relative and absolute paths. Co-authored-by: Codex review bot * fix(proxy): parse model_catalog_json field instead of substring match Replace raw config_text.contains() with proper TOML field parsing so commented-out lines and stray mentions of the filename in other fields don't defeat the stale guard. Also switch from contains() to exact filename match (Path::new(val).file_name() == Some(...)) to stay consistent with resolve_cc_switch_catalog_path in codex_config.rs. Add log::debug! when the guard blocks serving so the operator can distinguish "no models configured" from "guard blocked stale catalog". * refactor(proxy): reuse resolve_cc_switch_catalog_path in handle_models Replace the inline config.toml parsing and filename match in handle_models with the existing resolve_cc_switch_catalog_path helper (now pub(crate)). This removes the duplicated stale-guard logic, keeps a single source of truth for catalog-path ownership, and makes the handler honor absolute model_catalog_json paths the same way Codex live-setting import does. --------- Co-authored-by: Jason <[email protected]>
…heck (farion1231#3818) * feat(proxy): add GET /v1/models endpoint for Codex CLI reachability check Codex CLI probes GET /v1/models at startup. Without this endpoint the proxy returns 404, causing Codex to fail before any request reaches the upstream LLM. Return an OpenAI-compatible model list derived from the cc-switch–managed model catalog file. Fixes farion1231#3812 * fix(proxy): return Codex catalog schema from /v1/models Codex deserializes the response as a catalog with a top-level `models` field, not the OpenAI `{"object":"list","data":[...]}` envelope. Return the catalog file content directly so the format matches what Codex expects. Co-authored-by: Codex review bot * fix(proxy): guard /v1/models against serving stale catalog Only return the model catalog when config.toml still references it via `model_catalog_json`. After switching to a provider without a custom catalog, the old file lingers on disk — serving it unconditionally would advertise the previous provider's models to Codex. Co-authored-by: Codex review bot * fix(proxy): match relative model_catalog_json in stale-guard cc-switch writes `model_catalog_json = "cc-switch-model-catalog.json"` (relative) via set_codex_model_catalog_json_field. Match on the filename constant rather than the absolute path so the guard works with both relative and absolute paths. Co-authored-by: Codex review bot * fix(proxy): parse model_catalog_json field instead of substring match Replace raw config_text.contains() with proper TOML field parsing so commented-out lines and stray mentions of the filename in other fields don't defeat the stale guard. Also switch from contains() to exact filename match (Path::new(val).file_name() == Some(...)) to stay consistent with resolve_cc_switch_catalog_path in codex_config.rs. Add log::debug! when the guard blocks serving so the operator can distinguish "no models configured" from "guard blocked stale catalog". * refactor(proxy): reuse resolve_cc_switch_catalog_path in handle_models Replace the inline config.toml parsing and filename match in handle_models with the existing resolve_cc_switch_catalog_path helper (now pub(crate)). This removes the duplicated stale-guard logic, keeps a single source of truth for catalog-path ownership, and makes the handler honor absolute model_catalog_json paths the same way Codex live-setting import does. --------- Co-authored-by: Jason <[email protected]>
Summary
Add a
GET /v1/modelsendpoint. Codex CLI probes this endpoint at startup — without it cc-switch returns 404, causing Codex to fail before any request reaches the upstream LLM.The handler returns the cc-switch–managed model catalog file directly (
~/.codex/cc-switch-model-catalog.json), gated onconfig.tomlstill referencing the catalog viamodel_catalog_json. This prevents serving stale models after switching to a provider without a custom catalog.Verification
Codex doctor sends exactly one request —
GET /v1/models— and only checks the HTTP status code (200 vs 404). Response body format does not affect the reachability check./v1/modelsRelated Issue
Fixes #3812
Changes
src-tauri/src/proxy/server.rs: registerGET /v1/modelsandGET /modelsroutes (+3 lines)src-tauri/src/proxy/handlers.rs: newhandle_models()handler with stale-catalog guard (+20 lines)Checklist
cargo fmt --checkpassescargo clippy --no-depspasses (zero warnings)cargo test -p cc-switch -- proxy— 837 passed, 5 pre-existing failures (port 15721 in use by running cc-switch)pnpm typecheck/pnpm format:checknot applicable)