linxuhao/Deepseek-Continuity

linxuhao/Deepseek-Continuity

本地生成图像/配音/音乐/音效并听写,同一性锁定:角色、动物、道具与配音角色定妆定声一次,之后每次调用复用同一份参考;退化产物(接近纯色的图、静音的音频)被拒绝而不是当成成功返回;生成的台词可以听写回文字,好让吞掉结尾的克隆暴露出来。空闲时引擎卸载显存,生图与听写也可改指向任意 OpenAI 形状的 API 而不用本地 Vulkan 后端。

视觉与多模态agent-toolsdeepseek-harnessimage-generation

场记 / Continuity

A DeepSeek Harness plugin that gives an agent local image / speech / music / SFX generation, the transcription to hear its own output back, and a memory of what it made — the same character stays the same character across every call, and a failed generation is never allowed to pass as a success.

Runs locally. Models are lazy-loaded per request and released when idle, so when you are not using it the GPU is untouched — 0.21 GiB resident, measured. You can play a game on the same card.

Or keep only the half you want local: image generation speaks any OpenAI-shaped /v1/images/* API and transcription any OpenAI-shaped /v1/audio/transcriptions, and telling continuity-setup so means that half's engine and weights are never downloaded — the 8 GiB VRAM gate leaves with the image half. See Bring your own backend.

场记 is the continuity supervisor on a film set. Their entire job is two things: make sure
the costume, hair and props match between takes, and catch the mistake on set before it is
cut into the film. That is exactly this plugin's job.

What it looks like

One create_character call fixed this face. Everything after it is a single subject_image call carrying nothing but a scene — no reference image passed by hand, no re-describing the character, no retouching. These are the files the tools returned.

create_character — the reference every later call is held against “close-up portrait, lit from below by a lantern” “strict side profile, on a cliff edge at dusk” “from behind, looking back over her shoulder, snow” “sitting by a campfire, cleaning her gauntlet, low angle” “wearing heavy red lacquered plate armour” “as a stark black and white woodcut print”

The blind right eye, the scar through the brow, the bone pendant and the brass gauntlet come through all seven. That is the entire point: the same description through generate_image gives you a different woman every time, which is how a game ends up with three protagonists.

Two things it did not do, kept here because a demo that only shows the wins teaches you nothing about the tool:

  • Style requests only partly take. The woodcut landed. "Pixel-art sprite" was asked for

twice and ignored both times — the reference image dominates the style of the output, which is exactly the mechanism that makes the face hold.

  • The armour is layered, not swapped. The face and the gauntlet held, but the red plate

went on *over* the grey coat instead of replacing it.

The same for voices

create_actor once, then one actor_tts call per line. Each line comes back as its own 24 kHz mono WAV; they are joined here into one clip because GitHub will not play a .wav inline.

▶ Play the 19-second clip — GitHub strips `` out of a README, so the picture above is a still and the link opens GitHub's own player.

别碰那扇门。上一个碰它的人,我埋在山下第三棵松树底下。
我这只眼睛看不见,可另一只看得比你清楚。
拿上灯,跟紧我。这条路我走过十七次,没有哪一次是一样的。

Three different lines, three different lengths, one voice. Through generate_speech — the same voice description, no actor — those three lines are three different people; the measurement behind that claim is in Two things it actually does.

What it can do

· tools · Look · create_character create_animal create_object import_subject subject_image · pin a character, animal or prop once; every later image is that one Voice · create_actor import_actor actor_tts · cast a voice once; every later line is that voice Hearing · transcribe · read a WAV back as text — including one this plugin just made Music · generate_music · Stable Audio, up to 120 s, no loop points — score a scene, not a BGM loop SFX · gen_sfx sfx_presets · procedural sfxr: milliseconds, byte-identical for a given seed, no model and no VRAM at all One-offs · generate_image generate_speech · for things that never recur; their own descriptions say so and point back at the pinning tools Post · remove_bg slice_sheet · real RGBA cutout (CPU), and a grid sheet cut into single frames State · continuity_status · which engines are up, which capabilities are on, where the assets live

Two of these read *in* rather than write out, and they are the ones people miss:

  • `import_actor` / `import_subject` pin something you already have — a real actor's

recording, a character sheet drawn elsewhere — and everything downstream is identical to a natively cast one (measured: an imported actor tracks a native one to 11 Hz).

  • `transcribe` closes the loop. A cloned line that swallowed its last two words sounds

completely normal; it is only visible once you read it back and compare it to the script. That is also what fills in an imported recording's transcript when you don't have one (the ASR model loads on demand and unloads with the rest — 3.05 GB while resident, 0.4 s for 9.5 s of audio).

The table above is the short version — all 21 tools, grouped, are in Tools.

Install

uvx --from dsh-continuity continuity-setup

That one command does the whole backend: preflight → build the engines → fetch only the weights this machine can use → start them.

The PyPI distribution is `dsh-continuity`
(the import name stays continuity_mcp). It is *not* continuity-mcp — that name on PyPI
belongs to an unrelated project, so do not uvx continuity-mcp.
To run from source instead: uvx --from git+https://github.com/linxuhao/Deepseek-Continuity continuity-setup

Then add the plugin to your dsh profile. `dsh plugin` shells out to pnpm, so install that first if you have not (corepack enable pnpm); without it the command stops at pnpm not found on PATH:

dsh plugin --profile <your-profile> add dsh-plugin-continuity

Add it to a profile that already has an app bundle. If you point it at a *new* profile, dsh creates one containing only @deepseek-ai/dsh-base plus this plugin — no app, so booting it does nothing and hangs. Add the app yourself in ~/.dsh/profiles//package.json:

"dsh": { "profile": { "bundles": [
  "@deepseek-ai/dsh-base", "@deepseek-ai/dsh-headless", "dsh-plugin-continuity"
] } }

The bundle reads its settings from the environment, so export what continuity-setup printed for your machine before booting the profile:

export CONTINUITY_STATE_DIR=~/.continuity
export CONTINUITY_SD_SERVER=http://127.0.0.1:9020
export CONTINUITY_AUDIO_SERVER=http://127.0.0.1:9021

To wire it by hand instead — continuity-setup prints this block filled in for your machine:

- insert:
    - id: continuity
      name: '@deepseek-ai/dsh-mcp-client'
      config:
        serverName: continuity
        transport: stdio
        command: uvx
        args: ['--from', 'dsh-continuity', 'continuity-mcp']
        env:
          CONTINUITY_STATE_DIR: !!js process.env.CONTINUITY_STATE_DIR ?? ''
          SD_SERVER: !!js process.env.CONTINUITY_SD_SERVER ?? ''
          AUDIO_SERVER: !!js process.env.CONTINUITY_AUDIO_SERVER ?? ''

(the complete row, with every passthrough documented: bundle/cordis.patch.yml)

continuity-setup checks the machine before it downloads anything, and sizes the install to what it finds. Run continuity-setup --check first to see what it would do — that reads hardware and changes nothing:

体检结果:
  GPU     AMD Radeon RX 7800 XT (RADV NAVI32)  (16.0 GiB, 此刻可用 15.8 GiB, DISCRETE_GPU, vulkan device 1)
          未选 AMD Radeon RX 7900 XTX (RADV NAVI31) (24.0 GiB, 此刻可用 1.4 GiB)
          跳过 llvmpipe —— 软件渲染, 不是真显卡
  内存    30.9 GiB
  磁盘    3118.4 GiB 可用 / 需要 34 GiB
  生图    启用
  音频    启用
  抠图默认档  best

Two details in there that exist because the naive version is wrong:

  • It skips `llvmpipe`. The software rasterizer advertises 30.9 GiB of "VRAM" (it is your

system RAM) and would win any "pick the biggest card" contest. Everything would then run on the CPU — working, looking completely normal, and unusably slow.

  • It picks by free VRAM, gates by total VRAM. On the machine above the 24 GiB card has

1.4 GiB actually free because another process holds it; picking by size would select it and then OOM. But "is this card good enough" is a hardware question, so that one uses the total — otherwise a 16 GiB card would be rejected for having a game open.

Minimum requirements

· Minimum · Notes GPU · 8 GiB VRAM · Peak is 6.80 GiB (measured). Requests are serialized, so peak is one model, not the sum. GPU API · Vulkan 1.2+ · No CUDA, no ROCm. Kernels are SPIR-V compiled at runtime. Disk · 34 GiB during install, 21.8 GiB after · 19.7 weights + 2.1 runtime image + 8.5 build layers (reclaimable) + 4 headroom. Host RAM · 16 GiB (8 GiB workable — see below) · Driven by transient peaks, not idle. CPU · any x86-64 · Background removal runs on CPU.

Audio-only installs (see below) need 20 GiB during install, 9.5 GiB after.

Every row above is about the halves you run locally. --image-api-server (or --sd-server) drops the GPU row to the 4 GiB the audio half needs and leaves 10.1 GiB of weights undownloaded, --asr-server another 2.3 GiB, and --audio-server the rest — see Bring your own backend.

All VRAM/RAM figures on this page are GiB (2³⁰ bytes), which is what rocm-smi and vulkaninfo report. An earlier version of this README labelled them GB; that was wrong and made the headroom look tighter than it is.

Vulkan instead of CUDA is not a preference — it is why this runs at all. ROCm miscomputes VAE decode on this GPU class (ROCm#6633): five decodes of identical input returned five mutually uncorrelated results. Vulkan/RADV compiles SPIR-V at runtime instead of looking up a per-arch kernel table, and is correct and faster here. The side effect is portability across all three vendors.

GPU vendors

· How the container gets the GPU · Status AMD · /dev/dri + mesa RADV inside the image · Tested (RX 7800 XT, RX 7900 XTX) Intel · /dev/dri + mesa ANV inside the image — same mechanism · Untested NVIDIA · nvidia-container-toolkit injects the host driver (docker-compose.nvidia.yml) · Untested

I only have AMD cards, so I will not claim more than that. Nothing in the code is AMD-specific — no CUDA, no ROCm, no HIP, no /dev/kfd, no gfx targets — and ggml's Vulkan backend is widely run on NVIDIA. But "widely run" is not "I verified it".

The NVIDIA path is a genuinely different wiring, not just a different card: NVIDIA's Vulkan ICD lives in the host driver and must be injected by nvidia-container-toolkit, with NVIDIA_DRIVER_CAPABILITIES including graphics — the default compute,utility gives you working CUDA and an empty device list in Vulkan. continuity-setup detects NVIDIA, uses the right compose overlay, and tells you the path is unverified. Reports either way are welcome.

Host RAM in detail

Idle is negligible; the peaks are what sizes the machine.

operation · peak RSS idle · 0.52 GiB music · 0.50 GiB speech · 1.63 GiB image (1024²) · 4.94 GiB remove_bg quality="best" · 7.74 GiB remove_bg quality="fast" · 1.33 GiB

Background removal is the ceiling, and its cost is independent of input size — 256 / 512 / 1024 px all peak at ~6.8 GiB, because BiRefNet runs at a fixed internal resolution.

On 16 GiB everything works. Below 12 GiB, continuity-setup sets the default to quality="fast" (u2netp): peak drops to 1.33 GiB and it runs in 0.6 s instead of 7.2 s. On a typical game sprite the two are hard to tell apart by eye — checked side by side over a magenta backdrop with the edges zoomed. best remains the default where there is room, because the models do differ in principle on fine edges (hair, semi-transparent fringes), but treat fast as a legitimate choice rather than a degraded fallback.

One rule, not a tier list

Jobs are serialized, so at any moment exactly one model is needed. Everything else is released before the job starts. That is the whole VRAM policy. (The one exception is a split deployment: if the image backend is on a different host from the audio one, they are not competing for a card, so nothing is released — freeing local VRAM for a remote job buys nothing and costs a reload.)

It buys a property worth more than a few saved seconds: peak VRAM is a constant 6.80 GiB regardless of what you call, in what order. Measured over an alternating speech→image→speech→image sequence:

· peak · speech · image · 6 calls keep models resident · 10.94 GiB · 2.8 s avg · 11.5 s · 42.9 s release what isn't needed · 6.79 GiB · 4.8 s · 11.6 s · 49.2 s

Keeping them resident is 16% faster and does not fit an 8 GiB card — and "voice a line, then draw something" is the most ordinary sequence there is. An earlier version of this README quoted 7.84 GiB for that overlap; that came from a lighter sequence I happened to test, and using it as the ceiling was wrong. A cloned voice keeps its reference audio resident too, which is where the rest comes from.

What the reload actually costs: 4.8 s instead of 1.2 s, and only on the first call after switching away. Ten dialogue lines in a row pay it once:

第 1 句 4.63s   之后九句平均 1.19s   十句合计 15.4s

So there is no VRAM tier list, and no 12 GiB threshold. Above 8 GiB every card behaves identically. Below 8 GiB the installer explains why image generation will not fit and asks whether to install the audio half alone — it does not quietly substitute a different product:

生图    显存不足
          Fake GTX 1060 只有 6.0 GiB, 而生图实测峰值 6.80 GiB, 需要 8 GiB。
          换更小的生图模型省不下这部分 (Q4 与 Q8 峰值相同 6.60 / 6.59), 降分辨率也不行
          —— 瓶颈是那个 8 GiB 不量化的文本编码器。
          音频那半仍然可以装: 铸声/配音/听写/音乐/音效/抠图都能用, 4 GiB 就够。

  ⚠️ 这张卡装不了生图那半。
     只装音频那半 (铸声/配音/听写/音乐/音效/抠图)? [y/N]

The audio-only install is a real product, not a consolation prize: casting voices, dialogue, music, SFX and cutout all work in 4 GiB.

What does not adapt at all: the image model. Quantizing it does not move VRAM — Q4_0 (2.29 GiB of weights) peaks at 6.60 GiB, Q8_0 (4.01 GiB) at 6.59 GiB, identical. Lowering resolution does not help either (512 / 768 / 1024 all peak the same; only time changes). The bottleneck is the 8 GiB unquantized 4B text encoder. So there is no "medium" image tier to offer, only installed or not. (Q4_0 ships anyway — same VRAM, 1.7 GiB less disk.)