Hands repetitive text and vision labor (OCR, image analysis, comparison) to a locally running Unsloth Desktop (Unsloth Studio) server through unsloth_run and unsloth_vision tools; pure HTTP client, never spawns or owns processes.
Install
# from npm (prebuilt)
dsh plugin --profile web add dsh-unsloth-hands
# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)
dsh plugin --profile web add github:MicroHEROX/dsh-unsloth-hands
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
Unsloth for DeepSeek Harness
dsh-unsloth-hands — give your DeepSeek Harness agent a pair of local hands.
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 officialdsh-toolscontract (defineTool, canonical JSON values, pure render/presenters,exec.signalforwarding). - Authenticated wire calls: every request carries
Authorization: Bearer sk-unsloth-…. The key comes from theapiKeyconfig or theUNSLOTH_API_KEYenvironment 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 asAUTHwith a hint. - Text + vision wire support: non-streaming OpenAI-compatible chat-completions; images sent as the standard multimodal
contentarray. - 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
| Item | Requirement |
|---|---|
| Node.js | ≥ 20 |
| DeepSeek Harness | installed (npx @deepseek-ai/dsh web or a source checkout), 0.1.0-rc series |
| Unsloth Desktop | running, with a model loaded and an API key created (Settings → API) |
| Model | any 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
preparebuild script until you allowlist it (exact package key printed by pnpm) in your profile'spnpm-workspace.yaml:allowBuilds: dsh-unsloth-hands: trueThen re-run the
add. Installing from the npm registry needs no such step.
⚙️ Configure
- Start Unsloth Desktop, load the model you want (the model hub downloads GGUFs; the loaded model is the one the tools reach).
- Create an API key: avatar → Settings → API → Create, copy the
sk-unsloth-…value (it is only shown once). - 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 theunsloth-toolrow — just override its config in your profile'scordis.patch.yml(the harness override form, nonameneeded):- 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
| param | type | required | meaning |
|---|---|---|---|
prompt |
string | yes | instruction/text sent as a user message |
system |
string | no | optional system instructions |
temperature |
number | no | sampling temperature (0–2) |
max_tokens |
integer | no | output cap (default maxTokens) |
stop |
string[] | no | stop sequences |
Returns { text, reasoning?, model, usage, elapsedMs }.
unsloth_vision — images / OCR
| param | type | required | meaning |
|---|---|---|---|
mode |
analyze/ocr/compare |
no | built-in prompt template (default analyze) |
prompt |
string | no | custom instruction (overrides the template) |
image_paths |
string[] | no | local images (png/jpg/jpeg/webp/gif/bmp, ≤20 MB each) |
image_urls |
string[] | no | data:image/... or http(s):// URLs |
temperature |
number | no | sampling temperature (lower for OCR, ~0.2) |
max_tokens |
integer | no | output cap |
stop |
string[] | no | stop 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:
- 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.
- The model sees that path, calls
unsloth_visionwithimage_paths: ["<that path>"](or animage_urlsentry), and the local vision model reads the file directly. - 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/modelsto fill the wiremodelfield automatically. - Publishing to the npm registry and the
dsh-plugintopic. - 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
- Remove the plugin row from your profile
cordis.patch.yml(orcordis.yml):
Installed via# delete this block - insert: - id: unsloth-tool name: 'dsh-unsloth-hands'dsh plugin?dsh plugin --profile <name> remove dsh-unsloth-handsremoves both the dependency and its bundle layer. - 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. - 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
| Component | Version |
|---|---|
| This plugin | 0.1.0 |
| DeepSeek Harness | 0.1.0-rc series (tested against npm @deepseek-ai/* 0.1.0-rc.8 / 0.1.1-rc.1) |
| Node.js | ≥ 20 |
| Unsloth Desktop | any 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
| doc | content |
|---|---|
| docs/engineering.md | structure, plugin contract, commands, test tiers |
| docs/api.md | authoritative API reference (Config, tools, classes, error codes) |
| docs/glossary.md | standard terminology |
| docs/solutions.md | pitfalls, 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-handsthis 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.
Links
More in this category
liustack/modlens★ 4945
Vision bridge for text-only models: paste an image, get structured JSON evidence (OCR, layout, semantics).
ysr666/dsh-vision-router★ 1264
Free vision for text-only agents: built-in keyless vision chain plus pixel tools (Q&A, grounding, crop, pixel diff, colors, OCR, SVG trace, cutout, screenshots); paste an image to use it.
Anionex/dsh-vision-toolkit★ 943
Vision for text-only models: paste an image and the model switches to a Vision Toolkit variant for image Q&A, multi-image comparison, long-screenshot OCR, screenshot-to-UI reproduction, element grounding, and pixel diff. No API key by default — images are processed by the author-hosted free service, 100 per machine per day; configurable to your own provider.
SAOS-3624/dsh-see-now★ 612
Lets a text-only agent watch a live screen or camera feed: every frame is scored by how much the picture changed, only the ones that matter are packed into one numbered sheet with an audio envelope, so the model looks at what actually changed instead of a screenshot every few seconds.
SAOS-3624/dsh-video-storyboard★ 530
Turns a video (a local file, or a Bilibili/yt-dlp link) into one addressable storyboard grid — numbered cells with exact timestamps, per-cell transcript and an audio envelope on the same timeline — and video_zoom re-reads any window at higher resolution, so a text-only model can look at the video instead of reading a summary.
dickpy/dsh-imagegen★ 188
AI image generation for the DSH Web GUI: text-to-image and image-to-image through a configurable OpenAI-compatible endpoint (gpt-image-2 / gpt-image-1 / dall-e-3), with an api_url/api_key settings card and a sidebar split-pane generation studio.
Community comments
Comments are public GitHub Discussions. Loading them connects to GitHub and Giscus; a GitHub account is required to post.