Skip to content
dsh-market Browse plugins GitHub 中文

Ztyss/dsh-llm-provider

Replaces the built-in llm-pi-ai and llm-deepseek adapters, the model selector and the Models settings page with self-maintained versions, tracks upstream @earendil-works/pi-ai through a safe-zone download with a one-click enable toggle, and adds provider quota and balance lookups plus a provider management page.

Stars ★ 1 Category Models & Providers Listed 2026-09-23

Install

Inside DeepSeek Harness, with dsh-market

dsh plugin --profile web add dshmarket

Or from the command line

dsh plugin --profile web add github:Ztyss/dsh-llm-provider

Installing runs third-party code with your own permissions — it can read your files, use your credentials and reach the network. Review the source first, and pin a commit (github:owner/repo#sha) when you can.

README

A dsh (DeepSeek Harness) plugin that takes over the official pi-ai adapter (llm-pi-ai), the native DeepSeek adapter (llm-deepseek), the model selector (ui-model-selection) and the Models settings page (ui-settings-models), providing a self-maintained pi-ai bridge, model selector, quota lookups and provider management UI.

中文 · English

This repository is forked from imchangchang/dsh-llm-provider and maintained independently since. Current version v0.2.3 (installs from main).

Install

dsh plugin --profile web add github:Ztyss/dsh-llm-provider   # private repo; git needs credentials (gh auth login)
dsh web     # restart required: the plugin tree is assembled at process start

The build output (lib/) ships with the repository, so it works right after install. After changing source, run npm run build and commit lib/ together with src/ (there is deliberately no prepare script: pnpm runs it in a temp dir on git installs where tsdown is missing, which fails the whole install).

Features

pi-ai bridge: enable the latest version

The "Enable latest pi-ai" toggle in the settings page (Provider → pi-ai bridge) runs the official pi-ai adapter on the newest @earendil-works/pi-ai downloaded from npm, replacing the DSH-bundled copy — new pi-ai versions and model support arrive without waiting for a dsh release.

  • ON = a standing intent: pull dist-tags.latest of @earendil-works/pi-ai from the npm registry — sha512 verification → extract → install the dependency closure → probe against the bridge copy's own import requirements — into the safe zone $DSH_HOME/llm-provider-bridge/pi-ai/<version>/ (the plugin package can be recursively deleted at any time, so hundreds of MB never live inside it). After a restart the bridge link points at the downloaded copy, replacing the DSH-bundled one; a failed probe falls back automatically, so the switch always has a safety net.
  • OFF = fall back to the DSH-bundled copy: downloaded files are kept, and flipping back ON costs nothing.
  • The toggle preference also lives in the safe zone: $DSH_HOME/llm-provider-bridge/vendor-status.json — the preference used to be written inside the package as vendor/status.json, and the package manager rebuilds the plugin directory from scratch on every reinstall/upgrade, resetting the switch to OFF. Moved out, a reinstall loses nothing (an in-package legacy file is merged into the safe zone on first read, then removed).
  • Only the newest downloaded version is kept: loadBridge cleans older ones after the switch has completed (the running process never steps on a directory being deleted).
  • The network is touched only while the switch is ON, in two places: the flip itself, and one check on every startup — no local copy ready, the download starts right away (the UI enters its downloading state, no second flip needed); a copy ready, npm is still checked and a newer version downloads automatically (updateDecision skips "latest ≤ newest local"). The 60-second floor is crash-loop damping, not a throttle on intent. OFF never touches the network.
  • The version row tells the truth: current x.y.z (DSH-bundled / npm latest / vendor) — which copy is in use at a glance. The toggle row only speaks when there is something in progress or to do: downloading / x.y.z downloaded (restart to apply) / x.y.z updated (restart to apply) / x.y.z downloaded (restart to fall back to official) / x.y.z downloaded (cannot be enabled) / x.y.z downloaded (not in use). Whether a restart is pending is derived live by piAiNeedsRestart (preference + newest ready safe version + the copy currently loaded) — the enable-in-place state (nothing to download) says so too; a pending version that this very check actually downloaded (lastCheck.installed is it — typically the startup auto-update after a restart) says "updated", while a copy that was already on disk says "downloaded"; verification failures live in the detail rows. While downloading, poll every 2s.

Model services settings page

  • Bilingual (zh/en): dictionary + tf interpolation through dsh's own locale mechanism, following language switches live.
  • Inline provider editing: display name, endpoint, protocol and credential name are edited right on the card — no edit mode, no separate form. With no changes the card shows no editing affordances at all; as soon as a draft exists, "Save changes / Cancel" appear. Only changed fields are written, clearing a field removes the key. The route id stays read-only.
  • The model list is the checklist: expanding "Models (N)" shows checkboxes directly, decoupled from any edit button. Existing entries are read-only (capability badges + formatted context, same columns as the display list); configured rows get an ✕ to delete them in one click; only custom ids not in the catalog need context window / max output and vision / video. Saving goes through the dedicated route POST /provider/set-models (server-side validation, native routes rejected), with the declared entry as the base — hand-written reasoningEfforts / compat survive; "Follow catalog (restore)" deletes the key and returns to the full catalog.
  • pi-ai bridge card, now a toggle: the "Enable latest pi-ai" switch plus a status line (current version + source / already latest / downloaded, restart pending / failed verification with the reason, persistent); after flipping ON — and during each startup check — it polls every 2s until the download settles.
  • Delete confirmation modal: cost list + "Export config (YAML)" backup (secrets excluded).
  • Three-state capability badges: supported / explicitly unsupported / unknown are rendered separately; models missing from the catalog get capabilities from the route declaration and adapter self-report (modlens-style synthetic providers).

Console cookie storage

Providers that need a console session (StepFun's Step Plan points, for one) keep their cookies in $DSH_HOME/llm-provider-bridge/.cookies.yaml — one file, dot-prefixed to match the .credentials.yaml habit; same schema as credentials (version + flat key table), keys are credential ref names (e.g. STEPFUN_CONSOLE_COOKIE), so a new provider or cookie is just another key. Parsing tolerates hand edits, and a bad row never kills a lookup.

  • Legacy single-slot session files migrate automatically: stepfun-console-session.json is merged into the new file on first read, then removed.
  • Seed fingerprint dedup (write convergence): an entry records seedSha, the fingerprint of the seed credential — later quota lookups for the same credential reuse the stored value instead of writing the static credential back. Previously one lookup meant two writes plus a pointless renewal RPC; now writes happen only on two real changes: repasting a cookie, and renewal rotation.
  • Fragment seed guard: a seed must contain an Oasis-Token section — a fragment with only Oasis-Webid can no longer overwrite a rotated good jar.
  • First write on a fresh machine: the bridge directory is created when missing.

Model selector

  • Full takeover: once the official model selector is disabled, the selection seat (current model state), the /model command and the model-catalog state machine are all provided by this plugin.
  • The /model command: filter by provider, search models, candidates grouped by provider with quota/balance shown per group.
  • The available contract: implements the officially required available(session) for / command contributions — sessions addressed as subagents get no model selection; the host calls it unguarded, so one throw would take the whole / candidate batch down — the implementation never throws and swallows every exception, preferring to show one extra menu entry.

Stability & safety design

  • Pre-load integrity check and candidate chain: src/pi-ai-source.ts verifies the manifest, the entry file and the four subpaths the official bundle actually imports; when broken it prints an executable restore recipe (npm pack over the host directory — the plugin never downloads for you). piAiCandidates() = safe zone (when ON, newest → oldest) → legacy vendor tiers → bundled dependency → DSH-bundled, probed in order, first pass wins.
  • The bridge workspace lives outside the package, in $DSH_HOME/llm-provider-bridge/: on Node ≥24.15 rmSync follows junctions and empties their targets, and in this layout there is no link left to follow. Invariant: the installed package contains zero links (enforced by test/host-safety.mjs).
  • The npm cache never sticks around: during a download the npm cache lives in the OS temp dir and is deleted when the install finishes (success or failure); on startup a sweep removes any legacy .npm-cache left in the safe zone.
  • Conditional disable of the official entries (fail-open): cordis.patch.yml uses !!js expressions — the official entries are disabled only while the host's pi-ai is intact; if it is missing or broken they stay enabled and DSH boots normally. Generated by src/patch-condition.ts, kept in sync with scripts/sync-patch-condition.mjs --check.

Tests

npm test                      # build + an 18-step offline chain (no dsh needed; the last step is the patch-condition drift check)
node test/host-safety.mjs     # junction-safety regression: run under BOTH node 24.14 and the DSH runtime (≥24.15)

Content from the project README on GitHub ↗

Comments

Comments live in GitHub Discussions. Sign in with GitHub to post or react.