dshkit

Add a custom model provider to DeepSeek Harness

Register an OpenAI-compatible gateway, self-hosted endpoint or third-party model in dsh: the settings.yaml shape, what apiKeyEnv means, declaring model capabilities, and why the provider ID is permanent.

Updated 2026-08-133 min
Short answer

Catalog providers are added through Settings → Add provider in the web UI. A custom provider is declared in $DSH_HOME/settings.yaml with a lowercase provider ID, apiKeyEnv naming an environment variable, an api protocol such as openai-completions, a baseURL, and a models list. The provider ID cannot be renamed later.

The harness is model-agnostic by construction: model adapters are plugin-provided rows in dsh-base, no different from tools or storage. Adding a model means declaring where it lives and how to talk to it.

There are two paths, and picking the wrong one accounts for most of the friction.

Path 1 — a provider already in the catalog

If your vendor is in the catalog — DeepSeek, Anthropic, OpenAI and others — do it in the UI:

Settings → Add provider → pick the provider → paste its API key → save.

The key is written to $DSH_HOME/.credentials.yaml. That file is deliberately separate from $DSH_HOME/settings.yaml so settings can be shared or version-controlled while credentials are not.

That is the whole procedure. If this covers you, stop here.

Path 2 — a custom provider

Use this for an OpenAI-compatible gateway, a self-hosted inference server, or any endpoint not in the catalog. A custom provider needs five things: a lowercase provider ID, a base URL, an API protocol, a credential reference, and a model list.

llm-pi-ai:
  providers:
    my-gateway:
      apiKeyEnv: GATEWAY_API_KEY
      api: openai-completions
      baseURL: https://gateway.example/v1
      models:
        - id: vision-preview
          input: [text, image]

Field by field:

my-gateway — the provider ID. Lowercase, and permanent. There is no rename; changing it means creating a new provider and deleting the old one, then fixing every model reference that pointed at the old ID. Choose it as if you will live with it, because you will.

apiKeyEnv — the name of an environment variable, not the key. This is the single most common mistake in custom provider configuration. Pasting a live secret here both fails to authenticate and writes a credential into a file designed to be shareable.

api — the wire protocol. openai-completions is the right choice for the large family of gateways and servers that expose an OpenAI-compatible surface.

baseURL — where requests go. Whether to include the /v1 suffix depends on what your endpoint expects; getting this wrong produces a 404, not an auth error, which is a useful signal when debugging.

models — what the provider serves. Each entry needs an id and its input modalities. input: [text, image] declares a vision-capable model; text-only models declare [text].

Export the credential

export GATEWAY_API_KEY="sk-..."
dsh --profile web

Two things bite people here. A running dsh process holds the environment it started with — adding the export to ~/.zshrc does nothing until you restart it. And a GUI-launched terminal may never have sourced your login shell at all. Check in the shell you actually launch from:

printenv GATEWAY_API_KEY

Verify before you trust it

Confirm the endpoint and key work independently of the harness:

curl -sS https://gateway.example/v1/models \
  -H "Authorization: Bearer $GATEWAY_API_KEY" | head -20

401 means the key is wrong. 404 usually means the baseURL is wrong. A connection error is network, not configuration. Then confirm the provider survived composition:

dsh --profile web --dump-config

Remember the patch order — bundles, then the profile's cordis.patch.yml, then the home-level $DSH_HOME/cordis.patch.yml, then --patch. A home-level layer overriding your edit is a routine cause of a provider that "is definitely in the file" but is not in the running config.

Multiple providers, one harness

Nothing restricts you to one. Registering several — a cheap model for mechanical work, a stronger one for reasoning, a vision model for screenshots — is normal, and routing between them is exactly the kind of thing sub-agents in Standard mode exist for.

The practical guidance is to keep provider IDs descriptive of role rather than vendor (fast-bulk, deep-review) where you can. Since the ID is permanent, a name describing what you use it for survives a vendor swap; a name describing the vendor does not.

Frequently asked

Can I rename a provider ID?

No. The provider ID is permanent. Create a new provider with the ID you want and delete the old one, then update any model references.

Do I put my API key in settings.yaml?

No. apiKeyEnv is the name of an environment variable, not the key. Catalog-provider keys live in $DSH_HOME/.credentials.yaml; custom-provider keys live only in your environment.

Does the endpoint have to be DeepSeek's?

No. Any endpoint speaking a supported protocol works — a commercial gateway, a self-hosted inference server, or another vendor's API.

Keep reading