DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

yzhclcl /

yzhclcl/dsh-omni-modal

Verified

Adds an Omni-modal settings section to DeepSeek Harness: configure OpenAI-compatible image/video generation endpoints with switchable config profiles, plus generate_image / generate_video / check_video tools.

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@caeddd8a

dsh-omni-modal

License: MIT dsh-plugin

Give a text-only model the ability to draw pictures and shoot videos — without touching a single line of DeepSeek Harness source.

dsh-omni-modal adds an Omni-modal settings section to the DSH settings sidebar, where you can point the harness at any OpenAI-compatible image / video endpoint and register three model-visible tools:

Tool What it does
generate_image Synchronous text-to-image. Saves the PNG/JPEG/WebP to disk and (when the attachment service is available) returns an inline preview card.
generate_video Asynchronous submit. Returns a task id immediately — the clip is still rendering.
check_video Poll that task; on success it downloads the finished video to disk.

Status: usable, tracks the Harness developer preview — interfaces may change.

Image tab

Video tab

Features

  • Live-editable settings page — a real DSH settings section (not a static skill). Change endpoint, key, model, path or default size and hit Save; the three tools pick it up on their next call, no restart.
  • Multiple config profiles — keep several endpoints side by side, name them, switch the active one at a time. Every tool also accepts an optional profile argument to use a non-active profile for a single call.
  • Separate image and video configuration — the two APIs rarely share a shape. Each profile carries an independent baseUrl / apiKey / model / path / size for each side.
  • Extra request parameters — a free-form JSON object merged into the request body, for endpoint-specific knobs ({"quality":"high"}, {"mode":"text","aspect_ratio":"16:9"}, …).
  • Connectivity test — one click probes GET <baseUrl>/models (falling back to the base URL itself) without spending generation quota.
  • Async video by design — submit, remember the task, poll later. Task records survive restarts.
  • No build step — plain ESM host half plus a hand-written CJS client bundle. Nothing to compile, no bundler needed.

Install

Requires Node.js ≥ 18 and DeepSeek Harness.

dsh plugin --profile <profile> add github:yzhclcl/dsh-omni-modal

<profile> is your DSH profile name (web, desktop, …). Add #v0.1.0 to pin a tag.

Then restart DSH — the host half registers its routes and tools at boot, and the settings section appears under Settings → Omni-modal settings.

Manual install (no pnpm / offline)

Copy this repository somewhere permanent, then add it to ~/.dsh/profiles/<profile>/package.json:

{
  "dependencies": {
    "dsh-omni-modal": "link:/absolute/path/to/dsh-omni-modal"
  },
  "dsh": {
    "profile": {
      "bundles": ["dsh-omni-modal"]
    }
  }
}

and mirror the same entry into the importers. → dependencies block of pnpm-lock.yaml, then create the link:

New-Item -ItemType SymbolicLink `
  -Path "$HOME\.dsh\profiles\<profile>\node_modules\dsh-omni-modal" `
  -Target "/absolute/path/to/dsh-omni-modal"

Never copy the package into node_modules instead of linking it. DSH uses a Symbol as the cross-module call key; loading the module twice makes those symbols unequal and every tool call then throws Cannot read properties of undefined (reading 'prepare'), with no recovery after a restart.

Usage

Once installed, just talk to the agent:

  • "Draw a strawberry on a pale blue plate." → generate_image
  • "Make a short video of a paper boat drifting down a stream." → generate_video, then check_video

Settings page

Section Contents
Config profiles Pick the active profile, rename it, add one (copied from the current) / delete one
Image Base URL, API key, model, request path, default size, extra request params
Video Base URL, API key, model, submit path, query path, default resolution, default duration, extra request params
Task records Submitted video tasks with their status and artifact path

Notes:

  • Saving takes effect immediately for the tools — no restart.

  • The base URL must be the address alone. Documentation examples are easy to copy verbatim as POST https://api.example.com/v1; the plugin strips a leading POST/GET/… automatically, but writing just https://api.example.com/v1 is safer.

  • The query path accepts two forms: a relative path (/videos/generations/{id}) appended to the base URL, or a full URL (https://api.example.com/agnesapi?video_id={id}&model_name={model}) used as-is. The full form is necessary when the query endpoint does not live under the base URL.

  • Placeholders available in the query path:

    Placeholder Replaced with
    {id} the task id returned by the submit call
    {model} the model name configured in that profile
  • The tools always use the active profile unless you pass profile explicitly.

Tools

generate_image(prompt, size?, model?, profile?, save_to?)
generate_video(prompt, model?, profile?, size?, seconds?, image_url?)
check_video(task_id, profile?, save_to?)
  • generate_image returns synchronously. The file lands in the plugin data directory's media/ and, if the attachment service is available, an image attachment is produced so the picture shows up in the conversation. The model itself cannot see the pixels — to actually look at the image, call read_image on the returned path.
  • generate_video is asynchronous: it returns a taskId right away. Call check_video(task_id) later; when the status becomes succeeded the artifact is downloaded into media/ automatically.
  • Passing a non-existent profile is a hard error that lists the available profiles, rather than silently falling back to another one — otherwise the model would quietly use the wrong endpoint and model while the result still claims the profile it asked for.
  • check_video failures are soft failures: a missing task (404), an unreachable download URL or a failed download return status: "failed" / "succeeded" plus a message, instead of throwing. These are expected business outcomes, and throwing would abort the whole tool-calling round.
  • Video URL extraction covers the common response shapes: video_url / videoUrl / url, nested video.url, data.*, output.*, result.*, videos[0].url, and more.
  • Task id extraction prefers video_id / videoId over id / task_id — some services (Agnes, for instance) return an internal id that cannot be used for polling.
  • 429 (rate limit) / 503 (busy, queue full) responses come back with a "retry in a moment" hint: they are retryable server states, not configuration errors.

Example: Agnes AI

Agnes AI's image and video endpoints have different shapes; the video side needs the official parameter set. Using apihub.agnes-ai.com:

Image

Field Value
Base URL https://apihub.agnes-ai.com/v1
Model agnes-image-2.5-flash
Request path /images/generations
Default size e.g. 1312x736

Video

Field Value
Base URL https://apihub.agnes-ai.com/v1
Model agnes-video-2.5-flash
Submit path /videos
Query path https://apihub.agnes-ai.com/agnesapi?video_id={id}&model_name={model}
Default resolution 720P (Flash only accepts 720P; anything else returns 400)
Default duration 5 (a string, range "4"–"12")
Extra request params {"mode":"text","aspect_ratio":"16:9"}
  • Video submission is POST /v1/videos (not /v1/videos/generations), and mode is required (text / keyframe / reference) — put it in the extra params.
  • Polling goes through GET /agnesapi?video_id=…&model_name=…, which is outside /v1, hence the full URL in the query path.
  • Output dimensions follow aspect_ratio: 21:9→1680x720, 16:9→1280x704, 4:3→960x720, 1:1→720x720, 3:4→720x960, 9:16→720x1280.
  • The free tier is rate-limited fairly aggressively; repeated calls in a short window will return 429, and it recovers after a wait.

Data locations

What Where
Configuration ~/.dsh-omni-modal/config.json (contains the API key, mode 0600)
Generated artifacts ~/.dsh-omni-modal/media/
Video task records ~/.dsh-omni-modal/tasks.json

Set DSH_OMNI_DATA_DIR to relocate all of it (handy for isolated testing).

Uninstall

dsh plugin --profile <profile> remove dsh-omni-modal

For a manual install, drop the dependency and bundle entries plus the pnpm-lock.yaml entry, then remove the link:

cmd /c rmdir "$HOME\.dsh\profiles\<profile>\node_modules\dsh-omni-modal"

Removing a symbolic link must be done with cmd /c rmdir or [IO.Directory]::Delete(path, $false); never Remove-Item -Recurse, which follows the link and deletes the source directory. Quit DSH completely first.

~/.dsh-omni-modal/ holds your data — delete it yourself if you want it gone.

Implementation notes

  • lib/index.js — the host half (Node ESM): config storage, the /omni-modal/* routes, and the three tool definitions.
  • lib/client.js — the browser half (a hand-written CJS bundle loaded through window.__ModuleLoader__.load): registers the settings.section slot and renders the settings page.
  • cordis.patch.yml — the plugin registry insert row, carried by dsh.bundle.patch.

The host half imports no bare @deepseek-ai/* modules (third-party plugins cannot resolve those internal exports) — only node:* and relative paths. Tool parameter and output schemas are written as standard JSON Schema (required as a string array) rather than the defineTool property-map DSL, which would make the LLM API reject the whole function schema.

中文文档

见 README.zh.md.

License

MIT

—/ 5

No ratings yet

Verified DSH bundle

Commit caeddd8aa388

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout