Skip to content

feat: add third-party backend account support (upstream, modelMap, models)#74

Merged
MagicalTux merged 2 commits into
KarpelesLab:masterfrom
imnuke:master
Jul 6, 2026
Merged

feat: add third-party backend account support (upstream, modelMap, models)#74
MagicalTux merged 2 commits into
KarpelesLab:masterfrom
imnuke:master

Conversation

@imnuke

@imnuke imnuke commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Changes

src/account-manager.js

  • Added upstream, modelMap, and models fields to the account object
    (both the constructor map and addAccount)
  • Added _accountOwnsModel(account, model): when any account declares a
    models list, only accounts that also list the requested model are eligible
    for that request — accounts without a matching entry are skipped at selection
    time, alongside the existing disabled/quota/Fable checks
  • Wired the check into _isAvailable after the existing Fable-exhaustion gate

src/server.js

  • Per-account upstream URL: account.upstream || upstream so each account can
    target a different base URL without touching the global config
  • rewriteModel(body, modelMap): parses the request body as JSON, rewrites
    the model field using the account's modelMap, and re-serializes; returns
    the original buffer unchanged if the model isn't in the map or the body isn't
    valid JSON
  • Fixes Content-Length after rewriting: the model name change shifts the body
    size, so the header is updated to match before the request is forwarded

Behavior

Fallback: a third-party account with a higher priority value (lower =
preferred) sits idle while Claude accounts have headroom. When they're
exhausted it takes over, and modelMap translates e.g. claude-sonnet-4-6
deepseek-v4-pro[1m] transparently — no change needed in the claude session.

Explicit routing: a models list lets a session opt in to a specific
backend by naming one of its models directly (--model "deepseek-v4-pro[1m]" on launch or /model deepseek-v4-pro[1m] inside a session). Other sessions are unaffected.

Example config

{
  "name": "deepseek",
  "type": "oauth",
  "accessToken": "sk-...",
  "upstream": "https://api.deepseek.com/anthropic",
  "priority": 100,
  "modelMap": {
    "claude-haiku-4-5-20251001": "deepseek-v4-flash",
    "claude-sonnet-4-6": "deepseek-v4-pro[1m]"
  },
  "models": ["deepseek-v4-pro[1m]", "deepseek-v4-pro", "deepseek-v4-flash"]
}

imnuke and others added 2 commits July 5, 2026 18:19
…dels)

- Per-account `upstream` URL routes requests to any Anthropic-compatible
  API (e.g. DeepSeek, GLM) instead of the global upstream
- `modelMap` rewrites the request body's model name before forwarding,
  with Content-Length updated to match the new body size
- `models` array declares model-name ownership: when any account claims
  a model, only those accounts are eligible for requests that name it —
  enabling per-session backend selection via --model or /model without
  affecting other sessions
- Third-party accounts set to a higher priority value act as transparent
  fallbacks when all Claude accounts are exhausted
@MagicalTux MagicalTux merged commit 462c057 into KarpelesLab:master Jul 6, 2026
5 checks passed
MagicalTux added a commit that referenced this pull request Jul 6, 2026
#74 shipped the third-party-backend feature (per-account upstream/modelMap/
models) with no tests. Add coverage for both surfaces:

- Model-ownership routing in AccountManager: ownership is inert until some
  account declares a `models` list; an owned model routes only to owning
  accounts; the [Nm] suffix is stripped when matching; a Claude model claims
  no owner so Claude accounts stay eligible; the third-party account acts as a
  priority fallback; an owned model whose only owner is unavailable selects
  nothing; two accounts can co-own a model.
- Request-body model rewrite (server.rewriteModel, now exported for tests):
  maps a known model, updates the byte length so Content-Length can be
  corrected, and passes through unchanged for an unmapped model, a non-JSON
  body, or a body with no model field.

163 tests pass, lint clean. No behavior change beyond exporting rewriteModel.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

2 participants