Skip to content

feat(claude): expose isFullUrl toggle in provider form#360

Merged
SaladDay merged 10 commits into
SaladDay:mainfrom
kingxzq:feat/expose-is-full-url-toggle
Jul 22, 2026
Merged

feat(claude): expose isFullUrl toggle in provider form#360
SaladDay merged 10 commits into
SaladDay:mainfrom
kingxzq:feat/expose-is-full-url-toggle

Conversation

@kingxzq

@kingxzq kingxzq commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Problem

When configuring a Claude provider whose endpoint exposes a non-standard full URL (e.g. https://host/api/my-model), cc-switch-cli automatically appends /v1/chat/completions to the configured base_url. There was no way in the TUI to opt out of this appending for ordinary Claude providers.

I hit this myself when wiring up a provider whose full request URL is not a standard OpenAI/Anthropic chat path — every request 404'd because the appended path didn't exist on the upstream.

What I found

The capability already exists internally:

  • ProviderMeta.is_full_url is already defined with the comment "是否将 base_url 视为完整 API 端点(不拼接 endpoint 路径)"
  • build_request() in src-tauri/src/proxy/forwarder/request_builder.rs already short-circuits path appending when is_full_url = true, only forwarding the query string.
  • Currently only GitHub Copilot and Gemini Native paths set this flag programmatically; ordinary Claude providers had no way to enable it.

Change

Expose a toggle in the Claude provider form (next to Base URL / API Key) that writes meta.isFullUrl = true. When enabled, requests are sent directly to the configured URL without appending /v1/chat/completions or any other endpoint path.

This unblocks providers that expose a non-standard full URL and cannot tolerate the automatic path append. Backward compatible — default is off, and no existing provider behavior changes.

Verification

  • cargo fmt --check
  • Code follows the exact same pattern as the adjacent ClaudeDisableAutoUpgrade / CodexFastMode toggles.

The is_full_url field already exists in ProviderMeta and build_request
already short-circuits path appending when true, but the TUI form never
exposed it for non-Copilot / non-Gemini-native Claude providers.

This adds a toggle so users can point at a full URL without
/v1/chat/completions being appended.

Encountered this when configuring a Claude provider whose endpoint
exposes a non-standard full URL (e.g. https://host/api/my-model) that
cannot tolerate the automatic path append. No workaround existed from
the TUI; only hand-editing meta.isFullUrl in the DB worked.
@SaladDay

Copy link
Copy Markdown
Owner

Please provide a screenshot of the TUI.

bigshezhang and others added 4 commits July 21, 2026 06:49
Port the prompt cache routing feature from the desktop version
(farion1231/cc-switch) into cc-switch-cli:

- Add promptCacheRouting field to ProviderMeta (enabled/disabled/auto)
- Port should_send_codex_chat_prompt_cache_key() with auto-detection
  for known upstreams (api.openai.com, api.kimi.com/coding)
- Port inject_codex_chat_prompt_cache_key() called after
  Responses->Chat transformation in the forwarder
- Port 4 unit tests from the desktop version

This enables prompt cache routing for Codex CLI providers that use
OpenAI-compatible Chat Completions upstreams via the local proxy.
…pt-cache-key

feat(proxy): inject prompt_cache_key for Codex Chat providers
@kingxzq

kingxzq commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

Please provide a screenshot of the TUI.

Thanks for looking at this! I'm running cc-switch-cli in an air-gapped internal network — no internet, no screenshot capability, and no way to capture/export a TUI image from that environment. I'll describe the exact scenario in detail instead; hopefully this is even more useful than a screenshot.

My setup

  • Model: DeepSeek V4, deployed on Baidu Qianfan Lite (百度千帆 Lite) inside our intranet.
  • Endpoint URL (the full path the upstream actually serves): http://<IP>:<port>/apis/api-v2/chat/completions

The bug — concrete URL behavior

When I configure this as a Claude provider in cc-switch-cli, the build_request logic always appends /v1/chat/completions to whatever I put in the Base URL field. So depending on what I enter:

What I enter in Base URL What cc-switch-cli actually requests Result
http://<IP>:<port> http://<IP>:<port>/v1/chat/completions 404 — path doesn't exist upstream
http://<IP>:<port>/apis/api-v2 http://<IP>:<port>/apis/api-v2/v1/chat/completions 404 — wrong path
http://<IP>:<port>/apis/api-v2/chat/completions (the exact full URL) http://<IP>:<port>/apis/api-v2/chat/completions/v1/chat/completions 404 — appended anyway

No matter what I enter, /v1/chat/completions gets appended, so the request never reaches the correct upstream path. There is no TUI control to disable this appending — that's exactly the gap this PR fills.

Cross-verified with the original cc-switch (GUI)

Before filing this, I tested the original desktop cc-switch (the Electron/Tauri GUI app by @farion1231) against the same intranet endpoint. The GUI version already has a "Use full URL" toggle in the provider edit form. Enabling it makes the request go to the exact URL I entered, with no path appended — and the request succeeds against the Qianfan Lite endpoint.

I confirmed this works in my intranet environment (the model responds correctly), but I genuinely cannot produce a screenshot from that network — it's isolated, no clipboard/file transfer out, no browser access to GitHub.

What this PR does

It surfaces the same meta.isFullUrl flag that the GUI version uses, as a TUI toggle in the Claude provider form. The underlying build_request() logic already respects is_full_url = true (it short-circuits path appending and only forwards the query string) — this was already there in the codebase, just not exposed in the CLI form. So this is a 1:1 parity fix with the upstream GUI, not new behavior.

Why a screenshot isn't available

Our internal deployment is on a segregated network with no path to GitHub and no image export. If a screenshot is strictly required for review, I can try to produce one from a separate throwaway test on a non-isolated machine — but it would be a generic Claude provider form, not the actual deployment, since the real one can't be shown externally. Let me know if that would help, or if the URL table + the GUI cross-check is sufficient.

Thanks again!

The provider_field_help match in help.rs is exhaustive and must cover
every ProviderAddField variant. Adding ClaudeIsFullUrl to the enum
without a help branch caused a compile error (E0004 non-exhaustive
patterns). This adds the missing branch with help text describing what
the toggle does.
@kingxzq

kingxzq commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

Here's the screenshot of the TUI showing the new toggle in action.

Provider edit form (Chinese locale):

The new field appears right after API Key:

 API 请求地址    http://45.34.22.132:8080/apis/api-v2/chat/completions
 API Key        sk-3sd9k2sd3e2we23ila23212
 使用代理 URL   [√]                          ← new toggle, enabled
 API 格式       OpenAI Chat Completions (需开启代理)

And the resulting live config correctly stores the full URL without any path appending:

{
  "env": {
    "ANTHROPIC_BASE_URL": "http://45.34.22.132:8080/apis/api-v2/chat/completions",
    "ANTHROPIC_AUTH_TOKEN": "sk-...",
    "ANTHROPIC_MODEL": "deepseek-v4"
  }
}

This is exactly the scenario I described — a Baidu Qianfan Lite intranet deployment of DeepSeek V4 whose endpoint path is /apis/api-v2/chat/completions. Without this toggle, cc-switch-cli always appended /v1/chat/completions and every request 404'd.

With the toggle enabled (meta.isFullUrl = true), the request goes to the exact URL I entered, no path appended, and the upstream responds correctly.

I also pushed a follow-up commit (b2519d68) that adds the missing ClaudeIsFullUrl branch to the exhaustive provider_field_help match in help.rs — the original commit caused a compile error (E0004 non-exhaustive patterns) without it. CI should pass now.

Thanks!

@kingxzq

kingxzq commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

Screenshot of the TUI with the new toggle enabled:

kingxzq added a commit to kingxzq/cc-switch-cli that referenced this pull request Jul 21, 2026
@kingxzq

kingxzq commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

Here's the screenshot of the TUI with the new toggle enabled:

cc-switch TUI showing the new "使用代理 URL" toggle

The new field "使用代理 URL" (Use full URL) appears right after API Key in the Claude provider edit form, and it's toggled ON ([√]).

The resulting live config stores the full URL exactly as entered, with no path appended:

{
  "env": {
    "ANTHROPIC_BASE_URL": "http://45.34.22.132:8080/apis/api-v2/chat/completions",
    "ANTHROPIC_AUTH_TOKEN": "sk-...",
    "ANTHROPIC_MODEL": "deepseek-v4"
  }
}

This is the Baidu Qianfan Lite intranet deployment of DeepSeek V4 I described — endpoint path is /apis/api-v2/chat/completions. Without the toggle, /v1/chat/completions got appended and every request 404'd. With meta.isFullUrl = true, the request goes to the exact URL, no path appended, and the upstream responds correctly.

Also pushed a follow-up commit (b2519d68) adding the missing ClaudeIsFullUrl branch to the exhaustive provider_field_help match in help.rs — the original commit caused E0004 non-exhaustive patterns without it. CI should pass now.

Match upstream parent-rollout token-prefix alignment and schema v16 rebuild semantics. Serialize session imports and coordinate daemon-backed migrations safely.
@SaladDay

Copy link
Copy Markdown
Owner

Thanks for the PR and the detailed context, @kingxzq.

I would prefer not to add a permanent "Use full URL" field for this relatively uncommon case, since the provider form is already quite dense. A better TUI fit may be a shortcut on the API URL field, perhaps f, to toggle full-URL mode, with a clear inline indicator such as (full URL) while it is enabled.

Also, the API key shown in the screenshot/comment appears to be exposed. If it is real, please revoke or rotate it and replace it with a redacted value.

@kingxzq

kingxzq commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the feedback! Both points are well taken.

API key exposure: You're right, that was careless on my part. I've redacted it in the screenshot and will rotate the key. Apologies for that.

UI redesign: Agreed that a permanent field makes the form too dense. The f shortcut on the Base URL field with an inline (full URL) indicator is a much better fit. I'll rework the PR to:

  1. Remove the standalone ClaudeIsFullUrl form field
  2. Add an f shortcut on the ClaudeBaseUrl field that toggles meta.isFullUrl
  3. Show an inline (full URL) indicator next to the Base URL value when enabled
  4. Apply the same pattern to CodexBaseUrl, GeminiBaseUrl, etc. for consistency

Give me a bit to implement this and I'll push the updated commits.

Per maintainer feedback: the provider form is already dense, so
instead of a permanent ClaudeIsFullUrl form field, add an 'f'
keyboard shortcut on the ClaudeBaseUrl field that toggles
meta.isFullUrl inline. When enabled, the Base URL label shows
an inline '(full URL)' indicator.

- Remove ClaudeIsFullUrl from ProviderAddField enum and visible_fields
- Keep the struct field, toggle method, loading, and serialization
- Add 'f' key handler when ClaudeBaseUrl is selected
- Show '(full URL)' suffix on the Base URL label when enabled
- Remove the standalone help branch (the Base URL help covers it)

cargo fmt + clippy + check all pass.
kingxzq added a commit to kingxzq/cc-switch-cli that referenced this pull request Jul 21, 2026
@kingxzq

kingxzq commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

Reworked per your feedback — the standalone field is gone, replaced with an f shortcut on the Base URL field. Here are the screenshots:

Full URL mode ON (press f while focused on the Base URL field):

full URL on

Note the inline (full URL) indicator on the API 请求地址 label, and the URL points to the complete path …/apis/api-v2/chat/completions.

Full URL mode OFF (press f again to toggle off):

full URL off

The indicator disappears, and the URL reverts to the base path …/v1 (standard path-appending behavior).

Commit eb9a9ca2 implements this with 7 files changed (+14 -29 lines):

  • Removed ClaudeIsFullUrl from the ProviderAddField enum, visible_fields, match arms, and help branch
  • Added KeyCode::Char('f') handler when ClaudeBaseUrl is selected — calls toggle_claude_is_full_url()
  • Added inline (full URL) suffix to the Base URL label when claude_is_full_url is true
  • The struct field, toggle method, loading from provider, and JSON serialization are all preserved

cargo fmt + cargo clippy + cargo check all pass.

(Also: I redacted/rotated the API keys from the earlier screenshot. Sorry again for that.)

SaladDay
SaladDay previously approved these changes Jul 22, 2026

@SaladDay SaladDay left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The follow-up implementation now aligns the Claude and Codex backend paths with upstream and fits the existing TUI workflow. Tests and focused reviews pass. Thanks for starting this work, @kingxzq.

@kingxzq
kingxzq dismissed SaladDay’s stale review July 22, 2026 06:01

The merge-base changed after approval.

@SaladDay
SaladDay merged commit 98363c3 into SaladDay:main Jul 22, 2026
@SaladDay

Copy link
Copy Markdown
Owner

Thanks for the original contribution, @kingxzq. This PR provided the foundation for the full URL workflow. I completed the Claude and Codex TUI integration, aligned the backend paths with upstream, and added coverage for the missing proxy and OAuth cases before merging it. Appreciate your work on this.

@kingxzq

kingxzq commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the original contribution, @kingxzq. This PR provided the foundation for the full URL workflow. I completed the Claude and Codex TUI integration, aligned the backend paths with upstream, and added coverage for the missing proxy and OAuth cases before merging it. Appreciate your work on this.

Happy to see this land! Thanks @SaladDay for carrying it across the finish line — the Codex integration and proxy/OAuth coverage you added on top are exactly what I was missing. Glad the f shortcut pattern worked out as the foundation.

I use cc-switch-cli daily on my internal network, so I'm happy to keep contributing to the project — whether it's bug reports, features, or testing on non-standard environments. Looking forward to the next one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants