MicroHEROX / dsh-unsloth-hands

Listed

Unsloth for DeepSeek Harness — a pure-client tool plugin that connects the DeepSeek Harness online model to a locally running Unsloth Desktop for repetitive text and vision (OCR) labor.

mainModelTool View source

Installation

npm install dsh-unsloth-hands

This command is generated from the GitHub repository address. Inspect the upstream README and source before running it; pin a release or commit when reproducibility matters.

README

Maintainer-authored documentation snapshot.

View on GitHub ↗
Commit 011eee4Synced Aug 18, 2026

Unsloth for DeepSeek Harness

dsh-unsloth-hands — give your DeepSeek Harness agent a pair of local hands.

中文 README

version license node harness unsloth

A third-party tool plugin for DeepSeek Harness that lets the online model (your main conversation model) hand repetitive, token-cheap labor to a local Unsloth Desktop (Unsloth Studio) server — plain text work and vision work (image analysis / OCR / comparison).

The main model stays where your deployment puts it. When it decides a job is better done locally, it calls:

  • unsloth_run — run one prompt on the local text model (batch rewrites, name translations, string munging, short summarization, extraction).
  • unsloth_vision — send images to the local multimodal model (OCR, image analysis, multi-image comparison) with structured report templates.

The plugin is a pure client: it only connects to the Unsloth Desktop you are already running. Model selection, downloading, quantization and context settings all happen in the Unsloth app itself — the plugin never starts, owns, or stops any process, and never kills anything.


✨ What it does

  • Two model-facing tools registered on the harness tool registry (ctx.tools), following the official dsh-tools contract (defineTool, canonical JSON values, pure render/presenters, exec.signal forwarding).
  • Authenticated wire calls: every request carries Authorization: Bearer sk-unsloth-…. The key comes from the apiKey config or the UNSLOTH_API_KEY environment variable (create it in Unsloth Settings → API).
  • Friendly failure modes: before each call the plugin probes /v1/models; if Unsloth Desktop is not running you get a clear, actionable error instead of a generic network failure. A wrong/missing key surfaces as AUTH with a hint.
  • Text + vision wire support: non-streaming OpenAI-compatible chat-completions; images sent as the standard multimodal content array.
  • Three image sources for the vision tool: local file paths, data:/http(s): URLs, or the images attached to the current conversation (read through the harness attachment service).
  • Structured vision prompts — machine-verifiable report contracts: analyze (8-section report), ocr (character-exact), compare (multi-image, 5-section) — plus a fidelity rule for the online model (relay verbatim, never invent, preserve uncertainty).
  • Live config: a llm-unsloth: section in the harness user-settings document overrides the plugin config without a restart.
  • Safe by construction: nothing is ever spawned or killed — the plugin only talks HTTP to your Unsloth Desktop.

🚫 What it does NOT do

  • Does not replace the harness LLM provider — the online model stays the main model; the local model is only reached through the two tools.
  • Does not launch, configure, or stop Unsloth — you run Unsloth Desktop and load the model you want (quantization, context size, GPU settings) in its UI.
  • Does not modify any DeepSeek Harness or Unsloth file; it is a pure add-on plugin.
  • Does not bundle or host GGUF model files — Unsloth downloads and caches them for you.
  • Does not stream responses (tool calls get the full answer in one round trip).

📋 Requirements

ItemRequirement
Node.js≥ 20
DeepSeek Harnessinstalled (npx @deepseek-ai/dsh web or a source checkout), 0.1.0-rc series
Unsloth Desktoprunning, with a model loaded and an API key created (Settings → API)
Modelany GGUF/safetensors model loaded in Unsloth; vision needs a multimodal model (e.g. Qwen3-VL / Gemma vision GGUFs)

📦 Install

The package is a standard harness bundle (declares dsh.bundle with its cordis.patch.yml), so the official install path works:

dsh plugin --profile <name> add dsh-unsloth-hands        # from npm registry
dsh plugin --profile <name> add github:MicroHEROX/dsh-unsloth-hands   # straight from GitHub

It can also be installed as a plain npm dependency in your harness project (the directory whose cordis.yml / cordis.patch.yml composes your deployment), then add the plugin row yourself:

npm install dsh-unsloth-hands
- insert:
    - id: unsloth-tool
      name: 'dsh-unsloth-hands'

From a source checkout of the harness, you can point the plugin row directly at a clone of this repo:

- insert:
    - id: unsloth-tool
      name: '../dsh-unsloth-hands'

Installing from GitHub? pnpm may refuse to run the package's prepare build script until you allowlist it (exact package key printed by pnpm) in your profile's pnpm-workspace.yaml:

allowBuilds:
  dsh-unsloth-hands: true

Then re-run the add. Installing from the npm registry needs no such step.

⚙️ Configure

  1. Start Unsloth Desktop, load the model you want (the model hub downloads GGUFs; the loaded model is the one the tools reach).
  2. Create an API key: avatar → Settings → API → Create, copy the sk-unsloth-… value (it is only shown once).
  3. Add the plugin row to your profile cordis.patch.yml:
- insert:
    - id: unsloth-tool
      name: 'dsh-unsloth-hands'
      config:
        baseURL: 'http://127.0.0.1:8888'              # Unsloth's default port
        apiKey: 'sk-unsloth-xxxx...'                   # from Unsloth Settings → API

That's it. The plugin connects to whatever model is currently loaded — no model names, no config files, no launch flags. Alternatively set UNSLOTH_API_KEY in your environment instead of apiKey.

Installed via dsh plugin add? The bundle already inserts the unsloth-tool row — just override its config in your profile's cordis.patch.yml (the harness override form, no name needed):

- id: unsloth-tool
  config:
    apiKey: 'sk-unsloth-xxxx...'

Full config reference (all 10 fields with defaults): docs/api.md §1.2.

🛠 Using the tools

unsloth_run — text

paramtyperequiredmeaning
promptstringyesinstruction/text sent as a user message
systemstringnooptional system instructions
temperaturenumbernosampling temperature (0–2)
max_tokensintegernooutput cap (default maxTokens)
stopstring[]nostop sequences

Returns { text, reasoning?, model, usage, elapsedMs }.

unsloth_vision — images / OCR

paramtyperequiredmeaning
modeanalyze/ocr/comparenobuilt-in prompt template (default analyze)
promptstringnocustom instruction (overrides the template)
image_pathsstring[]nolocal images (png/jpg/jpeg/webp/gif/bmp, ≤20 MB each)
image_urlsstring[]nodata:image/... or http(s):// URLs
temperaturenumbernosampling temperature (lower for OCR, ~0.2)
max_tokensintegernooutput cap
stopstring[]nostop sequences

Image sources resolve in order: explicit image_paths + image_urls → the most recent image(s) attached to the conversation → clear error. compare sends 2–4 images in ONE request for joint reasoning.

Returns { text, reasoning?, model, images, usage, elapsedMs }.

Vision requires the model currently loaded in Unsloth to be multimodal. Unsloth serves one loaded model at a time — switch to a vision model in the app before calling unsloth_vision.

❓ FAQ

My main model is text-only — how do images get in?

DeepSeek's flagship chat models (and most other routes) are text-only: the harness refuses to send image messages to them (the adapter rejects them with UNSUPPORTED_CONTENT), so you cannot attach an image to the conversation. That is exactly the case unsloth_vision is built for — no harness upload is involved:

  1. When you paste/drop an image in a text-only model's composer, the harness (like OpenCode and Pi) lands it as a temporary file path in your message instead of pixels.
  2. The model sees that path, calls unsloth_vision with image_paths: ["<that path>"] (or an image_urls entry), and the local vision model reads the file directly.
  3. You can also just tell the model a path to any image on disk.

For a main model that DOES support images, the conversation-attachment source also works automatically.

My requests get 401 Unauthorized?

Unsloth requires a valid key on every request. Create one in Settings → API (revoked keys fail with 401) and put it in apiKey or UNSLOTH_API_KEY. The health probe treats a 401 as "server is running" — the error surfaces from the tool call itself with an actionable message.

🗺 Roadmap

Possible / planned directions:

  • More vision modes and prompt templates (document layouts, table extraction).
  • Reading the currently loaded model from /v1/models to fill the wire model field automatically.
  • Publishing to the npm registry and the dsh-plugin topic.
  • Batch jobs: drive many local calls from one agent turn.

Deliberately NOT planned:

  • Launching or managing the Unsloth process — the plugin stays a pure client; you own the app.
  • Becoming an LLM provider adapter — the plugin stays a tool; the online model stays the main model.
  • Streaming responses — tool calls get the full answer in one round trip (simpler and sufficient).
  • Bundling model files or modifying DeepSeek Harness / Unsloth itself.

🗑 Uninstall

  1. Remove the plugin row from your profile cordis.patch.yml (or cordis.yml):
    # delete this block
    - insert:
        - id: unsloth-tool
          name: 'dsh-unsloth-hands'
    
    Installed via dsh plugin? dsh plugin --profile <name> remove dsh-unsloth-hands removes both the dependency and its bundle layer.
  2. Restart the harness (or let HMR reload if you edit the config live). The two tools (unsloth_run, unsloth_vision) are unregistered automatically — the online model no longer sees them.
  3. No residue: the plugin never spawned anything, so there is nothing to stop; your Unsloth Desktop keeps running untouched. Installed via npm? npm uninstall dsh-unsloth-hands.

📌 Version & compatibility

ComponentVersion
This plugin0.1.0
DeepSeek Harness0.1.0-rc series (tested against npm @deepseek-ai/* 0.1.0-rc.7)
Node.js≥ 20
Unsloth Desktopany version exposing the external API (/v1/chat/completions)

Peer dependencies (runtime): @deepseek-ai/cordis ^4.0.1, @deepseek-ai/dsh-tools/dsh-llm/dsh-session/dsh-attachment/dsh-settings/dsh-launch-environment >=0.1.0-rc.2, @deepseek-ai/schemastery ^3.18.1.

🛠 Development

npm install
npm run typecheck   # tsc --noEmit
npm test            # vitest run (46 tests: unit, tool, integration, Loader composition)
npm run build       # clean + tsc -> lib/

Tests include a REAL-composition tier (app boot → Cordis Loader → cordis.yml) per the harness testing policy, and a real-machine driver (tests/real-driver.mjs) for main / auth / not-running scenarios.

📚 Documentation

doccontent
docs/engineering.mdstructure, plugin contract, commands, test tiers
docs/api.mdauthoritative API reference (Config, tools, classes, error codes)
docs/glossary.mdstandard terminology
docs/solutions.mdpitfalls, troubleshooting, methodology

🙏 Credits & thanks

  • DeepSeek AI — the DeepSeek Harness platform this plugin plugs into, and the reference implementations (dsh-llm-deepseek, dsh-tool-todo) that define the patterns we follow.
  • Unsloth — the local training/inference stack and Desktop app whose OpenAI-compatible API makes all of this possible (llama-server underneath), and its docs that guided the integration.
  • Cordis — the plugin runtime that powers the harness.
  • LostRuins / KoboldCpp — the sibling plugin dsh-koboldcpp-hands this project evolved from.
  • The open-source models and quantizers (llama.cpp ecosystem, GGUF) that run locally on your machine.

License

MIT. Not affiliated with DeepSeek AI or Unsloth AI; dsh and unsloth are trademarks of their respective owners.

Project files and signals

Shown items are public repository signals detected in the directory snapshot.

TestsDetected
DocumentationDetected

Repository information

Language
TypeScript
License
MIT
Latest release
v0.1.0
Last updated
Aug 18, 2026, 3:31 AM

Install deliberately

Review source code, permissions, lifecycle hooks, dependencies and network access. Test untrusted plugins in an isolated environment.