Skip to content

New-upstream wizard v2: service groups, per-service steps, account sign-in in the wizard - #317

Closed
fylorn wants to merge 3 commits into
devfrom
feat/upstream-wizard-v2
Closed

fylorn wants to merge 3 commits into
devfrom
feat/upstream-wizard-v2

Conversation

@fylorn

@fylorn fylorn commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

What

The new-upstream wizard, second version.

Step 1 — Service. Three groups: Model vendors (Anthropic, OpenAI, Google Gemini, DeepSeek, Z.ai / BigModel), Platforms and relays (Amazon Bedrock, OpenRouter, ThinkWatch, Sub2API, New API / One API) and Local and custom (Ollama, Custom), each tile with the one-line description from the design (ThinkWatch: "ThinkWatch 企业网关", Sub2API and New API: "API 中转平台"). Real marks from @/ui/logos; ThinkWatch draws the app's own TW mark (src/ui/app-mark.tsx, strokes shared with the launch screen through src/ui/tw-strokes.ts); Sub2API and New API have no mark we may use and keep the initial-letter tile. Search matches name, description and common names ("ChatGPT" finds OpenAI, "智谱" finds Z.ai). Picking a tile goes straight to step 2. The separate "sign in with an account" group is gone: signing in is an authentication method of OpenAI and Z.ai.

New presets:

Tile Base URL Protocol Auth balance written
OpenRouter https://openrouter.ai/api (core appends /v1/chat/completions, /v1/models) OpenAI Chat, fixed API key — (auto resolves openrouter.ai)
ThinkWatch entered auto (the gateway takes every dialect) API key / OAuth thinkwatch
Sub2API entered choice, auto by default API key / OAuth sub2api
New API / One API entered choice, auto by default API key / OAuth newapi
Z.ai / BigModel (API key) by site: api.z.ai/api/anthropic, open.bigmodel.cn/api/anthropic Anthropic, fixed API key / account —

Step 2 — Connection / Account. A full-width, equal-segment Authentication row, only when the service has more than one method: OpenAI → API key / ChatGPT account; Z.ai / BigModel → API key / account sign-in; Bedrock → API key / access keys / AWS profile; Custom, ThinkWatch, Sub2API, New API → API key / OAuth; everything else has no row. Name and outbound proxy share the next row in every method. What the tile decided is not asked again (presets.ts): the protocol select appears only where there is a real choice (OpenAI: Chat Completions / Responses; relays and Custom: all plus auto-detect), Z.ai's URL comes from its site control, Ollama has no API key field, "Forward client identity" only shows for relays and Custom, and services that need a key cannot go on without one. There is no balance-source control anywhere; toInput writes balance from the tile.

Account sign-in inside the wizard (ChatGPT, Z.ai). The step is labelled Account and the login panel replaces the connection fields. Everything the two login dialogs did is kept: the notes with the "已阅读上述说明" checkbox (minus the "requests say they come from ThinkWatch" line), Z.ai's site choice, the name checks (ChatGPT names must be free; a Z.ai name on the same site replaces that upstream's key), proxy, errors, expiry, reopen and cancellation. New:

  • Copy sign-in link: starts the browser sign-in without opening the default browser and copies the link; while waiting, the link is shown with a Copy button.
  • Device code for ChatGPT locally too; switch between browser and device code while waiting.
  • Remote core: ChatGPT offers only the device code — verification address with Copy and Open, the code large with Copy, minutes left and "get a new code". Z.ai polls and has no local callback, so it keeps the browser flow (and the link) remotely.
  • After sign-in the upstream exists: the panel shows the account (email, plan, ChatGPT quota windows), Back is gone, Next leads to Models and Billing, which edit the created upstream through the edit path; the last button reads Done and writes only if something changed.
  • Closing the wizard while waiting cancels the sign-in; Cancel sign-in returns to the panel.

The standalone Z.ai dialog is deleted. ChatgptLoginDialog became ReloginDialog, used only to sign an existing upstream in again (a ChatGPT upstream's Account section, or a new key for a Z.ai / BigModel upstream, see Editing), built from the same hook (accountLogin.ts) and panel (AccountPanel.tsx).

Rust (src-tauri). start_chatgpt_login / start_zai_login take open and return the authorize URL for display. Copying and opening still go by login ID on the Rust side: copy_chatgpt_code became copy_chatgpt_login(id, item: "url" | "code"), copy_zai_login(id) is new, and reopen_chatgpt_login opens the verification address for a device-code sign-in.

Types. Core's protocol 46 (ThinkWatch-Core#321) is being built in parallel. Added by hand to src/generated/tw-api.ts, following that branch: BalanceSetting = "auto" | "off" | "openrouter" | "deepseek" | "moonshot" | "sub2api" | "newapi" | "thinkwatch", ProviderInput.balance?: BalanceSetting | null and ProviderView.balance_setting (optional here, required in core; the regenerated file from core replaces all of it, and scripts/shots/core/ answers get regenerated with oracle.sh then).

Balance round-trip. The edit form fills balance from ProviderView.balance_setting and sends it back as ProviderInput.balance, so saving (or toggling) an upstream never turns an off or a named source into auto. New upstreams write the tile's source (Sub2API, New API, ThinkWatch); OpenRouter, DeepSeek and everything else leave it to auto, which recognises their hosts and the enterprise gateway.

The Bedrock draft path (create from a client takeover) still opens on step 2 with the type decided.

Editing

The edit dialog applies the same per-service rules as creating. serviceOf (in presets.ts) recognises the saved upstream: official hosts (api.anthropic.com, api.openai.com, generativelanguage.googleapis.com, api.deepseek.com, openrouter.ai, api.z.ai / open.bigmodel.cn with the site), Bedrock by its standard address or protocol: bedrock, Ollama by port 11434, and Sub2API / New API / ThinkWatch by balance_setting (they live on arbitrary hosts). Everything else is Custom; ChatGPT accounts keep their own Account section and re-login.

  • The header shows the recognised service (name · Anthropic, the TW mark for ThinkWatch, S / N for Sub2API / New API); Custom keeps name · protocol · address.
  • The authentication row offers that service's methods, without the account option (switching an API-key upstream to an account is a different upstream). The service itself cannot be changed; changing a recognised upstream's address to another host makes it Custom from then on (retarget), with the full form.
  • A value that is set is never hidden (fieldRules, decided by what the config held when the dialog opened, so a field cannot vanish while it is being edited): an Anthropic-host upstream saved with OAuth gets Custom's authentication choices with OAuth selected and kept; an Ollama upstream with a key shows the key; a protocol other than the one the service fixes (or none on a host that does not auto-detect to it) shows the full protocol list; a Z.ai upstream on a non-standard path shows its address; "Forward client identity" shows wherever it is on; headers always show. Editing never requires a key (a saved upstream may authenticate with its own header).
  • Z.ai / BigModel: a key written by sign-in looks exactly like a typed one in config (core keeps no marker), so the current method shows as API key. Under the key there is "登录账号,换一把新密钥", which opens the re-login dialog (now ReloginDialog, shared with ChatGPT) for that site; core replaces only the key of the same-named upstream on the same site. Z.ai's notes and checkbox are shown there, since the key may never have come from a sign-in.

Tests: recognition for every host and setting, re-recognition on address change, and "a set value is never hidden" cases (OAuth on an official host, Ollama key, foreign protocol, non-standard Z.ai path, client identity, key not required when editing).

Checked in the preview harness: editing each mocked upstream (anthropic, openrouter, deepseek, gemini, ollama, zai, bigmodel, chatgpt) and Sub2API / ThinkWatch / New API upstreams created through the wizard, saving with balance kept, moving Anthropic to another host, and the Z.ai sign-in from the edit dialog; light and dark, Chinese and English.

Where this deviates from the design canvas, and why

  • Login link before starting. The canvas shows the URL row before sign-in starts. The link only exists once a sign-in has started (it carries that sign-in's one-time state; ChatGPT's also holds the local callback port, Z.ai's starts polling their service), so creating it just to show it would start a sign-in nobody asked for. Before starting there is a Copy sign-in link button with the canvas's hint; it starts the sign-in without opening the default browser. From then on the URL row is shown as on the waiting board.
  • API key layout. Name and outbound proxy sit in one row under the authentication row in every method (as on the account boards), so switching methods does not move them. The protocol select moved under the URL and appears only where there is a choice. "When the proxy is unavailable" sits next to the concurrency limit. Concurrency and "Forward client identity" are not on the canvas but exist today and are kept (the latter only where it can matter).
  • Z.ai site for API keys too. The site decides the endpoint, so the URL field is hidden; the hint under the site names it.
  • Balance field on the Sub2API board dropped, per the product rule; the test-result balance line on that board waits for protocol 46 and is not in this PR.
  • Remote subtitle. The header does not add "连接的是 X"; the device-code panel says core runs on X instead.
  • Footer after sign-in reads "上游已创建,关闭对话框不会撤销登录": that step has no Cancel button, closing is the ×.
  • Ollama uses its real mark instead of the canvas's monitor glyph; the outbound-proxy note under the proxy select was dropped (it described something that works as intended).
  • Done card shows every ChatGPT quota window (5h and weekly), not only weekly.

Known gaps

  • Balance readings (ProviderView.balance, the test result's balance line on the Sub2API board) are not shown here.
  • Z.ai's quota is not shown in the done card (only ChatGPT usage can be asked for at that moment).
  • Existing ThinkWatch upstreams in the table keep the initial-letter tile (no fixed host to recognise them by).

Verification

  • pnpm typecheck, pnpm test (105 files, 1117 tests; new presets.test.ts and form tests for auth options, naming, site, key requirements, balance from the tile, and an off / named / auto setting round-tripping through the edit form).
  • cargo fmt --check and cargo clippy --all-targets -- -D warnings on src-tauri; cargo test --no-fail-fast passes except ts_bindings::the_committed_bindings_match_the_pinned_core, which fails on purpose until a core with protocol 46 is pinned: src/generated/tw-api.ts carries the hand-added balance types described above. Pinning the new core and regenerating (UPDATE_TS=1 cargo test --test ts_bindings) clears it.
  • Walked every tile to step 2 and on through Models, Billing and Create in the isolated preview harness, plus each sign-in (ChatGPT browser, copy link, device code, switch, cancel, close while waiting; Z.ai both sites with copy link) locally and with a remote core, the ChatGPT re-login, and the Bedrock draft path; light and dark, Chinese and English.

🤖 Generated with Claude Code

fylorn and others added 3 commits October 10, 2026 22:28
Step 1 lists the services in three groups (model vendors, platforms and
relays, local and custom) with real marks and one-line descriptions, and
adds OpenRouter, ThinkWatch, Sub2API and New API / One API. Picking a
tile goes straight to step 2.

Step 2 asks only what the tile did not decide: an authentication row
appears only when the service really has more than one method, the
protocol select only where there is a choice, Z.ai's URL comes from its
site, Ollama has no key field. The balance source is never asked for:
Sub2API, New API and ThinkWatch write it from the tile, everything else
leaves it to detection.

Signing in with a ChatGPT or Z.ai account now happens in the wizard's
second step instead of separate dialogs, keeping their notes, checks and
cancellation, and adding a copyable sign-in link, the ChatGPT device code
locally, and a device-code-only flow when connected to a remote core.
After sign-in the upstream exists, so Models and Billing edit it through
the edit path. The standalone Z.ai dialog is removed; the ChatGPT dialog
remains for signing in again from an existing upstream.

The shell returns the authorize URL for display; copying and opening
still go by login ID, so the webview never hands Rust a URL to open or
text to put on the clipboard.

ProviderInput.balance is added to the generated types by hand until core
protocol 46 is pinned.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Core's protocol 46 reports what config says as ProviderView.balance_setting.
The edit form now fills from it and sends it back, so saving or toggling an
upstream no longer turns `off` or a named source into `auto`. New upstreams
still take the source from the tile.

BalanceSetting and ProviderView.balance_setting are added to the generated
types by hand until core protocol 46 is pinned.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Editing used to show every upstream as Custom, so an Ollama upstream
asked for a key, an Anthropic one offered OAuth and a Z.ai one showed a
raw address. The dialog now recognises the service from the saved
upstream (official hosts, Bedrock, Ollama's port, and the balance setting
for relays and the enterprise gateway) and asks what creating it would
ask, with its logo and name in the header. Changing the address to
another host makes it Custom from then on.

A value that is set is never hidden: whatever the config held when the
dialog opened and falls outside the service's rules (OAuth on an
official host, a key on Ollama, another protocol, a non-standard Z.ai
path, client identity) is shown the Custom way. Editing never requires a
key.

A Z.ai / BigModel upstream gets "sign in to replace the key", through
the re-login dialog now shared with ChatGPT; core replaces only the key
of the same-named upstream on that site.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@fylorn

fylorn commented Oct 10, 2026

Copy link
Copy Markdown
Contributor Author

Merged as part of #318 (2026.10.14).

@fylorn fylorn closed this Oct 10, 2026
@fylorn
fylorn deleted the feat/upstream-wizard-v2 branch October 10, 2026 17:00
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.

1 participant