Usage · 08

Configure DeepSeek and OpenAI-Compatible Model Providers

Configure native, catalog, and custom routes while keeping credentials write-only, model metadata honest, and sessions reproducible.

Reading time
18 minutes
Sources verified

Know what Settings changes and when

Open Settings → Models after starting the Web UI. Provider cards are backed by adapter-owned settings namespaces, not a browser-only list. Saving a valid profile updates the user settings layer, and adapters re-read resolved settings once per operation. A changed endpoint, catalog, or request default therefore reaches the next model request without a server restart; an in-flight stream keeps the connection facts it started with.

The composition entry remains the base layer. User settings override it, while schema validation and adapter-specific validation reject an unusable draft instead of storing it. If an external edit becomes invalid, the settings service retains the last good namespace snapshot and warns. This keeps a typo from silently removing every provider route.

text
Settings → Models → edit draft → validate namespace/profile
  accepted → persist settings → next operation resolves new facts
  rejected → show settings-rejected → keep previous working facts

Keep credentials write-only and reference-based

Configuration carries a credential reference such as DEEPSEEK_API_KEY, never the secret. The Models page sends a new key to the credential provider, then receives only redacted state: whether the reference is configured, its source, and whether it is writable. It never reads the literal saved value back. The local provider stores managed values in $DSH_HOME/.credentials.yaml while settings retain apiKeyEnv.

Resolution happens per model request, so a managed key rotation reaches the next operation. Inherited environment values are read-only and shadow the managed file; trying to overwrite them through the UI is refused. Environment changes after launch are not observable and require restart. Never place a literal key in settings.yaml, cordis.yml, screenshots, shell history, or diagnostic exports.

yaml
# $DSH_HOME/settings.yaml — safe reference, no literal secret
llm-deepseek:
  apiKeyEnv: DEEPSEEK_API_KEY
  baseURL: https://api.deepseek.com
typescript
const info = await ctx.credentials.describe(credentialRef('DEEPSEEK_API_KEY'))
// { configured: true, source: 'file', writable: true }
// No secret value is returned.

Configure the native DeepSeek route

The native adapter owns the provider route deepseek-official. Its card asks for one key. By default apiKeyEnv is DEEPSEEK_API_KEY and baseURL resolves from configured settings, then DEEPSEEK_BASE_URL, then the public endpoint. The adapter advertises V4 Flash and V4 Pro by default; an explicit models list replaces that catalog, while an empty list advertises none. Catalog metadata guides selectors but the native adapter passes an unlisted model id through to the wire.

yaml
llm-deepseek:
  apiKeyEnv: DEEPSEEK_API_KEY
  baseURL: https://api.deepseek.com
  defaultContextWindow: 1000000
  maxTokens: 256000
  models:
    - id: deepseek-v4-flash
      name: DeepSeek-V4-Flash
    - id: private-reasoner
      contextWindow: 512000
      maxTokens: 32768

The route is chat-completions and text-only. Its model context capacity influences pressure handling, while maxTokens supplies an output default unless a request overrides it. Do not advertise a capacity merely because a marketing page mentions it; configure the limit actually supported by the selected gateway and deployment.

DeepSeek Harness Models settings page showing the DeepSeek provider card, Add provider, and Add a custom provider actions.
The Models page used to configure DeepSeek or begin adding another provider. Screenshot verified 2026-08-14. Official source ↗

Add an installed catalog provider

Choose Add provider for a route already known to the installed pi-ai catalog, such as OpenAI or Anthropic. The catalog supplies provider identity, endpoint, protocol, model list, capabilities, and defaults. Usually you provide only the appropriate credential. A profile can override the base URL for a proxy, narrow the model list, correct capacities, or reshape individual catalog models.

Native authentication providers are different. Bedrock needs AWS credentials and region, Vertex uses application-default credentials and project facts, Azure needs provider environment plus api-version, and Codex uses OAuth. A generic API-key field cannot substitute for those protocols. Prefer the catalog route when its authentication and wire semantics match rather than declaring a superficially compatible custom endpoint.

yaml
llm-pi-ai:
  providers:
    openai:
      apiKeyEnv: OPENAI_API_KEY
      baseURL: https://proxy.example.com/v1
      retryPolicy:
        mode: normal
        maxRetries: 3

Declare a custom compatible provider completely

Choose Add a custom provider for a company gateway, self-hosted endpoint, or provider absent from the installed catalog. Supply a lowercase Provider ID, display name, base URL, supported API protocol, credential, and at least one uniquely identified model. A hand-declared route cannot inherit missing transport facts, so api, baseURL, and models are required.

The Provider ID is permanent. Requests, saved sessions, the default model, settings paths, and credential references rely on it. Renaming it in place strands those references. Add a new provider, validate it, move new sessions to it, and only then remove the old route. Display name, endpoint, protocol, credential reference, and catalog remain editable.

yaml
llm-pi-ai:
  providers:
    acme-gateway:
      displayName: Acme Gateway
      apiKeyEnv: ACME_GATEWAY_API_KEY
      api: openai-completions
      baseURL: https://gateway.acme.example/v1
      defaultContextWindow: 65536
      defaultMaxTokens: 4096
      models:
        - id: acme-chat
          name: Acme Chat
          contextWindow: 65536
          maxTokens: 4096
  • provider key: permanent lowercase route identity.
  • displayName: editable UI label.
  • api: exact protocol spoken by the endpoint.
  • baseURL: endpoint used by every model on the route.
  • models: served ids plus honest capacity and capability metadata.
DeepSeek Harness custom provider form showing Provider ID, display name, base URL, API protocol, and API key fields.
The custom provider draft collects permanent route identity, endpoint, protocol, credential, and model settings. Screenshot verified 2026-08-14. Official source ↗

Treat model discovery as draft assistance

Fetch available models interrogates the endpoint with the base URL, protocol, and one-shot credential currently displayed in the form. For OpenAI-compatible endpoints it calls GET /models. The credential is used for that interrogation and is never stored by discovery. Returned entries are candidates, not registered models; selecting them updates the draft, and only Save persists settings and credentials.

Most listing endpoints reveal only ids. Discovery cannot prove context capacity, output limits, reasoning dialect, or image support. Duplicate or unusable ids are dropped, and adoption still owes metadata required by the adapter. Catalog providers use installed knowledge without a network request. If an endpoint lacks GET /models, enter the models manually.

text
form draft { provider?, baseURL, api, one-shot key }
→ Fetch available models
→ GET /models when supported
→ candidate ids only
→ select candidates into draft
→ review capacities/modalities
→ Save settings and credential separately

Declare image input conservatively

A manually entered model is treated as text-only unless metadata says otherwise. The harness refuses an attached image before network transmission when the selected model lacks image input. This conservative default avoids persisting an image in a session whose endpoint cannot accept it. DeepSeek's native chat-completions adapter is text-only and cannot be changed by configuration.

For a custom vision model, add input: [text, image] to that model in settings.yaml. Use route defaultInput only when every otherwise undescribed model supports those modalities. A per-model input wins, then installed catalog metadata, then route fallback. These fields are deployment claims, not endpoint verification: over-claiming admits an image that the provider may reject after it is durable.

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

If a provider rejects an image, remove the incorrect image grant and start a new session. The prior session still contains the attached image, so retrying the same request repeats the incompatible history.

Understand defaults and session stickiness

Configured providers appear in the model picker. Selecting one also sets the default for new sessions. Once a session has sent a request, its provider, model, rendered request header, and subsequent changes are recorded in its own log. Editing the global default does not silently reroute that established conversation. This makes replay and capacity decisions reconstructable.

text
Existing session S: first request records acme-gateway/acme-chat
Global selection changes to deepseek-official/deepseek-v4-flash
New session N uses the new default
Session S remains on its logged route unless explicitly changed

If the saved default names a deleted provider, the composer shows Select model and blocks input. Select a live route rather than recreating an unrelated provider under the old permanent id. Before deleting a route, inventory active sessions and automation that reference it.

Know how route and model fields resolve

On a catalog route, omitted endpoint, protocol, models, capacities, reasoning support, and modalities inherit installed catalog facts. Supplying models narrows or replaces the served catalog, and each named entry may still inherit unset fields from the installed model with the same id. modelOverrides changes selected installed entries without discarding their siblings. On a hand-declared route no catalog can complete the minimum contract, so the profile must spell the protocol, endpoint, and non-empty model list.

Capacity fields have different jobs. contextWindow describes total model capacity for pressure and compaction decisions. A configured model maxTokens becomes the default output request budget; a catalog capability alone is not automatically a request default. Route fallbacks cover otherwise unsized models, but they are deployment guesses that should be corrected against endpoint behavior. The adapter does not magically reconcile an output budget larger than an endpoint permits.

text
Catalog resolution for one model
identity: profile model id → installed model with same id
modalities: model input → catalog input → route defaultInput
capacity: model contextWindow → catalog capacity → route fallback
output request default: configured model maxTokens → configured route default
transport: route override → installed endpoint/protocol

Reasoning deserves the same care. A private OpenAI-compatible URL may not reveal whether thinking uses OpenAI, DeepSeek, or another dialect. Configure only supported reasoning efforts and compatibility switches documented by the adapter, then verify wire behavior with a non-production key. Protocol compatibility at the HTTP path does not imply reasoning-field compatibility.

Diagnose failures by stable boundary

MISSING_CREDENTIAL means the configured reference resolved nowhere; store it through Models or supply the named environment variable. INVALID_CREDENTIAL means a value exists but cannot safely form an authentication header; replace it without logging it. UNKNOWN_MODEL on pi-ai means the selected id is not in that route's resolved catalog. A discovery 401 points to the one-shot key or endpoint authentication, while NO_DISCOVERY or INVALID_DISCOVERY belongs to discovery composition or draft identity.

text
MISSING_CREDENTIAL → configure the referenced secret
INVALID_CREDENTIAL → replace malformed value; never print it
UNKNOWN_MODEL → select/add model under the same provider route
401 from Fetch → check key and base URL
image refused locally → correct input metadata
provider rejects image → metadata over-claimed endpoint support
TRANSPORT → DNS/TLS/proxy/baseURL boundary

Native DeepSeek maps provider responses to stable AUTH, QUOTA, RATE_LIMIT, CONTEXT_WINDOW_EXCEEDED, INVALID_REQUEST, SERVER, and HTTP status codes; transport, timeout, abort, and malformed-stream failures have separate identities. Preserve the first stable code, HTTP status, retry-after, and request id, but redact message content, endpoints containing secrets, and credentials.

Verify and secure a provider before production

Use a new session and test plain text, tool calling, cancellation, context pressure, rate limiting, invalid authentication, and every declared modality. Confirm the selected provider/model in durable request headers, not only the picker label. For a custom gateway, verify TLS ownership, data retention, regional routing, request logging, and whether harness attribution and stable anonymous/session headers reach it.

text
Provider acceptance checklist
[ ] permanent route id documented
[ ] baseURL hostname and TLS verified
[ ] protocol matches endpoint behavior
[ ] credential is write-only and least privilege
[ ] model ids, context windows, and output limits tested
[ ] image claims tested per model
[ ] discovery results reviewed before save
[ ] existing-session stickiness understood
[ ] stable errors and request ids retained redacted
[ ] deletion/rollback plan protects referenced sessions

Pin the harness and catalog revision because provider protocols and preview configuration evolve. Rotate test credentials after evaluation. A compatible wire shape does not guarantee identical tool calls, reasoning fields, streaming termination, safety policy, billing, or retry semantics; verify the behavior your workload depends on.

Official sources