DeepSeek Harness 插件

Boomerangz/dsh-openrouter-live

Star 数 ★ 0 分类 模型与账号接入 收录于 2026-09-24 npm dsh-openrouter-live

读取 OpenRouter 实时 /models 列表的模型提供商,新模型无需等待 harness 发版即可出现:模型按名称排序并显示输入/输出价格,按模型提供思考档位、图片输入、reasoning_details 回放、Anthropic 提示缓存,并按 OpenRouter usage.cost 统计本会话(含子代理)的实际花费,显示在 Web 输入框下方并可用 /cost 查看。

安装

# npm 包(预构建)

dsh plugin --profile web add dsh-openrouter-live

# GitHub 源码(首次需按提示配置 allowBuilds 构建授权后重试)

dsh plugin --profile web add github:Boomerangz/dsh-openrouter-live

装任何插件都等于在你的机器上跑第三方代码,权限和你本人一样大——能读你的文件、用你的凭据、访问网络,工具审批管不到它。GitHub 来源的插件还会在安装时执行构建脚本——pnpm 默认拦截,所以安装可能停在 ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWEDERR_PNPM_IGNORED_BUILDS;dsh 会打印出需要添加的确切键名,把它加进该 profile 的 pnpm-workspace.yamlallowBuilds 下,重跑一次即可装上。放行构建本身就是一次信任判断:请只安装可信来源,并尽量锁定 commit(github:owner/repo#sha)。

README

该插件的 README 只有英文版本。

An OpenRouter provider for DeepSeek Harness (dsh) that reads OpenRouter's live model list, so a model shows up in the picker as soon as OpenRouter serves it.

The harness's built-in openrouter route lists models from a catalog bundled with each release. A model released after that snapshot, such as deepseek/deepseek-v4.1-flash, does not appear in the picker until the next harness release. This plugin adds a second route, OpenRouter (live). It reads OpenRouter's /models endpoint and takes each model's metadata from there.

What it does

  • Live model list. It lists every OpenRouter model that accepts tools, sorted by name, so models from one vendor stay together. Each name ends with the input/output price per million tokens. The list is refreshed every 10 minutes. If a refresh fails, the previous list stays in use.

    Model picker with prices

  • Per-model metadata. The context window and output cap come from OpenRouter. The reasoning levels come from the model's own reasoning.supported_efforts, so the composer offers only levels the model accepts, including Off when reasoning is optional.

  • Image input. It inlines images for models whose /models entry lists image input. It follows the harness image budget: images are downscaled, and the oldest images are offloaded when a request grows too large.

  • Reasoning continuity. It sends reasoning_details back on later requests, including signed and encrypted entries. This lets models such as Claude and DeepSeek keep their reasoning across tool calls.

  • Prompt caching.

    • Anthropic models get cache breakpoints on the system prompt and the newest message. In a live check, a second request read 7209 of 7223 prompt tokens from cache.
    • Other providers cache automatically. The x-session-id header keeps each conversation on one upstream provider, so its cache stays warm.
  • Classified failures. Errors map to the harness failure codes, so the harness retry plugin retries only what is worth retrying:

    • Retried: rate limits, with OpenRouter's Retry-After honored; upstream 5xx errors; idle streams; truncated streams; empty responses.
    • Not retried: bad keys, exhausted credits, and context overflow.
  • Actual spend, including subagents. The plugin records the charge OpenRouter reports for every response (usage.cost), so these are real charges, not token estimates. Each charge is attributed to the session that made the request and to every session it was delegated from. Type /cost in the CLI or Web UI:

    OpenRouter spend in this session: $0.0022 (5 requests)
      this agent: $0.0011 (3 requests)
      subagents: $0.0011 (2 requests in 1 session)
    All recorded sessions: $0.0022 (5 requests).
    

    In the Web UI the session total is always visible under the composer, next to the built-in statistics and context meter. It updates during a turn, including subagent charges, and its tooltip splits the total between the agent and its subagents. It is hidden in sessions with no OpenRouter charges.

    Spend under the composer

    Charges are appended to ~/.dsh/openrouter-live/costs.jsonl, one JSON line per response, so totals survive restarts. Each line carries the OpenRouter generation id, so any charge can be matched against your OpenRouter activity page.

  • Shared credential. By default the route reads the same OPENROUTER_API_KEY credential as the built-in openrouter route, so a key you already stored works without extra setup.

Requirements

  • DeepSeek Harness 0.1.6-alpha.2 through 0.1.7. It has been tested on 0.1.6-alpha.2 and 0.1.7-alpha.2. The harness API is pre-stable, so later harness versions are added to the peer range after they are tested.
  • Node.js ^22.19 || >=24.
  • An OpenRouter API key.

Install

From npm:

dsh plugin --profile web add dsh-openrouter-live

From a local checkout:

git clone https://github.com/Boomerangz/dsh-openrouter-live.git
cd dsh-openrouter-live
npm install
npm pack --pack-destination dist          # runs typecheck and tests first
dsh plugin --profile web add "$PWD/dist/dsh-openrouter-live-0.2.0.tgz"

[!IMPORTANT] Install the packed .tgz, not the directory. When you add a directory, pnpm links it, Node resolves the link to its real path outside the profile, and the harness packages (@deepseek-ai/*) can't be found there. The whole profile then fails to boot.

Restart the profile after installing. OpenRouter (live) then appears in the model picker. If you haven't stored a key yet, choose one of these:

  • Enter it on the built-in OpenRouter card under Settings → Models. It is stored as OPENROUTER_API_KEY, which both routes read.
  • Export OPENROUTER_API_KEY before launching dsh.

To choose it as the default model, pick it in the composer's model picker. You can also set it in ~/.dsh/settings.yaml:

agent-default-model:
  provider: openrouter-live
  model: deepseek/deepseek-v4.1-flash

Configuration

Every field is optional. Set fields in the profile's patch layer, ~/.dsh/profiles/<profile>/cordis.patch.yml, under the plugin's entry id openrouter-live:

- id: openrouter-live
  config:
    models: ['deepseek/*', 'anthropic/claude-*', 'openai/gpt-5*']
    extraBody:
      provider:
        sort: throughput
Field Default Meaning
models [] Glob patterns over model ids. An empty list lists every model. Ids outside the filter still work when typed.
toolsOnly true List only models whose OpenRouter entry accepts tools. Agent turns need tools.
priceInName true Append input/output prices per 1M tokens to model names, such as DeepSeek: DeepSeek V4.1 Flash ($0.15/$0.60), because the pickers show names but not descriptions. The price then also appears in the composer's model button.
apiKeyEnv OPENROUTER_API_KEY Credential reference: a credential-store entry or environment variable.
provider openrouter-live Route id. Give each instance its own id to run several side by side, for example one with a *:free filter.
displayName OpenRouter (live) Provider name in the picker.
baseURL https://openrouter.ai/api/v1 API root.
modelListTtlMs 600000 How long one fetched model list is reused.
maxTokens 32768 Output cap for requests that set none. A lower model limit wins.
defaultContextWindow 262144 Context size assumed for an id the list does not describe.
streamIdleTimeoutMs 300000 Longest silence inside a response stream before failing with TIMEOUT.
maxRequestImageBytes 20971520 Base64 image budget per request. Older images are offloaded beyond it.
requestImagePixelBudget 4194304 Pixel budget of one request image.
requestImageMaxBytes 1048576 Encoded-byte target of one request image.
appTitle / appUrl DeepSeek Harness / unset OpenRouter app attribution headers (X-OpenRouter-Title, HTTP-Referer).
extraBody {} Fields merged into every request body, such as provider routing. Fields the plugin sets take precedence.
costLedgerPath $DSH_HOME/openrouter-live/costs.jsonl Spend ledger file. An empty string keeps spend in memory for the current process only.
costCommand cost Slash command that reports spend. Leave it empty on a second instance that shares the ledger, so the command is registered once.
costIndicator true Serve the Web UI spend indicator's data route (/openrouter-live/spend). Disable it on a second instance, which would otherwise claim the same route.
retryPolicy harness default The harness's provider retry policy for this route: {mode: normal, maxRetries, retryableCodes, backoff} or {mode: always}.

How it maps onto OpenRouter

Harness OpenRouter chat completions
System prompt, user and assistant text system, user and assistant messages
Tool definitions and tool calls tools (function type) and tool_calls
Tool results tool messages. Images returned by a tool follow in a user message, because tool messages carry text only.
Images image_url data URLs, each preceded by the harness's image handle line
Reasoning effort reasoning.effort
Reasoning output The reasoning stream becomes reasoning blocks. reasoning_details is kept for replay.
Usage prompt_tokens is split into uncached input, cache reads and cache writes. Reasoning tokens are reported separately.

Development

npm install
npm run check                          # tsc --checkJs (strict) + node:test suite
OPENROUTER_API_KEY=... npm run smoke   # live checks against OpenRouter; costs a few cents

The plugin is plain ESM JavaScript with JSDoc types, so no build step is needed to run it. npm pack generates the .d.ts files in types/. The test suite includes a composition test that boots the real LlmRuntime from @deepseek-ai/dsh-llm in a Cordis context and streams through it.

Known limitations

  • Spend covers only requests through this plugin's routes. The built-in openrouter and deepseek-official routes are not counted.

  • A charge is recorded when OpenRouter reports it at the end of a response. A request that is cancelled or fails mid-stream may still be billed by OpenRouter, but it does not appear in /cost.

  • The Web UI indicator reads a route that answers only same-origin requests to a loopback address (127.0.0.1, localhost), because the harness web server does not authenticate plugin routes. When the Web UI is opened over the network, the indicator stays hidden; /cost still works.

  • /cost needs the harness command service, which the CLI and Web profiles mount. Headless runs still write the ledger.

  • Only OpenRouter's text output is used. Image generation and audio models are listed only when they also accept tools, and their non-text output is ignored.

  • Prices in the picker are OpenRouter's base prices. Time-of-day price overrides are not shown.

  • Explicit cache breakpoints are sent only for anthropic/* models. Every other model relies on its provider's automatic caching.

  • A model that reasons but publishes no supported_efforts gets no reasoning selector, so OpenRouter's default applies.

License

MIT

内容来自项目 README(GitHub)↗

链接

同类插件

查看整个分类 →

社区评论

评论公开保存在 GitHub Discussions。加载评论会连接 GitHub 和 Giscus;发表内容需要 GitHub 账号。