Commit Graph
42 Commits
Author SHA1 Message Date
yetone 01d595817c providers: each account of a subscription, and each key, can be kept for some of the provider's models only (#474: 增加账号级别的模型选项, a low-quota but verified account kept for the cheap models) — every account or key a provider had served every model it did, so one couldn't be spared for some. An account's or key's row in its provider's editor now has a models badge: a click opens the provider's models as chips, Save keeps the account for those picked, All models gives it every one again; one kept for some says how many, always shown. magpie provider account-models <id> [account|key [models…|all]] does the same and lists them. It is kept in providers.json as accountModels, an account by its name in lower case, a key by its id; one not in it serves every model, as before. The gateway never sends a model to an account or key whose list leaves it out, in every routing mode, in drag order, in a group and as a fallback, and never falls back to it all the same; the trace says it was left out as set to serve other models. When every account or key there is leaves the model out, the request is refused with a 403 saying so and how to change it, not "none of X's models is ready". TestKeyModelsNarrowCandidates, TestAccountModelsNeverChosen, TestAccountModelsCmd and account-models.test.cjs fail without the change. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass; account-models, click-scroll, old-webkit, account-arrange, account-proxy, account-return, routing-account-order, panel-routing, unlisted-models pass. 2026-10-02 05:50:23 +08:00
czyt bde39aef5e gateway: advertise MAGPIE_PUBLIC_URL in the console and CLIs (#373) 2026-10-02 00:46:45 +08:00
Ns2Kracy bb1aa3acbd feat(gateway): named gateway keys with per-client usage (#344)
* feat(gui): show and filter usage for each API key

* feat(gateway): issue per-user caller keys and attribute usage

* feat(gui): manage gateway users and filter caller-key usage

* test: verify caller-key failover and document user management

* refactor(gateway): use named caller keys without user accounts

* feat(gui): manage named caller keys with existing account styles

* fix: remove user breakdown from CLI and leftover gateway styling

* fix(usage): drop stale user field from request response

* feat(gateway): authenticate LAN calls with named caller keys

* fix(gateway): preserve LAN key rotation in the named caller store

* fix(gui): retain native LAN key copy and rotation controls

* fix(gui): inherit the global theme for API key inputs

* fix(usage): show client API keys using the existing terminology

* fix(gateway): rotate named keys without changing usage identity

* fix(gui): unify gateway keys and selectable connection examples

Keep sharing controls in Settings and manage credentials only in Gateway. Refresh the named-key list on navigation, ignore stale reads after key actions, and generate all API examples from the selected address and enabled key.

* fix(gui): keep caller key names literal in connection menus

* fix(access): use Magpie as default key and drop user migration

* refactor(gui): name caller key source and tests consistently

* refactor(usage): split provider-key accounting out of caller-key PR

* fix(gateway): preserve LAN compatibility and add headless key management

* fix(gui): clarify gateway keys and confirm destructive actions

* test(gateway): guard caller attribution under 120 concurrent requests

* fix(gateway): keep open access, tolerate migration errors and revoke legacy keys

* fix(backup): preserve named gateway keys and honor no-keys restores

* fix(gui): keep local API-key semantics and disambiguate shared credentials

* docs(gateway): keep review scope focused and append browser test documentation

* fix(backup): retain existing default keys when old backups lack their store

* fix(gateway): attribute video creation usage to the caller key

* fix(access): explain default key revocation blocked by read-only settings

* test(usage): preserve provider and caller identities after rebase

* test(gui): reconcile caller key fixtures with merged main

* docs(usage): clarify caller coverage and independent provider accounting

* fix(gui): initialize fast members for model-shortcut groups

Latest main introduced fast-member arrays but omitted the model shortcut's draft. The existing unlisted-models browser regression fails unchanged on main. Initialize fast to an empty list and assert a standard-speed group is saved, without relaxing existing assertions.

* fix(access): retain legacy LAN access when the key store cannot be created

Allow only the configured legacy credential before either migration marker exists. Refuse revoked mirrors, disabled matches and unreadable stores; keep the fallback's caller identity stable when migration later succeeds. Cover read-only configuration directories, remote credential transports and revocation.

* fix(backup): save settings before replacing gateway credentials

Leave the key store untouched when settings validation or writes fail. Restore the previous settings association when the key store refuses its write, and report a rollback failure separately. Cover invalid settings and read-only settings/store failures.

* fix(gui): reset missing caller filters and focus key controls without scrolling

* test(provider): synchronize Zhipu quota fixture observations
2026-10-01 21:28:54 +08:00
yetone 9c54a560cc provider: a providers.json that can't be read is said where the providers are listed, never backed up or synced as none, and is replaced through a rename (PR #415's review) — #415 made edits refuse a file they couldn't read or decode, but every read for the user still took it as empty: magpie providers said "no providers yet", magpie models "no models yet", the Providers page opened the Add sheet as for a first use, and backup.Collect, so an export and a WebDAV/S3 sync, carried it as no providers, which another computer's Mirror would then take as every provider removed. magpie providers and magpie models now list what is left (the signed-in accounts) and end in the error, the TUI says it over its list, and the Providers page shows a "Your providers file can't be read" box (无法读取供应商配置文件) over the list with magpie's reason and no Add sheet; start-up, the gateway and the catalog still go on with it as empty. Stored and StoredGroups return the read error, so a backup and a sync stop on it with nothing pushed. The error, for reads and refused writes alike, names the file, says magpie left it unchanged and to fix it or move it aside (errors.Is ErrUnreadable). store writes through writePrivate, a 0600 temp file renamed over it (steady.Rename waits out a Windows reader holding it, a symlink keeps its target), instead of os.WriteFile's truncate-then-write, and the read goes through steady.ReadFile. TestFileErrorSaysUnreadable, TestWriteErrorsSayLeftUnchanged, TestStoredRefusesUnreadable, TestStoreReplacesAtomically, TestSyncStopsOnUnreadableProviders, TestProvidersCmdSaysUnreadable and provider-file-error.test.cjs fail without the change. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass; provider-file-error, click-scroll, old-webkit, providers-off-fold, add-sheet pass. 2026-10-01 19:45:24 +08:00
yetone b4c6772677 lan: magpie in Docker shares an address other computers can reach (莫 on Discord) — inside a container the LAN address shown was the container's own (172.17…). MAGPIE_PUBLIC_URL now names the share address; without it, a page opened through the host's IP shows that host, and the Gateway page says it runs in a container and how to set the address. lan-docker.test.cjs (Chromium, WebKit, en/zh) and the serve/lan tests fail without the change. 2026-10-01 11:29:05 +08:00
yetone f5a2b5d406 gui: a moved provider's editor reads as the built-in's — its model list dated, its agent's own sign-in said, its move a button (要保证插件订阅 provider 跟内置的订阅 provider所有逻辑都要一模一样) — after a move the list said "magpie's list · Refresh asks the vendor" where the built-in said "vendor list · 9m ago"; it is now dated by when the plugin last listed it, in the editor and in magpie providers. "Command Code itself stays signed in as it is." is kept for Devin, ZCode, WorkBuddy, Kiro and Command Code, whose agents keep their own sign-in through the move; and Move to the plugin / Use the built-in again is drawn as a button, not a line of text. plugin-move.test.cjs fails without the kept line. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass; plugin-move, click-scroll, old-webkit, plugin-market, plugin-signin, nokey-editor pass. 2026-10-01 11:15:27 +08:00
RandyandClaude e8315b18ed gateway: a relay that searches the web by itself is sent Claude Code's WebSearch as it was sent (#359) (#365)
magpie tells the APIs that search by themselves by their host (api.anthropic.com), so a relay in front of Anthropic's API was given magpie's search instead: the request was parsed and built again, and with no searcher set up in magpie the WebSearch tool was left out altogether. A provider now has Searches (magpie provider set <id> search=yes, or the editor's Web search row for a custom provider): a web search offered on its Anthropic or Responses API goes to it as sent. It counts only on an API the provider has an address for, so a Chat-only one isn't taken as searching, and the editor shows the row only while an Anthropic or Responses URL is filled in. A provider said to search isn't one magpie searches with for other models: that would spend the relay's quota, and a relay serving only Claude Code refuses magpie's own request, which has no metadata.user_id.

TestWebSearchOfARelayThatSearches fails without the change (the request built again, magpie searching with the relay, a Chat-only relay taken as searching); provider-search.test.cjs added (Chromium, WebKit, en/zh), its Chat-only case failing without the row's gate. Checked against real Claude Code 2.1.285 through a sub2api relay in claude_code_only mode: WebSearch returns results with search=yes. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass but for TestPluginProxy, which fails on main (813fdcb) as well here.

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-01 11:10:41 +08:00
yetone 140b6161bc cli: magpie provider shows a moved built-in as the built-in read (插件版…保持一模一样) — Zed moved onto its plugin said its account came "from plugin's own sign-in" and listed plugin://zed endpoints no one can reach; it now says zed's own sign-in (another plugin's provider its name's sign-in) and leaves the plugin:// URLs out. TestShowMovedProvider fails without the change. 2026-10-01 04:04:22 +08:00
yetone ba3c26dec4 providers: a Remote magpie provider uses another computer's shared magpie, each request translated at most once (Jorben on Discord: 远程 magpie / magpie 接力) — a custom provider pointed at another magpie has one API per address and couldn't know which API each model's own provider serves, so a request was translated on both computers (Chat → Messages here, then again there). magpie's /v1/models now tells each model's native_endpoints (the APIs its provider serves it on; none for a routing group) and its image input, and reads them, with max_output_tokens and supported_reasoning_levels, from a list; the new remote-magpie preset takes the other magpie's address however typed (host:port, /v1, /v1/messages) for all three APIs and lists all its models, its user's choice already. Add sheet tile with the address field and a hint to Settings → Share on local network; CLI: magpie provider add remote-magpie <key> url=http://<ip>:3425. Checked with two sandbox magpies over the LAN address with the key check on and a Messages-only fake upstream: Chat, Messages (streamed), count_tokens, Responses, a routing group and Gemini all reached it as /v1/messages, translated once. TestRemoteMagpieNativeAPI, TestFetchMagpieFields and TestRemoteMagpie fail without the change; remote-magpie.test.cjs (Chromium, WebKit, en/zh) added. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass but for TestCursorPluginContext, which fails on main (1ce05f0) as well; click-scroll, old-webkit pass. 2026-10-01 01:35:18 +08:00
yetone 52c8e838d2 providers, sign-in, tray, plugins: four of StringKe's reports on Discord — (1) Removing a provider left its editor open and listed, and a second Remove said there was no such provider: the removal had been made, but moving the agents off its models failed on one agent whose config couldn't be written, and Reseat returned that as the change's error; now such an agent is a move with its error ("still on X, not moved: …", a warning in the panel, a line in the CLI and TUI), the others are moved, and the panel, told a delete failed, re-reads the list and closes the editor when the provider is gone. (2) The sign-in waiting sheet shows the sign-in link itself, selected whole on a click, with a copy button, for a browser other than the one that opens. (3) A subscription with several accounts offers "Account in use" among the menu bar's usages, following the account the gateway goes to first. (4) The Antigravity plugin's sign-in never started: magpie called every plugin's authorize with an inputs object, the way OpenCode's CLI does, and opencode-antigravity-auth then asked its Project ID on the terminal — magpie's stdin — until the minute ran out; inputs now go only to a method with prompts, as OpenCode's TUI passes them (checked against the real package under Bun in a sandbox: the Google sign-in URL comes back at once). TestReseatUnmovable, TestTrayInUse, TestInUseOf, TestAuthorizeNoPrompts, provider-remove.test.cjs, signin-link.test.cjs and tray-inuse.test.cjs fail without the change. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass; click-scroll, old-webkit, tray-usages, plugin-signin, signin-callback, factory-sub pass. 2026-09-30 20:45:23 +08:00
yetone 4f01baa65f catalog: fetched again each day magpie stays open, and sooner when a call's model has no price, so a model models.dev lists after magpie started is priced (#224 Koohoko: gpt-6.1-sol shows no cost) — the models.dev catalog, where list prices come from, was fetched only at the app's start (when older than a week), so a magpie left open, or magpie serve, never learned a new model's price. KeepFresh (internal/catalog/fresh.go), run by the app's backend and by magpie serve, looks every hour and fetches when the cache is older than a day; a call whose model has no price asks for a fetch (catalog.Missing, from usage's pricer) when the cache is older than six hours and none was tried in that time, only in a process keeping it fresh, so CLI commands and tests never fetch on their own. Loading and Reset now share a mutex instead of a sync.Once reassigned under a running Do, which a background Sync could hit while a call was being priced. Tested with fresh_test.go against a fake models.dev in a temp XDG_CACHE_HOME: stale after 25h and not before, Missing fetching nothing when not keeping, fetching once when keeping and gpt-6.1-sol then priced, and not again; passes under -race (failed under -race with the Once). go vet, go build -tags nogui (and GOOS=linux, GOOS=windows) and go test -tags nogui ./... pass. 2026-09-30 04:28:01 +08:00
yetone d173bb0f79 serve: the banner no longer says the gateway only listens on localhost when MAGPIE_ADDR puts it on the network (the Docker image of #245 runs it on 0.0.0.0) — there it takes any key from anyone who reaches it until it is shared from Settings, and it now says so; once shared, it says other machines need the key under Settings → Share on local network. Tested with TestKeyNote (loopback, localhost, [::1], 0.0.0.0, :port, a LAN address, and shared). 2026-09-30 03:22:36 +08:00
yetone 037189ebdb agents: a provider switched off or removed, or its last account signed out, moves the agents on its models to others (#200: 希望关闭某个llm provider的时候,更改agent选择的model到可用的model … 关闭这个provider之后,第一次使用会报错,因为copilot/ 开头的模型不存在) — switching Copilot off took copilot/… out of the catalog and the agents' model lists, but an agent picked on one kept it in its config and its next request was refused as switched off. Now every field on one of magpie's models (the model, Claude Code's opus/sonnet/haiku/fable tiers, any other) whose model the change stopped serving is moved: to the same model from a provider still on, the same id first, else one spelt another way (claude-sonnet-4.5 and claude-sonnet-4-5-20250929), else back to the agent's own default (a Claude Code tier: following the main model). It is done around switching off, removing, a key removed or off, and a sign-in signed out or off, in the window, the tray panel, the TUI and the CLI; the page's status line, the TUI and the CLI say which agent went to what. A session started before the switch still asks for the old model: the gateway now takes a switched-off provider's model as unserved, so Claude Code's stand-in (its model as set now) answers it rather than a refusal. Tested in a sandbox HOME: Claude Code on the main model and three tiers plus Hermes, switched off and removed (same id, other spelling, default, a tier following), switched on again moving nothing; /api/provider/off returns the moves; the gateway serves a switched-off model through the stand-in (fails without the change) and still refuses it to an agent left on it 2026-09-29 17:56:43 +08:00
yetone efed707066 providers: a provider can be switched off, kept with its keys but out of every agent's list (希望服务商可以有一个开关按钮,关闭后不再写入模型) — a provider whose quota ran out could only be removed, losing its key and settings, or left in, its models still written into the agents and requests still sent to it. Now each provider (a signed-in account too) has a switch on its row in Providers and in its editor's header; off, the row is dimmed and says Switched off (已关闭), its models leave the catalog, so the model lists magpie writes into agents' files are rewritten without them as after any provider edit, and it is left out of routing groups, fallbacks, the Automatic image, vision and search picks, the routing pools and balance and usage polling. A request naming one of its models is answered 404 saying the provider is switched off in Magpie, not that the model is unknown. Switched on, all of it comes back. Saving the provider from its editor keeps it off; magpie provider off|on <id> and the TUI's o do the same. Tested: an off provider's models are not in the catalog or served, a group with it routes to the member left, a fallback to it is not tried, a request for it is refused as switched off, and turned on it is listed and answers again; an account's off is kept. Checked the Providers page in a sandbox HOME in light and dark: switching a row off and on, the editor's switch and its note 2026-09-29 02:49:36 +08:00
yetone 111f5ba8ef provider: a base URL with a version in its path is used as written, never given a /v1 (tcdw: 提供一个选项能够取消自动给 LLM 提供商地址补全 /v1/ 的默认行为) — a custom provider at Volcengine Ark's Agent Plan, https://ark.cn-beijing.volces.com/api/plan/v3 (its 其他工具 page, docs/82379/2373746), was asked for its list at …/v3/models and then …/v3/v1/models, and only the last failure was shown, so the error named a URL the user never gave. Ark answers both 401 without a valid key (checked unauthenticated: auth comes before routing), so which path it serves can't be told from outside; and had …/v3/v1/models answered, fixV1 would have rewritten the chat base to …/v3/v1 for good. Now an OpenAI-style base with a version segment anywhere in its path (v1, v3, v4, v1beta: Ark's /api/plan/v3 and /api/coding/v3, Zhipu's /api/paas/v4, Gemini's /v1beta/openai) is asked only at base/models and fixV1 leaves it alone; a bare host or an unversioned path (a relay's …/api) is still looked around for the /v1 it may lack, and an Anthropic base still has its /v1/models. A failed list names every URL asked with its status and the vendor's own message ("401 Unauthorized (The API key doesn't exist.)") and says the model ids can be typed in by hand or a models URL given — both already work, the base stays as typed; magpie provider add now prints it instead of saying nothing. No "don't touch my URL" toggle: a versioned URL is never touched, and an unversioned one is only fixed when its /v1 alone answered, when requests at the base as written would miss too. Tested against an httptest server shaped like Ark (/api/plan/v3/chat/completions served, /models 404, a lenient /v3/v1/models) in a sandbox HOME: before, …/v3/v1/models: 404 and, lenient, chat moved to …/v3/v1; after, only …/api/plan/v3/models is asked and the base is kept 2026-09-29 01:19:06 +08:00
yetone 3f3cdff119 stats: magpie counts its users — once a day a running app or magpie serve sends PostHog (the Magpie project) one "magpie active" event under a random id made up on this computer (~/.config/magpie/install-id), with the version, the system and the architecture and nothing else, no person profile kept; off in Settings → Privacy → Count me as a user, or with DO_NOT_TRACK=1 or MAGPIE_NO_STATS=1, and never from a build from source; the README says what goes 2026-09-28 17:23:41 +08:00
yetone 08309e8c04 providers: Test models sends each model agents see a tiny request of its own, on the endpoint it's served on and with a key that sees it, and marks its chip with a dot — green with how long it took, red with what the vendor said — so a relay whose other models answer can't hide one that doesn't (magpie provider test <id> <model>… does it in the terminal). Usage has Hide emails too, one setting with Routing's, to screenshot the subscription cards 2026-09-27 12:26:00 +08:00
yetone d7999aefc7 providers: a balance endpoint named on a provider can be asked with an access token of its own (balance.token), with the provider's headers — a new-api relay's /api/user/self tells the whole account's quota to its access token and New-Api-User, not to a key: magpie provider set my-relay balance=…/api/user/self balance.path='$data.quota / 500000' balance.token=… header.New-Api-User=… 2026-09-27 02:30:55 +08:00
yetone 6fd85894ea routing: Jev (TypeSafe's System One) as a group's classifier — asked once as a turn begins which of the rules' intents the message is, with how sure it is, and, for a group with effort auto, how hard the turn is to think about; the turn's requests then ask their model for that reasoning, where the agent asked for any. TypeSafe is a decision provider: it serves no conversation and is no group's member 2026-09-27 01:53:04 +08:00
yetone f12a75899d providers: a subscription whose models come from its own CLI (Kiro, Devin, Cursor …) has no host, so magpie provider models said its models were fetched "from" nothing; it names the provider. Kiro signed in with Google or GitHub says so, not SocialGoogle. Checked against a real Kiro account: plain and streamed text, a tool call and its result, OpenAI chat with another model, and Claude Code reading a file through Kiro 2026-09-27 00:51:17 +08:00
yetone 47e12df418 models: a provider's model can go by a name of the user's, and offer only some of its reasoning levels — Claude Opus 5.5 · Claude Code can be Opus 5.5, and a model with six levels can offer just low, medium and high. Both are kept in settings.json by provider/model (modelNames, modelEfforts), apart from the vendor's list, so a Refresh, a restart or an upgrade leaves them, and the same model from another provider keeps its own; a provider renamed takes them along. They are applied where the catalog is made, so the gateway's /v1/models, the lists magpie writes into the agents (Codex's model catalog, ZCode, Pi, OpenCode, OMP …) and magpie's own pages all show them, and a change rewrites the agents' lists as any other change of the catalog does. Only what is shown changes: the id, where requests go, and the levels the vendor is asked at stay as they were; a found routing group keeps the model's own name. The provider's editor has Names & levels under its models — a name typed in place, a tick per level, Restore default — and the terminal has magpie model name <provider/model> <name>|--reset and magpie model efforts <provider/model> low,medium,high|--reset. Issue #92 2026-09-26 23:23:45 +08:00
yetone c4268e524f providers: a provider's id can change — its editor has an ID field (magpie provider set <id> id=<new> in the CLI), and renaming it moves what named it by the old one: the agents on its models (each spelled its own way, recorded as magpie's), the routing groups' members, rules and classifier, other providers' fallbacks, which agents are shown it, and the vendor's fetched list. The old id stays with it, so an agent still running on the old config reaches it, a profile saved before applies to the new id, and its usage so far is counted under the new one. Asked on Discord 2026-09-26 20:22:30 +08:00
yetone 9b06c30790 balance: a custom provider's balance field can name several amounts, each with a label, apart by ";" — Command Code's credits reply is 5h: windowLimits.fiveHour.used / windowLimits.fiveHour.cap %; week: windowLimits.weekly.used / windowLimits.weekly.cap %; $credits.monthlyCredits, shown as "5h 0% · week 3.4% · $70.00" wherever the balance is (Usage, magpie provider, magpie quota). Asked on Discord 2026-09-26 18:41:01 +08:00
yetone 73be42eca8 models: each agent can be shown only some of them — a provider or group gets a family (magpie provider set relay-x family=relay), and magpie visible zcode relay shows zcode only that family's models (or named providers and groups): in the files magpie writes for it and in what /v1/models answers it, told apart by its User-Agent; magpie models zcode lists what it is shown and what is kept from it, and magpie visible zcode all shows it everything again. A model kept from an agent is still served when asked for by name. Asked for on Discord 2026-09-26 16:02:29 +08:00
yetone c83e03d5c7 provider: how long a request a model takes can be set — magpie provider set <id> context=272k (all its models) or context.<model>=1m, and magpie group set <id> context=… for a group, which otherwise takes its shortest model's; agents are told it (Pi's contextWindow, ZCode, Codex's catalog, Grok, omp, dsh) over what the vendor or models.dev says, and the Providers and Routing views keep it when saved. An empty value clears it 2026-09-26 14:39:55 +08:00
yetone 5f839eae99 provider: saved Claude or Codex accounts that are not offered say why — under a HOME where the agent is not signed in (a magpie serve with its own HOME, no ~/.codex/auth.json or ~/.claude/.credentials.json there) they went missing without a word; magpie serve, magpie providers and the Providers view now tell 2026-09-26 14:32:48 +08:00
yetone 6b39a69bf4 provider: a balance field can be a sum — paths into the reply and numbers with + - * / and brackets, "(1 - credits.monthlyCredits / 70) %" for what is left of a plan, a "%" after it showing a percent of 1; "data.total / 500000" and "$data.x" read as before 2026-09-26 14:26:28 +08:00
yetone c330e20231 provider: an account's "only through routing groups" holds, and one removed stays removed — All() merged a signed-in account's picks without Unlisted, so provider listed claude no saved but claude/* stayed in /v1/models; and any save of an account rebuilt its entry without Hidden, so a removed one came back; a save now keeps it removed, and Add it back (and magpie provider add <account>, which made claude-2) goes through ShowAccount, keeping its picks; and magpie provider set <id> k=v… changes a provider with add's pairs (the v0.1.77 notes named it, it didn't exist) 2026-09-26 01:38:37 +08:00
yetone 81342b1b05 provider: a custom provider can list its models from a URL of its own — Xiaomi MiMo's plans are served at their own hosts while the list is at api.xiaomimimo.com/v1/models, so fetching models from the base URL found nothing; More endpoints has a Models URL (模型列表地址), asked with the key and nowhere else, and magpie provider set <id> models.url=… sets it (#67) 2026-09-25 22:16:11 +08:00
yetone 925e4a6ccd provider: a provider can serve only through routing groups — ticked, its models leave the list agents pick from (and /v1/models) while the groups they are in, and a request naming provider/model, still reach them (magpie provider listed <id> no); the routing groups come first in the list; a model list fetched from the vendor can be forgotten; magpie group add under a name in use replaces that group; and the Library gives MCP servers to Claude Desktop, the ones it runs as a command — a remote one is added in its Connectors, which the page says 2026-09-25 20:11:29 +08:00
yetone 35ebc94d58 provider: a preset's provider takes request headers of the user's own, and a preset can be added more than once (#50) — which workspace a key that reaches several is for (anthropic-workspace-id), or who the app is to OpenRouter, could only be said by a custom provider, so it meant giving up the preset's endpoints and catalog; the editor now shows headers for presets as well, offering the ones the vendor documents with their values left to fill in, and "Add another" makes one more provider of a preset under an id of its own (anthropic-2) where adding again replaced the first; the same key, host and headers twice is refused as the one already there, and importers keep a preset's headers and its preset when they split an entry off to a free id 2026-09-25 09:36:42 +08:00
yetone 520a2425fc gateway(routing groups, affinity) + gui(routing): routing groups — several models, from one provider or many, that an agent picks as one (group/<id>), routed over all their members' keys and accounts together, smart, in order, in turn or least used first; a model more than one provider serves is a group magpie finds on its own, and becomes the user's once changed. Affinity keeps a conversation with the key or account that answered it — for the session, within a turn, never, or by default while what the vendor read from its cache is worth keeping and not yet cold — so the prompt cache holds; in turn now spreads turns rather than requests, a turn's tool results staying where it began. The Routing view shows each agent's requests in its own colour, the magpie flying them through the gateway's box rather than jumping across it, groups and why a conversation stayed or moved; and a Hide emails switch blurs the email addresses on the page, for a screenshot to share 2026-09-25 04:07:40 +08:00
yetone 2b1e9e8619 usage: the Usage page shows what is left on an API key beside the subscriptions' allowances — DeepSeek, Kimi, OpenRouter and SiliconFlow are asked by their hosts, and any custom provider can name its balance endpoint and where the amount sits in the reply (Balance URL / field in the editor, balance= / balance.path= in the CLI), each key it has on asked with itself; magpie provider <id> prints it too (#40) 2026-09-25 01:12:18 +08:00
yetone 435d65882b gateway: fallback — when a provider is out of quota, rate limited or down, a request goes to the next model you listed for it, before any of the reply is sent; set it in the provider editor or with magpie provider fallback 2026-09-24 14:04:42 +08:00
yetone 61004668ac provider: a custom provider can have its own icon — a picture of yours (PNG, JPEG, SVG…) or any built-in one, in the editor or with magpie provider icon (#12) 2026-09-24 13:10:45 +08:00
elonnzhang 3ffebeebb6 provider: custom request headers for custom providers
Extra HTTP headers, typed by the user, ride on every request magpie makes
to a plain key+URL provider — forwarded calls, connectivity tests, and
model-list fetches — applied after the auth headers. Presets and signed-in
agent accounts ignore them. Editable in the GUI (with a JSON value mode) and
via the CLI as header.X-Foo=bar.
2026-09-24 12:50:51 +08:00
yetone d77e14701b provider: a signed-out Claude Code account goes away, and any account can be removed (#9)
Logging out of Claude Code can leave its credentials behind; claude auth status
now decides. Removing a Claude, Codex or Copilot account hides it from magpie
(the agent stays signed in); Add it back, or magpie provider add <id>, restores it.
2026-09-24 12:42:14 +08:00
yetone 4bd3f3a4b5 import: magpie://import links add a provider after the user confirms
A vendor or relay can hand out a ready-made provider as a link. The app
registers the magpie scheme (Info.plist on macOS, HKCU on Windows, a
.desktop handler on Linux), a second launch hands the link to the running
one, and the window shows what the link would add, with the hosts the
prompts and key would go to, before anything is saved. `magpie import`
does the same in a terminal.
2026-09-24 12:17:45 +08:00
yetone 402a96c39d Rename dial to magpie, with a magpie for a logo
The product, module path (github.com/yetone/magpie), binaries, env vars
(MAGPIE_ADDR, MAGPIE_DEV_UI, MAGPIE_THEME, MAGPIE_DEBUG), config and cache
folders (~/.config/magpie, ~/.cache/magpie), bundle id (com.yetone.magpie),
gateway provider id and token, and every UI string now say magpie.

On first run magpie copies ~/.config/dial and ~/.cache/dial to the new
folders when they don't exist yet (copy, not move, so a still-running dial
keeps working; the old folders can be deleted by hand later).

The logo is a small round magpie in profile: one path for the header and
the tray, and the app tile adds the white belly and the blue-green tail.
The header mark is drawn in ink rather than the accent colour, which
fought the text in light mode.
2026-09-23 23:55:11 +08:00
yetone 9174a6510b provider: signed-in agents (Codex, Copilot) become providers
A Codex (ChatGPT) or Copilot login is a subscription with models behind
it, so dial offers it as a provider: every other agent can use its models
through the gateway. The agent's own credential file is read each time,
tokens are refreshed the way the agent does it, and only the model picks
are stored. Claude Code is found but deliberately not offered (Anthropic's
terms), and the CLI and API say so.

The gateway signs upstream requests through the provider (Sign/Prepare),
translates non-streaming requests for backends that only stream, and now
recognises an event stream by its body when the vendor sends no
Content-Type, which is what the ChatGPT backend does.
2026-09-23 15:25:05 +08:00
yetone 8fb93f8fc2 Make dial a local LLM gateway with preset providers and one catalog
dial now serves OpenAI chat completions, OpenAI Responses and the
Anthropic Messages API on 127.0.0.1:3425 and forwards each request to the
vendor that serves the model, translating between the APIs when the vendor
speaks a different one (streaming, tool calls and reasoning included).
Every agent points at that one URL; models are spelled provider/model.

Providers are added from presets (vendors, relays, local servers) with
just an API key, or as custom OpenAI/Anthropic-compatible endpoints.
Keys live in ~/.config/dial/providers.json; nothing is read from the
shell environment. Each provider chooses which of its models to expose.

Agents get gateway wiring instead of vendor-specific tables: Claude Code
via the env block, Codex via a dial model provider plus a model catalog
file, OpenCode/Pi/Crush via a dial provider entry; native settings are
stashed and restored when a native model is picked again.

The Providers page shows the gateway, each provider with the agents on it,
an inline editor with model chips, and an add sheet of preset tiles.
CLI: dial presets | providers | provider add|key|models|test|rm | models | serve.
2026-09-22 22:43:51 +08:00
yetoneandClaude 0a234ce41d dial: one dial for every coding agent's model
Wails v3 tray panel and window, TUI and CLI. Switches model, effort and
provider for Claude Code, Codex, Gemini CLI, OpenCode, Pi, Goose, Cursor,
Copilot CLI and Crush with format-preserving config edits. Providers page
and registry, stored keys, live vendor model lists, profiles.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-22 21:47:29 +08:00