Unified static-image generation routing for DeepSeek Harness.
One tool, many engines — health-aware fallback, durable results, counted usage.
| Surface | Status |
|---|---|
| Harness | DeepSeek Harness 0.1.0-rc.6 (compat declared for 0.1.0-rc.6) |
| Node | ^22.19.0 || >=24.0.0 |
| Engines | Any OpenAI-compatible images endpoint; presets for OpenAI Images (gpt-image-1) and Zhipu CogView (cogview-3-flash) |
| Surfaces | Host image_generate tool + web result card + Plugins settings tab |
dsh-draw gives the harness one unified image_generate tool with standard parameters (prompt/size/count/quality/style/engine) that are translated per engine:
- Multi-engine routing — a config-driven chain (OpenAI Images, Zhipu CogView, or any OpenAI-compatible endpoint) walked top-down with health-aware fallback: consecutive failures push an engine into cooldown, and the next healthy engine serves the call.
- Durable results — generated images are saved as workspace attachments (content-addressed, under the harness's attachment policy) and returned as canonical file references.
- Quota accounting — per-session caps on generation calls and image bytes, folded from the durable session log and enforced before engine spend and before storage.
- Credentials as references — engine API keys are environment-variable names resolved per call through the official
ctx.credentialsseam; literal keys are never stored in configuration and never logged. - Web surfaces — an in-conversation result card (images, engine, quota, one-click regenerate) and a Plugins settings tab (engine chain, credential status, probes, quota limits).
model harness
│ image_generate {prompt, ...} ──▶ validate ──▶ quota check ──▶ router
│ openai ──(fail)──▶ cogview ──▶ images
│ ◀── canonical JSON + image blocks (durable attachment refs)
│ └── draw/generated session event (quota + audit)
# 1. install the bundle into your profile
dsh plugin --profile web add "github:PerryLink/dsh-draw#main"
# or from npm (published releases)
dsh plugin --profile web add dsh-draw
# 2. provide the engine keys as credential references (environment variables)
# OPENAI_API_KEY and/or ZHIPU_API_KEY — never in the profile patch
# 3. restart and verify the row
dsh --profile web --dump-config | grep -A2 'id: dsh-draw'Then ask the agent to draw:
> Draw a 1536x1024 landscape of a lighthouse at dusk, vivid style.
- git channel (latest
main):dsh plugin --profile web add "github:PerryLink/dsh-draw#main"— thepreparescript builds with production dependencies only. - npm channel (published releases):
dsh plugin --profile web add dsh-draw. - tarball channel:
pnpm packin this repo, thendsh plugin --profile web add ./dsh-draw-<version>.tgz. - uninstall:
dsh plugin --profile web remove dsh-draw(or remove the row from the profile patch).
If pnpm reports
ERR_PNPM_IGNORED_BUILDSfor this package (esbuild's harmless platform-binary validation), addallowBuilds: { esbuild: true }to yourpnpm-workspace.yaml— thedshCLI prints the exact snippet.
All tunables are Schemastery Config fields (changeable from cordis.yml). An id-targeted override replaces the whole row — restate every key you need. cordis.patch.yml documents each key inline.
| Key | Default | Meaning |
|---|---|---|
engines |
OpenAI + CogView presets | Ordered engine chain, walked top-down with fallback; each entry: id, baseUrl (no credentials), model, apiKeyRef (env-var name), enabled, sizeMap, qualitySupported, styleSupported, responseFormat (b64_json/url), imageMediaType |
defaultEngine |
openai |
Engine id the router prefers; must name a configured engine |
requestTimeoutMs |
120000 |
Per-generation HTTP timeout (1000..600000) |
maxImagesPerCall |
4 |
Cap on images one call may produce (1..10) |
maxPromptLength |
4000 |
Prompt length cap in characters (1..32000) |
maxGenerationsPerSession |
200 |
Per-session generation-call cap (1..100000) |
maxBytesPerSession |
209715200 |
Per-session image-byte cap (1048576..4294967296) |
failureThreshold |
2 |
Consecutive failures before an engine enters cooldown (1..10) |
cooldownMs |
60000 |
Engine cooldown after the threshold trips (1000..3600000) |
Example override in your profile patch:
- insert:
- id: dsh-draw
name: dsh-draw
config:
defaultEngine: cogview
maxImagesPerCall: 2| Surface | Notes |
|---|---|
image_generate |
Standard parameters; returns canonical JSON (engine/model/size, image references, quota, fallback flag, attempts) plus image content blocks |
Result card (tool.call.toolview, key image_generate) |
Images, engine/quota line, one-click regenerate (full drawer path: quota + routing + audit) |
| Settings tab (Plugins → Image generation) | Engine chain, credential status, set/remove API keys (credential references), connectivity probes, quota limits |
- Permissions: the plugin makes outbound HTTPS calls to the configured engine endpoints only; every other surface is read-only. The settings tab's only writes are credential set/remove calls on the official
ctx.credentialsseam. - Data: generated images are saved through the official attachment store under the harness's own attachment policy. Quota usage is folded from the
draw/generatedsession events — nothing else is stored. - Session log: the
draw/generatedevent records engine, model, standardized request, byte totals, and attachment ids — the audit facts, never the API keys.
- Credential references, never literals.
apiKeyRefnames an environment variable; abaseUrlembedding credentials fails the load loudly. - Sanitized display. URLs, probe notes, and error text are redacted (userinfo passwords, credential query values, bearer tokens, JWTs) before any display or log.
- Quota before spend. Generation and byte caps are checked before engine calls and before attachment storage; exhausted sessions fail fast without spending engine credits.
- Fail loud, fall back deliberately. Malformed responses surface as structured errors; a failing engine is skipped after its cooldown threshold, and a fully exhausted chain returns the complete attempt record instead of pretending success.
- Image models only. No video, audio, or edit endpoints; no vision understanding.
- Engine compatibility. Engines must speak the OpenAI
POST /images/generationsshape (base64 or URL delivery); provider-specific extras are out of scope. - Cost awareness is structural. The plugin counts calls and bytes but does not know engine pricing — pair with
dsh-budgetfor cost governance.
pnpm install # node ^22.19 || >=24
pnpm run typecheck # tsc: src + tests against the local harness checkout
pnpm run typecheck:ci # tsc against the published 0.1.0-rc.6 faces (no paths)
pnpm test # vitest: 77 tests, 11 suites (scripted transport, real Context/Session/ToolRuntime)
pnpm run build # tsc declarations + tsdown bundles (lib/)
pnpm run verify:self-contained # dependency specs resolve from the registry
pnpm run verify:artifacts # host ESM face + typert manifest + browser bundle + config files
pnpm pack # the published tarballdsh, dsh-plugin, deepseek-harness, deepseek, cordis, image-generation, openai-images, cogview, zhipu, text-to-image
- @PerryLink — creator and maintainer: engine router, drawer, quota accounting, Typert wire vocabulary, browser half, and the five-language docs.
Apache License 2.0 © 2026 dsh-draw contributors