OpenAI-completions-compatible adapter for custom gateways (vLLM / LM Studio / self-hosted proxies): always role:"system", thinking driven by the model config, Qwen-style response split, and vision-model image input (single / multiple).
Install
# from npm (prebuilt)
dsh plugin --profile web add dsh-llm-openai-completions
# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)
dsh plugin --profile web add github:drscrewdriver/dsh-llm-openai-completions
Any plugin you install runs third-party code with your own permissions — it can read your files, use your credentials, and reach the network, and tool approvals don’t sandbox it. GitHub-sourced plugins also run build scripts at install time — pnpm blocks those until you allow them, so an install can stop with ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED or ERR_PNPM_IGNORED_BUILDS; dsh prints the exact key to add under allowBuilds in your profile’s pnpm-workspace.yaml, and the install works on the next run. Allowing a build is a trust decision: only install sources you trust, and pin a commit (github:owner/repo#sha).
README
✅ Revived as 0.3.0 (2026-10-05). The transport takeover is back with real features pi-ai cannot offer: multimodal true-send (image/audio/video/PDF) with a modality hard gate, an
llmOpenaiCompletionsprompt-source seam for dsh-promptbook-class consumers, credentials-service keys, and full-rc dynamic compatibility across0.1.0-rc.8–0.2.0-rc.2. The oldsettings.get-era architecture is gone — the judgment isenabled && providers.includes(route)(pure manual membership: the per-route checkbox is the truth), and the switch lives in this plugin's own settings section. Contract surfaces: AGENTS.md.
Positioning: revived as a short-circuit backend that dsh-thinking-levels can drive — speaking all three mainstream API formats per route api declaration (OpenAI Chat Completions · Anthropic Messages · OpenAI Responses), built primarily for Xiaomi MiMo multimodal: true audio/video/PDF send.
README languages / 说明文档
English · 中文 · 日本語 · 한국어 · Deutsch · Español · Français · Italiano · Русский
Install guides: EN · 中文 · 日本語 · 한국어 · DE · ES · FR · IT · RU — Changelog
OpenAI-completions-compatible transport takeover for custom gateways (vLLM / LM Studio / self-hosted OpenAI proxies): the plugin short-circuits the llm/stream waterfall for taken-over routes and serves them with behavior that never guesses — the data plane (baseURL / models / reasoningEfforts / compat / input) stays in llm-pi-ai, read live through settings.describe().
systemis alwaysrole: "system"— a custom gateway never receivesdeveloper, so theUnexpected message role400 is gone even when thinking is enabled (pi-ai'sdetectCompatdefaultssupportsDeveloperRole: truefor non-standard URLs, which breaks every custom gateway once areasoningEffortstable marks the model as reasoning).- Thinking is driven by the model's
compat.thinkingFormat:- toggle models (
reasoningEffortstable, nosupportsReasoningEffort) →chat_template_kwargs.enable_thinking(+ preserve) — always, regardless of the configured format, because that is what bare vLLM Qwen3 templates honor qwen→ top-levelenable_thinking: boolean(noreasoning_effort, no budget)qwen-chat-template→chat_template_kwargs.enable_thinking(+ preserve)- effort-capable →
reasoning_effortpassthrough (Qwen3.8-style)
- toggle models (
- The receive side splits Qwen3-style
</think>content (vLLM renders thinking intocontentwith noreasoning_contentfield) into a reasoning block, and readsreasoning_content/reasoning/reasoning_text. - No
thinking_budgetis sent (deliberate: avoid truncation surprises).
🎉 0.3.0 — Multimodal true-send with a modality hard gate
The short-circuit consumes the waterfall before the host projects file blocks to text handles, so media arrives structural and is encoded for the wire — no host patches, no file-handle reconstruction:
- image →
image_urldata-URI parts (multi-image count and order preserved, text interleaved in source order) - audio →
input_audioparts (base64 + format) - video →
video_urldata-URI parts (20 MB inline cap) - PDF → OpenAI
fileparts - Modality hard gate: every media block in the full history is checked against the model's llm-pi-ai
inputdeclaration before anything is read or sent; an undeclared modality is refused loudly (INVALID_REQUEST, naming the modality) instead of being silently degraded to text. - One-shot calls (
session-title) strip media blocks and never think. - Bytes come from the attachment service (
readImage/readFileStream); non-vision models still reject images loudly (UNSUPPORTED_CONTENT).
🎉 0.3.0 — The llmOpenaiCompletions prompt-source seam
ctx.provide("llmOpenaiCompletions", { listHostedModels, registerPromptSource }) — the same contract dsh-llm-mimo exposes as llmMimo, for dsh-promptbook-class consumers: replace the actually dispatched system prompt and tool descriptions per (provider, model), deterministically (cache-safe), text-only (never name/parameters), zero behavior with no source registered. Scope guarantee: consulted only on taken-over routes. Full contract: AGENTS.md §1.
Three wire faces (per-route api)
| face | endpoint | auth | thinking | modalities |
|---|---|---|---|---|
openai-completions (default) |
{base}/chat/completions |
Bearer | thinkingFormat matrix | image/audio/video/pdf |
anthropic-messages |
{base}/v1/messages |
x-api-key + version header |
off/one-shot → disabled | image only |
openai-responses |
{base}/responses |
Bearer | reasoning.effort (honest fold) |
image/audio/video/pdf |
- Every face:
max_tokens= request value → model row → route default → 8192; the leading system message is extracted into the face's system channel and stripped from the message list. - Anthropic pairing discipline: tool_use/tool_result must pair (broken history = loud
INVALID_REQUEST); assistant thinking is not replayed (no signature in the harness — strictly weaker than the chat face). - Responses boundaries:
stopdropped (no Responses spelling); reasoning effort folds honestly; probe calibration pending against real MiMo endpoints.
Takeover judgment (single source of truth)
takeover(route) = section.enabled === true
&& section.providers.includes(route)
# ~/.dsh/profiles/web/cordis.patch.yml (or Settings → Plugins → dsh-llm-openai-completions)
llm-openai-completions:
enabled: true
providers: # the takeover list — the single truth (see below)
- local-35b
- The switch and the takeover list live in this plugin's own settings section (
.volatile()— runtime-editable, host-generated form). - dsh-thinking-levels drives both fields: its
takeoverswitch mirrors{enabled}into this section, and its model panel's per-route takeover checkbox writesprovidersdirectly — the checkbox is the truth, so dispatch,listHostedModels, and the control layer's gating all read one set. - Disabling the switch (or unmounting the plugin) falls through to pi-ai unchanged — no adapter registration to unwind.
- Your llm-pi-ai config stays as-is (one shared config; this adapter only reads it — full field table: docs/settings-spec.md, takeover contract: docs/takeover-spec.md):
llm-pi-ai:
providers:
local-35b:
api: openai-completions
baseURL: http://192.168.100.242:8200/v1
models:
- id: Qwen3.6-35B-A3B
reasoningEfforts: { off: null, high: 'high' }
compat:
thinkingFormat: qwen # enable_thinking only, no reasoning_effort
Preview
Live screenshot — powered together with dsh-thinking-levels: the custom model's "thinkable" level dropdown (Default / Off / On). It demonstrates the capability-detection → takeover chain:
Install
Host compatibility: every rc from 0.1.0-rc.8 through 0.2.0-rc.2, version-conditional in code (shape-detected settings faces, a ToolCallId/CallId shim, credentials → launch-environment → process.env key resolution). See the install guide for all routes:
# npm (recommended)
dsh plugin --profile web add dsh-llm-openai-completions -w
# local tgz
dsh plugin --profile web add /path/to/dsh-llm-openai-completions-0.3.0.tgz -w
dsh web
Known limitations
- PDF support is endpoint-dependent (MiMo chat rejects it outright; some relays accept it) — the
inputdeclaration is a capability claim; check your endpoint before declaringpdf. - Non-media file attachments (e.g.
.txt) have no cross-gateway wire shape; on taken-over routes they degrade to a text placeholder (content stays reachable through the file tools). - No
thinking_budget(deliberate). - The thinking-level selector still comes from the model's reasoning metadata (llm-pi-ai + dsh-thinking-levels); this transport controls the wire behavior only.
- Restart
dsh webafter install; the takeover listener composes at boot.
Development
npm install
npm run typecheck # tsc --noEmit
npm test # vitest (config / identify / serialize / sse / translate / prompt-source / version-conditional)
npm run build # tsc → lib/
node scripts/audit-host-faces.mjs # 15-rc dependency-surface audit
Upstream proposal
This plugin doubles as a reference implementation for the llm-pi-ai / pi-ai
compatibility gaps: custom URLs should default supportsDeveloperRole: false,
the receive side should split </think> content, compat should expose
supportsDeveloperRole for gateways that genuinely support it — and the host
projection layer could grow the same capability-aware file-block gate this
plugin applies per route.
Links
More in this category
corrinehu/dsh-workbuddy-connect★ 970
Brings the models in the WorkBuddy desktop app straight into DeepSeek Harness — zero configuration in the DSH chat.
V1ki/dsh-plugin-subscriptions★ 469
Use ChatGPT (Codex), Claude, and Grok subscriptions as DeepSeek Harness LLM providers, with Settings login, model catalogs, usage, plus image_generate, video_generate, and x_search tools.
Mars-Sea/dsh-commandcode-provider★ 393
Unofficial Command Code LLM provider: registers a `commandcode` route with a live model catalog and reasoning-effort support.
cv-superding/dsh-deepseek-web-login★ 274
Adds a deepseek-web provider that uses chat.deepseek.com web models in DSH, with browser login capture, PoW request signing, SSE streaming, and prompting-based tool calls.
FishBottle7/opencode2dsh★ 212
Exposes OpenCode Zen free models to DeepSeek Harness, with no API key.
WSL043/dsh-codex-subscription★ 179
ChatGPT OAuth provider for Codex models with subscription web search, quota and safe reset, image tools, Fast mode, and model-aware context; no API key or Codex CLI.
Community comments
Comments are public GitHub Discussions. Loading them connects to GitHub and Giscus; a GitHub account is required to post.