Skip to content

dsh-pi-agent-providers

Verified

dsh-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