DeepSeek Harness Plugin

GodD6366/dsh-sub2api

Stars ★ 0 Category Models & Providers Added 2026-08-15 npm @godd6366/dsh-sub2api

Connect a sub2api gateway to DeepSeek Harness: OpenAI-compatible multi-provider routes (OpenAI / Claude / Grok / Gemini) behind one base URL, with per-key model discovery, usage lookup, and global vision/image tools.

Install

# from npm (prebuilt)

dsh plugin --profile web add @godd6366/dsh-sub2api

# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)

dsh plugin --profile web add github:GodD6366/dsh-sub2api

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. Only install sources you trust, and pin a commit (github:owner/repo#sha).

README

中文文档

Connect your sub2api gateway to DeepSeek Harness as model providers.

Sub2API is an AI API gateway that turns subscription quota into OpenAI-compatible endpoints. In its model, each API key is bound to a group, and the group decides the platform (OpenAI / Claude / Grok / Gemini) and the models that key can serve. The four provider routes (sub2api-openai, sub2api-claude, sub2api-grok, sub2api-gemini) are served by the harness's own pi-ai adapter (dsh-llm-pi-ai): this plugin translates its llm-sub2api: settings into llm-pi-ai: provider profiles (all sharing one bare-host base URL, no /v1), and protocol serialization, streaming, and usage accounting all live in pi-ai. The same gateway serves OpenAI, Claude, Grok, and Gemini models side by side, and the harness routes each request to the key whose group owns the requested model.

Features

  • One base URL, four provider routes: sub2api-openai, sub2api-claude, sub2api-grok, sub2api-gemini — each configured with its own key, registered as a live LLM provider the moment the key is set.
  • Streaming chat (backed by pi-ai): SSE streaming, tool calls, reasoning deltas, and token usage are mapped to the harness protocol by dsh-llm-pi-ai, which natively handles wire-format details like top-level function_call items in the Responses API.
  • Model discovery: one-click "fetch models" calls GET {baseURL}/models with the key, so each route's catalog matches exactly what the sub2api group serves.
  • Reasoning effort (thinking mode): reasoning_effort is passed straight through to the gateway and adjustable right in the chat model selector; the settings page's per-model "reasoning strength" column fills each model's real levels from models.dev reasoning_options (e.g. gpt-5.6-sol → none/low/medium/high/xhigh/max, deepseek-v4-flash → low/high/max), with editable levels and an explicit opt-out.
  • Usage lookup: "view usage" calls GET {baseURL}/usage and summarizes quota, balance, rate limits, and subscription windows.
  • Standards-based config: base URL and model catalogs live in the llm-sub2api: settings section ($DSH_HOME/settings.yaml, written by the web Models page); keys go through the harness credential store.
  • Global vision / image tools: analyze_image and generate_image stay available even when the current chat model cannot see or create images. They call a dedicated vision or image model configured on the settings page, and return a description or a workspace file path rather than injecting image blocks into a text-only session.
  • Auto Vision wrapper: image capability for text-only models. Every registered text-only provider route gets a same-name twin (<route>-vision, shown as "… + 自动识图") — our own sub2api-* routes and external providers (official deepseek-official, llm-pi-ai, routes added by other plugins). Twin models carry a -vision id/name suffix (e.g. deepseek-v4-flash-vision) so the picker shows at a glance which models accept images; the suffix is stripped again when the call is delegated back to the base route. The twin's catalog declares inputModalities: ['text', 'image'] so the harness attachment admission passes, and the twin's stream rewrites image blocks into vision-model transcriptions (via the configured tools.analyze model, cached per attachment) before delegating the text-only turn to the original route's adapter. DeepSeek stays the brain; the vision model is only the eyes. Twins follow llm/adapters-updated and skip names already taken by other plugins. Disable with autoVision: false.
  • Provider icons from lobehub/lobe-icons, embedded as SVG in the settings page.

Install

dsh plugin --profile web add @godd6366/dsh-sub2api

or, from this repository:

dsh plugin --profile web add .

Configure

Open Settings → Sub2API 模型 (or edit $DSH_HOME/settings.yaml directly):

llm-sub2api:
  baseURL: http://localhost:8080
  providers:
    openai:
      apiKeyEnv: SUB2API_OPENAI_API_KEY
      models:
        - id: gpt-4o
    claude:
      apiKeyEnv: SUB2API_CLAUDE_API_KEY
    grok:
      apiKeyEnv: SUB2API_GROK_API_KEY
    gemini:
      apiKeyEnv: SUB2API_GEMINI_API_KEY
  tools:
    analyze:
      provider: openai
      model: gpt-4o
    generate:
      provider: openai
      model: gpt-image-1

Store each key through the credentials service (the web Models page writes it, or export SUB2API_OPENAI_API_KEY=… etc.). A route activates only when its platform has a key; clear the key to drop the route again.

Wire protocol (automatic per group)

The gateway serves each platform group upstream through its NATIVE protocol, and pi-ai picks the endpoint automatically from the key's group — no configuration needed. Configure the bare host (no /v1): OpenAI-style endpoints get /v1 appended automatically, and the Anthropic SDK appends /v1/messages itself:

Group Protocol used Endpoint
openai openai-responses POST {baseURL}/v1/responses
claude anthropic-messages POST {baseURL}/v1/messages
grok / gemini openai-completions POST {baseURL}/v1/chat/completions

Speaking the native protocol means the gateway never has to convert chat/completions — that conversion is what drops/misaligns tool-call names and ids for parallel calls (unknown tool "", missing required property …). To force a different endpoint for a group whose gateway does not serve it natively, declare api on the provider in $DSH_HOME/settings.yaml (advanced; no settings-page control):

llm-sub2api:
  baseURL: http://localhost:8080
  providers:
    openai:
      apiKeyEnv: SUB2API_OPENAI_API_KEY
      api: openai-completions   # optional: openai-completions / openai-responses / anthropic-messages
      models:
        - id: gpt-4o

api accepts openai-completions (/v1/chat/completions), openai-responses (/v1/responses), or anthropic-messages (/v1/messages); omitted means the automatic group default above.

Relationship to dsh-llm-pi-ai

This plugin no longer implements the LLM protocol layer itself: the four sub2api-* routes are served by dsh-llm-pi-ai (shipped dormant with dsh-base) through llm-pi-ai: settings profiles. On every llm-sub2api: change (and at boot) the plugin translates the bare-host base URL, per-group models, and key references into hand-declared profiles and writes them to llm-pi-ai:, so routes register/drop live. The settings page, model discovery (GET /v1/models), usage lookup (GET /v1/usage), the vision/image tools, and the Auto Vision twins remain this plugin's own.

Dependency (pi-ai multi-turn crash; attribution: dsh-llm-pi-ai violates pi-ai's contract): pi-ai's AssistantMessage.usage is a required field that its prefix-token estimation dereferences, but the harness's own dsh-llm-pi-ai rebuilds assistant history without usage (the harness Message type records none), so multi-turn conversations throw Cannot read properties of undefined (reading 'totalTokens'). The root fix belongs in dsh-llm-pi-ai (attach a zero Usage); this plugin applies a defensive guard at boot (assistant.usage !== undefined before counting prefix tokens) to @earendil-works/pi-ai/dist/utils/estimate.js inside the dsh install — idempotent, re-applied automatically after a dsh upgrade, so a fresh install works out of the box; on a read-only install run node scripts/patch-pi-ai.mjs manually.

Auto Vision twins and replay: the twin delegates history to the base route with the base provider id, so pi-ai stamps its replay state with that provider while the harness records the message under the twin route — replaying it would fail pi-ai's provider does not match assistant source check (INVALID_REPLAY_STATE). The twin therefore strips replayState from assistant history before delegating (pi-ai then treats it as foreign); twin conversations intentionally skip provider-native replay.

Image input & reasoning effort (auto-filled)

Attaching an image to the session model requires that model to declare the image input modality — otherwise the harness refuses before sending ("model does not support images"). Both fields are auto-filled from models.dev — no manual selection (the model details panel shows the derived values read-only):

  • Image input: derived from models.dev attachment / modalities.input when present (e.g. gpt-5.6-luna → text+image, deepseek-v4-flash → text); otherwise guessed from the model id (gpt-*, claude-*, gemini-*, grok-*, glm-*, … default to text+image). Pin a model to text-only with input: [text] in $DSH_HOME/settings.yaml.
  • Reasoning effort: derived from models.dev reasoning_options when present (e.g. deepseek-v4-flash → high/max); otherwise the default low/medium/high, and models with reasoning: false are marked unsupported.

When the model accepts images, the request carries the image in the group's native protocol: openai → Responses input_image, claude → Messages image (base64), grok/gemini → chat-completions image_url.

Pick the dedicated vision / image models under Settings → Sub2API 模型 → 全局图像工具. Those two tools stay global: a text-only chat model can still call analyze_image (local file or URL) and generate_image (writes into the session workspace). Generation first tries POST {baseURL}/images/generations, then falls back to chat completions when the gateway has no images endpoint.

Development

npm install
npm run build     # tsdown → lib/ + client wrapper
npm run typecheck

License

MIT

Content from the project README on GitHub ↗

Links

More in this category

View the whole category →