DeepSeek Harness Plugin Hub

发布与管理完整 Harness Profiles,发现适合你的插件。

探索

插件目录环境预设文档中心动态

社区

发布插件联系我们报告问题

相关链接

Plugin Hub GitHubDeepSeek Harness 官方项目系统状态隐私说明
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

独立、非官方社区项目,与 DeepSeek 官方无隶属、授权或背书关系。

Plugin Openai Api — DeepSeek Harness 插件(DSH Plugin)
DeepSeek Harness Plugin Hub
ProfilesPlugins分类动态文档登录管理 Profiles
ProfilesPlugins分类动态文档登录
← Plugins
P

dsh-plugin-openai-api

Plugin Openai Api

DSH Web 服务器上的 OpenAI 兼容 /v1/chat/completions(流式和非流式)以及 /v1/models 端点:每个请求的 user 字段对应一个 DSH agent 会话。

插件会安装到这里;不确定时保持 web。

npx -y @deepseek-ai/dsh plugin --profile web add github:paulalesius/dsh-openai-api#ddc859fa3e2d1fa72c39b6fac47ea056a6b0fbbc
README兼容性版本

兼容性与来源证明

Plugin Openai Api 以 dsh-plugin-openai-api 发布,当前版本为 0.1.0。Plugin Hub 会校验它的 manifest,并保存精确安装来源,便于复现安装结果。

DSH 兼容范围
*
运行环境
any
发布来源
github
Registry 更新时间
2026/8/24

版本

0.1.0stable
2026/8/24

相关插件

正在加载相关插件…

最新版
0.1.0
DSH
*
HMR
重启进程
Tree shaking
未声明可安全裁剪
解包体积
未提供
文件数
未提供
Surface
any
许可证
MIT
发布源
github
GitHub
★ 0
周下载
0
最近提交
2026/8/24
查看源码 ↗
README Badge

点击下方 Badge 复制 Markdown,粘贴到 README 即可。

这是你的 Plugin?认领权益 · 优先安全扫描

验证 package.json 声明的 GitHub 仓库,即可管理这个公开页面。认领后,Hub 会优先安排当前版本的安全扫描,并在通过后公开展示结果。

认领这个 Plugin →
报告问题

相关插件

继续浏览 integrations-communication 分类下经过校验的插件。

Acp App@deepseek-ai/dsh-acp-appdsh ACP 配置文件包:基于 dsh-base 的仅限自动化的 JSON-RPC stdio 和进程生命周期管理Remote Web Ui@linxin666/dsh-remote-web-ui通过扫码配对访问 dsh Web GUI,共享一个官方界面:设置按钮旁的二维码可将手机和 PC 配对到同一个 Web GUI(手机采用竖屏触控适配层,PC 使用完整桌面界面),通过一次性令牌和 rIm@xmanrui/dsh-im将十一种 IM 渠道和一个公网 AI Office 接入本地 DeepSeek Harness。Pocketdsh-pocket把 DeepSeek Harness 装进你的口袋:一个包、一个设置页,手机扫码即同步访问电脑上的 DSH(局域网 + 公网,实时同屏)。

README

dsh-plugin-openai-api

dsh-plugin-openai-api banner

An OpenAI-compatible endpoint on your own DeepSeek Harness (dsh) web server. Point any OpenAI SDK client at the running dsh web instance and it drives a real DSH agent session — with all the tools and presets that agent has:

GET  /v1/models
POST /v1/chat/completions        (non-streaming and SSE streaming)

Base URL of a default local install: http://127.0.0.1:3080/v1 — the same port the web GUI serves on.

What the endpoint gives you:

  • a real agent on the other end — every request lands on a DSH agent session, not a thin LLM proxy: tool calls, reasoning, and the full session machinery happen server-side, and only committed assistant text comes back over the wire;
  • agent presets as clients — the request's user field names the agent preset the session joins (standard, or anything in your ~/.dsh/.agent-presets/); one preset = one session, by design;
  • two session lifetimes — shared (the default: one session per user, conversation state survives between requests) or per_request (a fresh session seeded from the full message array on every prompt);
  • standard OpenAI surface — streaming with usage, the standard error envelope, chatcmpl-… ids, and the finish_reason mapping, so SDK code you already have works unchanged.

The plugin is host-plane: it mounts on the web server at startup, and installing it never requires rebuilding the frontend.

Installation

You need a running dsh web with a profile that includes the web-app bundle (the one that owns the port).

With the dsh command

1. Install the plugin. One command does both halves — it links the plugin into your profile and registers the mounting row for it:

dsh plugin --profile web add /path/to/openai-api

(Use a plain path, not a file: URL — a plain path links the source, so edits are picked up without reinstalling. Without the dsh binary on your PATH, run node --import tsx/esm apps/cli/src/bin.ts plugin … from inside the DSH checkout.)

If you checked this repo out fresh, recreate the one machine-local link the source needs first: ln -s /path/to/deepseek-harness/apps/cli/node_modules node_modules inside this repo.

2. Restart dsh web once.

3. Enable and configure the endpoint — one small block in your own config, below. Then restart dsh web again, and the endpoint is live.

The install ships switched off on purpose: until you add that block, the port serves only the GUI. What you enable, with what key, is your call.

From source (no dsh command)

The command above does exactly these three things, by hand:

  1. Link the package into the profile — in ~/.dsh/profiles/web/package.json, add the dependency (alongside the existing ones) and the bundle (into the existing list):

    {
      "dependencies": {
        "dsh-plugin-openai-api": "link:/home/noname/deepseek-harness/dsh-plugins/openai-api"
      },
      "dsh": {
        "profile": {
          "bundles": [
            "…",
            "dsh-plugin-openai-api"
          ]
        }
      }
    }
    
  2. Recreate the machine-local source link (fresh checkouts only): ln -s /path/to/deepseek-harness/apps/cli/node_modules node_modules inside this repo — the plugin's TypeScript resolves the DSH packages through it.

  3. Enable the row in the profile patch layer (next section).

Then restart dsh web.

To uninstall later — command path: dsh plugin --profile web remove dsh-plugin-openai-api. Source path: remove the two lines above from the profile's package.json. Either way, remove your config block and restart dsh web. Sessions the plugin created are disposed with it; the web GUI's own sessions are untouched.

Configuration

The configuration is one openai-api row in your profile's patch layer (~/.dsh/profiles/web/cordis.patch.yml, append to your existing file):

- id: openai-api
  disabled: false
  config:
    model: default
    apiKey: local-key

Two things to know about the row:

  • the config block replaces the plugin's defaults wholesale — list every key you want; nothing is required, and unknown keys are a load error;
  • to switch the endpoint off again, set disabled: true on the row (or remove it) and restart.

The configuration keys

All four are optional.

keydefaultwhat it does
modeldefaultThe model id the endpoint advertises at /v1/models and echoes in completions. It is the wire identity — what SDKs see — not a DSH model selector: a request that omits model (or names the wire id) runs the host's effective default model selection — the same selection GUI sessions get, from the agent-default-model service, which honors the user's stored model choice. Any other value in a request is a real model id, passed through to the session. (The deployment persona renders {{model}} from the session's model, so every session always carries one.)
providerhost defaultThe provider route the created sessions use (the AgentOptions.provider meaning — the host's provider routing). Omitting it is a no-op on single-provider setups.
apiKeyunset (no auth)When set, every request must carry Authorization: Bearer <apiKey> — missing or wrong key is a 401. When unset the endpoint is unauthenticated: anyone who can reach the port can prompt the agent (which has the same tool access as the GUI). Keep the web server on its loopback bind (host: 127.0.0.1) unless you set this — and consider a reverse proxy — before exposing the port.
cwdthe dsh web process's own working directoryAbsolute working directory for the sessions the plugin creates (they run real file and shell tools, so they have a working directory — and the deployment persona in the system prompt renders it, so every session always carries one). Must start with /.

Using the API

The contract is OpenAI's, with two DSH-specific fields (user, session) and one honest limitation: only committed assistant text crosses the wire.

A request

import OpenAI from 'openai'

const client = new OpenAI({
  baseURL: 'http://127.0.0.1:3080/v1',
  apiKey: 'local-key', // the config's apiKey; 'unused' if you set none
})

const reply = await client.chat.completions.create({
  model: 'default',
  user: 'webtroll', // agent preset id; omit for the default preset
  messages: [
    { role: 'user', content: 'What are you? ' },
    { role: 'assistant', content: "I'm a DSH agent." },
    { role: 'user', content: 'Write a haiku about that.' },
  ],
})
console.log(reply.choices[0].message.content)

// streaming, same session (`user: 'webtroll'` matches the first request's
// session), with the trailing usage chunk
const stream = await client.chat.completions.create({
  model: 'default',
  user: 'webtroll',
  stream: true,
  stream_options: { include_usage: true },
  messages: [{ role: 'user', content: 'Count to five, slowly.' }],
})
for await (const chunk of stream) {
  if (chunk.choices[0]?.delta?.content) process.stdout.write(chunk.choices[0].delta.content)
}

// a fresh session for every prompt, seeded from the full message array
const once = await client.chat.completions.create({
  model: 'default',
  session: 'per_request',
  messages: [
    { role: 'system', content: 'You answer in exactly one sentence.' },
    { role: 'user', content: 'What are you?' },
  ],
})

The same, without an SDK (the Bearer header only when you set apiKey):

curl -s http://127.0.0.1:3080/v1/chat/completions \
  -H 'content-type: application/json' -H 'Authorization: Bearer local-key' \
  -d '{"user":"webtroll","messages":[{"role":"user","content":"hello"}]}'

Request parameters

fieldrequiredwhat it does
messagesyesNon-empty array. Roles: user, assistant, system (and developer, mapped to system). Content: a string, or an array of {"type":"text","text":…} parts. First request for a session (shared mode, or every per_request): the full array is rendered into one labeled transcript that seeds the new session. Later shared-mode request: only the latest non-empty user turn is admitted — the session already holds the earlier history, and a follow-up with no user turn is a 400.
usernoThe agent preset id the session joins: anything in ~/.dsh/.agent-presets/, plus the shipped presets. Absent → the profile default preset (the web bundle's standard). Unknown id → 400 listing the roster; a preset discovery reports broken → 400 with the reason. The preset is resolved at session creation only: in shared mode a session keeps the preset its first request chose — a different user value always means a different session (and therefore its own preset).
sessionno"shared" (the default — omit it): one DSH session per user value; requests without user share one default-preset session. "per_request": a fresh session for every prompt, seeded from the full messages array each time.
modelnoSession-creation detail. Shared mode: the first request's value sets the session's agent options; later requests' values are ignored on purpose. per_request: applies to that request's session. Absent (or equal to the configured wire id) → the host's default model selection; any other id is used as the session's real model on the configured (or host default) provider.
max_tokens / max_completion_tokensnoPositive integer; same creation-time semantics as model.
streamnoBoolean. true → Server-Sent Events: chat.completion.chunk frames, then .

Rejected with a 400 (the OpenAI error envelope): tools, tool_choice, functions, function_call (function calling is not supported — the agent still uses its own tools server-side, they just don't appear on the wire), tool-role messages, image or other non-text content parts, n ≠ 1, and unknown stream_options keys.

Responses

  • Only committed assistant text leaves the wire — each committed message's text blocks, in order. Reasoning and tool calls stay off it.
  • Streaming granularity is per committed message, not per token — one chat.completion.chunk per committed assistant message, then a finish chunk, then [DONE].
  • finish_reason: a turn ended max-tokens → length; everything else that settles (completed, aborted, interrupted, blocked) → stop. Error endings never produce a finish — the request gets a 5xx.
  • usage: DSH token counts are disjoint, so prompt_tokens = input + cache-read + cache-write, completion_tokens = output + reasoning.
  • Errors use the OpenAI envelope: {"error": {"message", "type", "param": null, "code": null}} — a 400 for wire violations, 401 for a missing/wrong bearer key, 409 when a second request arrives for a shared key that is still in flight (there is exactly one prompt per key at a time; per_request has no 409s), and 5xx when the turn itself fails.
  • Disconnecting mid-request cancels the in-flight turn. A non-stream request answers 500; a stream simply ends without [DONE] so SDKs raise instead of silently truncating.
  • per_request sessions are kept alive after the response — never reused, disposed only at plugin teardown.

GET /v1/models returns the single configured model id ({"object":"list","data":[{"id": …}]}).

Development

# tests (32: wire unit tests + a fake-ctx integration smoke)
node --import ./test/register.mjs test/test.mjs

# typecheck (repo toolchain, strict)
TSC=/src/misc/harness/deepseek-harness/node_modules/.bin/tsc
"$TSC" --noEmit --strict --noUnusedLocals --noUnusedParameters \
  --noFallthroughCasesInSwitch --module nodenext --target es2023 \
  --allowImportingTsExtensions --skipLibCheck \
  --typeRoots /src/misc/harness/deepseek-harness/node_modules/@types --types node \
  openai-api.ts

Layout: openai-api.ts (entry: plugin surface, session bookkeeping, HTTP routes, settlement), src/wire.ts (pure wire layer: parsing, transcript rendering, framing, usage/finish mapping), src/config.ts (hand-rolled Standard-Schema v1 config validator). No runtime dependencies beyond the DSH packages resolvable from the CLI's node_modules.

data: [DONE]
stream_options.include_usagenoBoolean. When true, a trailing usage chunk (choices: []) precedes [DONE].
nnoMust be 1 or absent.