dsh-pi-agent-providers
Verifieddsh-pi-agent-providers · v0.1.2 · MIT
The pi agent's provider catalog (~/.pi/agent/models.json) as DeepSeek Harness LLM routes — installable and removable with `dsh plugin`, no dsh source patch
Install
dsh plugin add dsh-pi-agent-providers Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Tags
Creators
Readme
dsh-pi-agent-providers
dsh-TUI · 一个为 dsh-TUI 生态打造的插件
Imports the providers in pi's
~/.pi/agent/models.json as DeepSeek Harness
llm routes, so models you already configured for the pi agent appear in the
harness model pickers. Keys are read from the pi file on each request and are
never copied into ~/.dsh.
~/.pi/agent/models.json -> pi-kividas, pi-code-for, pi-20xpin, ...
Install
dsh plugin --profile <name> add dsh-pi-agent-providers
Restart the profile. To remove:
dsh plugin --profile <name> remove dsh-pi-agent-providers
dsh picks up the package's cordis.patch.yml through its dsh.bundle.patch
field, so removing the dependency removes the layer. Nothing in dsh or dsh-TUI
needs to change.
pnpm reports this package's @deepseek-ai/* peer dependencies as missing during
install. That is harmless: dsh installs the framework tree once under
$DSH_HOME/profiles/node_modules, so the plugin resolves the same
@deepseek-ai/dsh-llm-pi-ai the adapter loaded.
Configuration
Set in the patch row's config: block.
| Option | Default | Meaning |
|---|---|---|
configPath |
$PI_CODING_AGENT_DIR/models.json, else ~/.pi/agent/models.json |
File to import. The default follows pi's own resolution. |
routePrefix |
pi- |
Prepended to every route id. |
providers |
all | pi provider keys to import. A key the file does not have is reported in the log. |
refreshIntervalMs |
5000 |
How often the route set is reconciled with the file. |
A route id is the provider key lowercased, with anything outside [a-z0-9-]
replaced by -, and routePrefix in front. The prefix is what makes 20xpin
usable: on its own it does not start with a letter, so it is not a valid id.
Behaviour
- The pi file is the only source of truth. Each route's key is read from it per request.
- One adapter per route, so a route id another adapter already owns is logged and skipped while the rest still mount.
- Edits reach the next request. Providers added or removed join the picker within
refreshIntervalMs. No restart, no settings write. - A provider or model that cannot be served is reported by name and skipped. An unreadable file keeps the last good catalog instead of emptying the picker.
Mapping
| pi agent | harness / pi-ai | Notes |
|---|---|---|
baseUrl (provider or model) |
Model.baseUrl |
Model level wins, as in pi. |
api (provider or model) |
Model.api |
openai-completions, openai-responses, anthropic-messages. A route whose models disagree gets an api map, so pi-ai dispatches per model. |
models[].{id,name} |
Model.{id,name} |
name defaults to id, as in pi. |
models[].{contextWindow,maxTokens} |
Model.{contextWindow,maxTokens} |
Defaults are pi's (128000 / 16384), not the harness's. |
models[].reasoning |
Model.reasoning |
Defaults to false, as in pi. |
models[].thinkingLevelMap |
Model.thinkingLevelMap |
Levels pi-ai does not know are dropped and named. Absent stays absent, so pi-ai's asymmetry (base levels supported, xhigh / max not) is preserved. |
models[].input |
Model.input |
Defaults to ["text"], as in pi. |
models[].{cost,samplingParams} |
Model.{cost,samplingParams} |
Passed through when present. |
provider.compat + models[].compat |
Model.compat |
Merged as pi merges them, including key-by-key merging of openRouterRouting, vercelGatewayRouting, chatTemplateKwargs and chatTemplateArgs. |
apiKey |
per-request credential | Read at request time; never persisted into ~/.dsh. |
authHeader: true |
Authorization: Bearer <key> |
Added on top of the protocol's own auth, as in pi. |
Provider-level oauth, modelOverrides and extensions are not imported: the
first needs a credential flow rather than a key, and the other two extend a
catalog this plugin does not read. Such a provider is skipped and named in the
log.
If a gateway rejects off
A thinkingLevelMap value is the string sent on the wire, not a label for what
you mean. pi calls the "no reasoning" level off; the OpenAI Responses API calls
it none. So "off": "off" sends off, and a gateway accepting only none,
minimal, low, medium, high, xhigh and max answers
Invalid value: 'off' (param: reasoning.effort).
It breaks only when a request names no reasoning effort, since pi-ai then sends
thinkingLevelMap.off ?? "none". Use "off": "none", or "off": null to send
no reasoning field. Leave the rest of the map in place: xhigh and max are not
in pi's default level set, so deleting the map loses those two levels.
Catalogs from a switcher often contain "off": "off", because the level names
are copied from the provider record unchanged. The pi agent reads the same file
and fails the same way.
Why there is no dsh-plugin.json
That manifest describes a TUI-plane plugin, registering contracts from dsh-TUI's
ecosystem spec (tui.dsh/v1alpha1, commands.dsh/v1alpha1, and so on). This
package registers into the harness's llm registry and declares no ecosystem
contract, so it is a host-plane bundle only: package.json (dsh.bundle.patch)
plus cordis.patch.yml.
Publishing
npm publish from this repository. prepublishOnly runs verify, so lib/ is
rebuilt from src/ before the tarball is written; build output is not tracked in
git.
Development
pnpm install
pnpm run verify # tsc build, smoke, boot; offline
node scripts/live.mjs pi-20xpin gpt-6-astra low # opt-in, one real request
scripts/smoke.mjs needs no cordis, harness or network: parsing and its
diagnostics, route id mapping, the authHeader bearer header, the api shape
pi-ai dispatches through, route construction against the installed pi-ai, and
pi-ai's own getAuth.
scripts/boot.mjs mounts the plugin into a real cordis composition carrying only
the llm service and reads the registry back: per-route registration and
conflict isolation, the context window and thinking levels a picker sees,
providers appearing and disappearing as the file changes, and a malformed file
keeping its routes. Fixtures in a temp directory keep it off your own ~/.pi.
scripts/live.mjs sends one real request, so it costs money and needs network.
It is not part of verify. It caught routes being handed pi-ai's protocol
factory instead of the implementation pi-ai dispatches through, a mistake every
offline check passes and the first request fails.
License
MIT