dsh-continuity 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dsh_continuity-0.1.0/.gitignore +6 -0
- dsh_continuity-0.1.0/LICENSE +21 -0
- dsh_continuity-0.1.0/PKG-INFO +339 -0
- dsh_continuity-0.1.0/README.md +321 -0
- dsh_continuity-0.1.0/bundle/cordis.patch.yml +39 -0
- dsh_continuity-0.1.0/bundle/package.json +30 -0
- dsh_continuity-0.1.0/pyproject.toml +40 -0
- dsh_continuity-0.1.0/src/continuity_mcp/__init__.py +1 -0
- dsh_continuity-0.1.0/src/continuity_mcp/config.py +156 -0
- dsh_continuity-0.1.0/src/continuity_mcp/cutout.py +315 -0
- dsh_continuity-0.1.0/src/continuity_mcp/deploy/Dockerfile +58 -0
- dsh_continuity-0.1.0/src/continuity_mcp/deploy/audio_server.json.tmpl +37 -0
- dsh_continuity-0.1.0/src/continuity_mcp/deploy/docker-compose.nvidia.yml +46 -0
- dsh_continuity-0.1.0/src/continuity_mcp/deploy/docker-compose.yml +61 -0
- dsh_continuity-0.1.0/src/continuity_mcp/deploy/models.json +57 -0
- dsh_continuity-0.1.0/src/continuity_mcp/engines.py +196 -0
- dsh_continuity-0.1.0/src/continuity_mcp/jobs.py +394 -0
- dsh_continuity-0.1.0/src/continuity_mcp/preflight.py +322 -0
- dsh_continuity-0.1.0/src/continuity_mcp/server.py +547 -0
- dsh_continuity-0.1.0/src/continuity_mcp/setup_cli.py +298 -0
- dsh_continuity-0.1.0/src/continuity_mcp/sfx.py +161 -0
- dsh_continuity-0.1.0/src/continuity_mcp/store.py +82 -0
- dsh_continuity-0.1.0/src/continuity_mcp/verify.py +49 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Xuhao Lin
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: dsh-continuity
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Asset consistency and output verification for agent-generated game media. Local Vulkan backends, zero VRAM when idle.
|
|
5
|
+
Project-URL: Homepage, https://github.com/linxuhao/Deepseek-Continuity
|
|
6
|
+
Author: Xuhao Lin
|
|
7
|
+
License: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Keywords: deepseek-harness,dsh-plugin,game-assets,image-generation,mcp,tts
|
|
10
|
+
Requires-Python: >=3.11
|
|
11
|
+
Requires-Dist: huggingface-hub
|
|
12
|
+
Requires-Dist: mcp>=2.0
|
|
13
|
+
Requires-Dist: numpy
|
|
14
|
+
Requires-Dist: onnxruntime
|
|
15
|
+
Requires-Dist: pillow
|
|
16
|
+
Requires-Dist: rembg
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
# 场记 / Continuity
|
|
20
|
+
|
|
21
|
+
A [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) plugin that gives an
|
|
22
|
+
agent local image / speech / music / SFX generation **and remembers what it made** — the same
|
|
23
|
+
character stays the same character across every call, and a failed generation is never allowed
|
|
24
|
+
to pass as a success.
|
|
25
|
+
|
|
26
|
+
Runs locally. Models are lazy-loaded per request and released when idle, so **when you are
|
|
27
|
+
not using it the GPU is untouched** — 0.21 GiB resident, measured. You can play a game on
|
|
28
|
+
the same card.
|
|
29
|
+
|
|
30
|
+
> 场记 is the continuity supervisor on a film set. Their entire job is two things: make sure
|
|
31
|
+
> the costume, hair and props match between takes, and catch the mistake on set before it is
|
|
32
|
+
> cut into the film. That is exactly this plugin's job.
|
|
33
|
+
|
|
34
|
+
## Install
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
uvx --from dsh-continuity continuity-setup
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
That one command does the whole backend: preflight → build the engines → fetch only the weights
|
|
41
|
+
this machine can use → start them.
|
|
42
|
+
|
|
43
|
+
> The PyPI distribution is **`dsh-continuity`** (the import name stays `continuity_mcp`). It is *not*
|
|
44
|
+
> `continuity-mcp` — that name on PyPI belongs to an unrelated project, so do not `uvx
|
|
45
|
+
> continuity-mcp`.
|
|
46
|
+
>
|
|
47
|
+
> Until the first release lands, install straight from the repo — this works today:
|
|
48
|
+
>
|
|
49
|
+
> ```bash
|
|
50
|
+
> uvx --from git+https://github.com/linxuhao/Deepseek-Continuity continuity-setup
|
|
51
|
+
> ```
|
|
52
|
+
|
|
53
|
+
`continuity-setup` checks the machine before it downloads anything, and sizes the install to
|
|
54
|
+
what it finds. Run `continuity-setup --check` first to see what it would do — that reads
|
|
55
|
+
hardware and changes nothing:
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
体检结果:
|
|
59
|
+
GPU AMD Radeon RX 7800 XT (RADV NAVI32) (16.0 GiB, 此刻可用 15.8 GiB, DISCRETE_GPU, vulkan device 1)
|
|
60
|
+
未选 AMD Radeon RX 7900 XTX (RADV NAVI31) (24.0 GiB, 此刻可用 1.4 GiB)
|
|
61
|
+
跳过 llvmpipe —— 软件渲染, 不是真显卡
|
|
62
|
+
内存 30.9 GiB
|
|
63
|
+
磁盘 3118.4 GiB 可用 / 需要 30 GiB
|
|
64
|
+
生图 启用
|
|
65
|
+
音频 启用
|
|
66
|
+
抠图默认档 best
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Two details in there that exist because the naive version is wrong:
|
|
70
|
+
|
|
71
|
+
- **It skips `llvmpipe`.** The software rasterizer advertises 30.9 GiB of "VRAM" (it is your
|
|
72
|
+
system RAM) and would win any "pick the biggest card" contest. Everything would then run on
|
|
73
|
+
the CPU — working, looking completely normal, and unusably slow.
|
|
74
|
+
- **It picks by free VRAM, gates by total VRAM.** On the machine above the 24 GiB card has
|
|
75
|
+
1.4 GiB actually free because another process holds it; picking by size would select it and
|
|
76
|
+
then OOM. But "is this card good enough" is a hardware question, so that one uses the total —
|
|
77
|
+
otherwise a 16 GiB card would be rejected for having a game open.
|
|
78
|
+
|
|
79
|
+
## Minimum requirements
|
|
80
|
+
|
|
81
|
+
| | Minimum | Notes |
|
|
82
|
+
|---|---|---|
|
|
83
|
+
| **GPU** | **8 GiB VRAM** | Peak is 6.80 GiB (measured). Requests are serialized, so peak is one model, not the sum. |
|
|
84
|
+
| **GPU API** | **Vulkan 1.2+** | **No CUDA, no ROCm.** Kernels are SPIR-V compiled at runtime. |
|
|
85
|
+
| **Disk** | **30 GiB during install**, 19.5 GiB after | 17.4 weights + 2.1 runtime image + 8.5 build layers (reclaimable). |
|
|
86
|
+
| **Host RAM** | **16 GiB** (8 GiB workable — see below) | Driven by transient peaks, not idle. |
|
|
87
|
+
| **CPU** | any x86-64 | Background removal runs on CPU. |
|
|
88
|
+
|
|
89
|
+
Audio-only installs (see below) need **20 GiB during install, 9.5 GiB after**.
|
|
90
|
+
|
|
91
|
+
All VRAM/RAM figures on this page are **GiB** (2³⁰ bytes), which is what `rocm-smi` and
|
|
92
|
+
`vulkaninfo` report. An earlier version of this README labelled them GB; that was wrong and
|
|
93
|
+
made the headroom look tighter than it is.
|
|
94
|
+
|
|
95
|
+
Vulkan instead of CUDA is not a preference — it is why this runs at all. ROCm miscomputes
|
|
96
|
+
VAE decode on this GPU class ([ROCm#6633](https://github.com/ROCm/ROCm/issues/6633)):
|
|
97
|
+
five decodes of identical input returned five mutually uncorrelated results. Vulkan/RADV
|
|
98
|
+
compiles SPIR-V at runtime instead of looking up a per-arch kernel table, and is correct
|
|
99
|
+
and faster here. The side effect is portability across all three vendors.
|
|
100
|
+
|
|
101
|
+
### GPU vendors
|
|
102
|
+
|
|
103
|
+
| | How the container gets the GPU | Status |
|
|
104
|
+
|---|---|---|
|
|
105
|
+
| **AMD** | `/dev/dri` + mesa RADV inside the image | **Tested** (RX 7800 XT, RX 7900 XTX) |
|
|
106
|
+
| **Intel** | `/dev/dri` + mesa ANV inside the image — same mechanism | Untested |
|
|
107
|
+
| **NVIDIA** | `nvidia-container-toolkit` injects the host driver (`docker-compose.nvidia.yml`) | Untested |
|
|
108
|
+
|
|
109
|
+
I only have AMD cards, so I will not claim more than that. Nothing in the code is
|
|
110
|
+
AMD-specific — no CUDA, no ROCm, no HIP, no `/dev/kfd`, no `gfx` targets — and ggml's Vulkan
|
|
111
|
+
backend is widely run on NVIDIA. But "widely run" is not "I verified it".
|
|
112
|
+
|
|
113
|
+
The NVIDIA path is a genuinely different wiring, not just a different card: NVIDIA's Vulkan
|
|
114
|
+
ICD lives in the host driver and must be injected by `nvidia-container-toolkit`, with
|
|
115
|
+
`NVIDIA_DRIVER_CAPABILITIES` including `graphics` — the default `compute,utility` gives you
|
|
116
|
+
working CUDA and an empty device list in Vulkan. `continuity-setup` detects NVIDIA, uses the
|
|
117
|
+
right compose overlay, and tells you the path is unverified. Reports either way are welcome.
|
|
118
|
+
|
|
119
|
+
### Host RAM in detail
|
|
120
|
+
|
|
121
|
+
Idle is negligible; the peaks are what sizes the machine.
|
|
122
|
+
|
|
123
|
+
| operation | peak RSS |
|
|
124
|
+
|---|---|
|
|
125
|
+
| idle | 0.52 GiB |
|
|
126
|
+
| music | 0.50 GiB |
|
|
127
|
+
| speech | 1.63 GiB |
|
|
128
|
+
| image (1024²) | 4.94 GiB |
|
|
129
|
+
| `remove_bg` `quality="best"` | **7.74 GiB** |
|
|
130
|
+
| `remove_bg` `quality="fast"` | 1.33 GiB |
|
|
131
|
+
|
|
132
|
+
Background removal is the ceiling, and its cost is **independent of input size** — 256 / 512 /
|
|
133
|
+
1024 px all peak at ~6.8 GiB, because BiRefNet runs at a fixed internal resolution.
|
|
134
|
+
|
|
135
|
+
**On 16 GiB everything works.** Below 12 GiB, `continuity-setup` sets the default to
|
|
136
|
+
`quality="fast"` (u2netp): peak drops to 1.33 GiB and it runs in 0.6 s instead of 7.2 s. On a
|
|
137
|
+
typical game sprite the two are hard to tell apart by eye — checked side by side over a magenta
|
|
138
|
+
backdrop with the edges zoomed. `best` remains the default where there is room, because the
|
|
139
|
+
models do differ in principle on fine edges (hair, semi-transparent fringes), but treat `fast`
|
|
140
|
+
as a legitimate choice rather than a degraded fallback.
|
|
141
|
+
|
|
142
|
+
## One rule, not a tier list
|
|
143
|
+
|
|
144
|
+
Jobs are serialized, so **at any moment exactly one model is needed**. Everything else is
|
|
145
|
+
released before the job starts. That is the whole VRAM policy.
|
|
146
|
+
|
|
147
|
+
It buys a property worth more than a few saved seconds: **peak VRAM is a constant 6.80 GiB
|
|
148
|
+
regardless of what you call, in what order.** Measured over an alternating
|
|
149
|
+
speech→image→speech→image sequence:
|
|
150
|
+
|
|
151
|
+
| | peak | speech | image | 6 calls |
|
|
152
|
+
|---|---|---|---|---|
|
|
153
|
+
| keep models resident | **10.94 GiB** | 2.8 s avg | 11.5 s | 42.9 s |
|
|
154
|
+
| release what isn't needed | **6.79 GiB** | 4.8 s | 11.6 s | 49.2 s |
|
|
155
|
+
|
|
156
|
+
Keeping them resident is 16% faster and **does not fit an 8 GiB card** — and "voice a line,
|
|
157
|
+
then draw something" is the most ordinary sequence there is. An earlier version of this README
|
|
158
|
+
quoted 7.84 GiB for that overlap; that came from a lighter sequence I happened to test, and
|
|
159
|
+
using it as the ceiling was wrong. A cloned voice keeps its reference audio resident too, which
|
|
160
|
+
is where the rest comes from.
|
|
161
|
+
|
|
162
|
+
**What the reload actually costs:** 4.8 s instead of 1.2 s, and only on the first call after
|
|
163
|
+
switching away. Ten dialogue lines in a row pay it once:
|
|
164
|
+
|
|
165
|
+
```
|
|
166
|
+
第 1 句 4.63s 之后九句平均 1.19s 十句合计 15.4s
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
So there is no VRAM tier list, and no 12 GiB threshold. Above 8 GiB every card behaves
|
|
170
|
+
identically. Below 8 GiB the installer explains why image generation will not fit and asks
|
|
171
|
+
whether to install the audio half alone — it does not quietly substitute a different product:
|
|
172
|
+
|
|
173
|
+
```
|
|
174
|
+
生图 显存不足
|
|
175
|
+
Fake GTX 1060 只有 6.0 GiB, 而生图实测峰值 6.80 GiB, 需要 8 GiB。
|
|
176
|
+
换更小的生图模型省不下这部分 (Q4 与 Q8 峰值相同 6.60 / 6.59), 降分辨率也不行
|
|
177
|
+
—— 瓶颈是那个 8 GiB 不量化的文本编码器。
|
|
178
|
+
音频那半仍然可以装: 铸声/配音/音乐/音效/抠图都能用, 4 GiB 就够。
|
|
179
|
+
|
|
180
|
+
⚠️ 这张卡装不了生图那半。
|
|
181
|
+
只装音频那半 (铸声/配音/音乐/音效/抠图)? [y/N]
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
The audio-only install is a real product, not a consolation prize: casting voices, dialogue,
|
|
185
|
+
music, SFX and cutout all work in 4 GiB.
|
|
186
|
+
|
|
187
|
+
**What does not adapt at all: the image model.** Quantizing it does not move VRAM —
|
|
188
|
+
Q4_0 (2.29 GiB of weights) peaks at 6.60 GiB, Q8_0 (4.01 GiB) at 6.59 GiB, identical. Lowering
|
|
189
|
+
resolution does not help either (512 / 768 / 1024 all peak the same; only time changes). The
|
|
190
|
+
bottleneck is the **8 GiB unquantized 4B text encoder**. So there is no "medium" image tier to
|
|
191
|
+
offer, only installed or not. (Q4_0 ships anyway — same VRAM, 1.7 GiB less disk.)
|
|
192
|
+
|
|
193
|
+
Going below 8 GiB for images means changing the text encoder or the model family. That is
|
|
194
|
+
possible, but it moves identity pinning from native `ref_images` to IP-Adapter, which is
|
|
195
|
+
**not verified here** — and identity pinning is the whole point.
|
|
196
|
+
|
|
197
|
+
The one thing that does still key off a resource is host RAM, and it is a different resource:
|
|
198
|
+
below 12 GiB RAM the cutout default drops to `quality="fast"` (see above).
|
|
199
|
+
|
|
200
|
+
## Zero residency
|
|
201
|
+
|
|
202
|
+
Measured on an RX 7800 XT with nothing else on the card:
|
|
203
|
+
|
|
204
|
+
| | GPU |
|
|
205
|
+
|---|---|
|
|
206
|
+
| idle | **0.21 GiB** |
|
|
207
|
+
| during image generation | 6.80 GiB |
|
|
208
|
+
| 2 s after it finishes | **0.21 GiB** |
|
|
209
|
+
| during TTS | 2.39 GiB |
|
|
210
|
+
| 120 s after TTS | **0.21 GiB** |
|
|
211
|
+
|
|
212
|
+
Images are free: the engine streams weights per request and never keeps them resident.
|
|
213
|
+
Audio is released by an idle timer (`AUDIO_IDLE_UNLOAD_S`, default 120 s) — not immediately,
|
|
214
|
+
because someone voicing ten lines in a row should not pay a reload each time. Reload costs
|
|
215
|
+
nothing measurable: the same TTS request took 3.0 s both cold and warm, because weights are
|
|
216
|
+
mmap'd and sit in page cache.
|
|
217
|
+
|
|
218
|
+
Requests are serialized and everything unneeded is released first, so peak = the single largest
|
|
219
|
+
model, always. The idle timer covers the one case the rule cannot: after the *last* job there is
|
|
220
|
+
no next job to trigger a release, so the timer does it. Closing the agent releases the VRAM too —
|
|
221
|
+
the MCP server unloads on exit rather than leaving the engines holding it.
|
|
222
|
+
|
|
223
|
+
## Two things it actually does
|
|
224
|
+
|
|
225
|
+
**1. Identity survives across calls.** Generation backends are stateless: ask for the same
|
|
226
|
+
character twice and you get two people who merely resemble each other. Measured on
|
|
227
|
+
Qwen3-TTS, four lines from one voice description:
|
|
228
|
+
|
|
229
|
+
| | pitch spread across 4 lines |
|
|
230
|
+
|---|---|
|
|
231
|
+
| straight to the model (default sampling) | 125 Hz |
|
|
232
|
+
| straight to the model, **greedy decoding** | **242 Hz — worse** |
|
|
233
|
+
| through Continuity (pinned reference) | **5 Hz** |
|
|
234
|
+
|
|
235
|
+
Under greedy decoding the seed is provably inert — seeds 5 / 99 / 777 produced one identical
|
|
236
|
+
sha256 — so randomness was fully eliminated, and it still drifted 242 Hz. **Identity is a
|
|
237
|
+
function of the input text, not of the random draw.** `temperature=0` and `top_k=1` cannot fix
|
|
238
|
+
it. Only pinning to a reference artifact can.
|
|
239
|
+
|
|
240
|
+
```
|
|
241
|
+
create_actor(name, voice) -> audition clip; listen before you commit
|
|
242
|
+
actor_tts(actor, text) -> same timbre every line
|
|
243
|
+
|
|
244
|
+
create_character / create_animal / create_object (name, appearance)
|
|
245
|
+
subject_image(subject, scene) -> same look, new scene / angle / outfit
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Identity and wardrobe are separate: pin the face and build, then change clothes in the scene
|
|
249
|
+
prompt. A reference in an indigo robe, asked for `wearing heavy red armor`, comes back in
|
|
250
|
+
armor with the same face.
|
|
251
|
+
|
|
252
|
+
**Already cast your character somewhere else?** `import_actor` and `import_subject` pin an
|
|
253
|
+
artifact you supply — a real voice recording, an ElevenLabs clip, a character sheet from
|
|
254
|
+
another tool — and everything downstream behaves identically. Audio is normalized to 24 kHz
|
|
255
|
+
mono for you (44.1 kHz stereo in, verified: reference f0 identical, and an imported actor
|
|
256
|
+
tracks a natively-cast one to 11 Hz).
|
|
257
|
+
|
|
258
|
+
**2. Degenerate output is refused.** A backend that miscomputes returns a perfectly
|
|
259
|
+
well-formed all-zero WAV, or a flat grey PNG, with HTTP 200. Every artifact is checked
|
|
260
|
+
(image standard deviation, audio RMS, non-finite samples) and the call fails loudly rather
|
|
261
|
+
than reporting success over garbage. Cutouts additionally get a quality report — mostly
|
|
262
|
+
transparent, nothing removed, subject shattered into fragments, holes eaten through the
|
|
263
|
+
subject — each with a specific warning instead of a silent pass.
|
|
264
|
+
|
|
265
|
+
Plus `remove_bg`: diffusion models draw "transparent background" as an opaque checkerboard;
|
|
266
|
+
this turns it into a real RGBA cutout, which sprites require. And `gen_sfx`, which synthesizes
|
|
267
|
+
sfxr-style game SFX procedurally — bit-identical for a given seed, milliseconds, no GPU —
|
|
268
|
+
because a diffusion model is the wrong instrument for a 40 ms coin pickup.
|
|
269
|
+
|
|
270
|
+
## Tools
|
|
271
|
+
|
|
272
|
+
19 tools. Everything returns **absolute local file paths**, not URLs — the agent and the
|
|
273
|
+
engines are on the same machine, so a path can go straight into your game project without a
|
|
274
|
+
download step, and there is no file server to run or misconfigure.
|
|
275
|
+
|
|
276
|
+
| | |
|
|
277
|
+
|---|---|
|
|
278
|
+
| voice | `create_actor` `import_actor` `actor_tts` `list_actors` `delete_actor` `generate_speech` |
|
|
279
|
+
| look | `create_character` `create_animal` `create_object` `import_subject` `subject_image` `list_subjects` `delete_subject` `generate_image` |
|
|
280
|
+
| audio | `generate_music` `gen_sfx` |
|
|
281
|
+
| post | `remove_bg` `slice_sheet` |
|
|
282
|
+
| meta | `continuity_status` |
|
|
283
|
+
|
|
284
|
+
`generate_image` and `generate_speech` exist for one-offs and say so in their own descriptions:
|
|
285
|
+
they explicitly tell the agent that what they produce will not come back on the next call, and
|
|
286
|
+
point at the pinning tools for anything recurring.
|
|
287
|
+
|
|
288
|
+
## Limits, and why each one exists
|
|
289
|
+
|
|
290
|
+
Every number here is a measured failure boundary, not a policy.
|
|
291
|
+
|
|
292
|
+
| limit | value | what happens past it |
|
|
293
|
+
|---|---|---|
|
|
294
|
+
| line length | 200 chars | 600 chars wedged the GPU: `amdgpu GPU reset(6)`, device lost, an unrelated process on the *other* card killed. 200 is half the largest known-safe value. |
|
|
295
|
+
| reference audio | 15 s | ~0.19 GiB VRAM per second: 15 s → 6.59 GiB, 30 s → 9.04 GiB. 15 s is the last value that stays under the image peak, so voice never becomes the ceiling. One value for every card — 3–10 s is already enough to pin a timbre, so a bigger cap on bigger cards would only mean "this clip imports on my machine and not on yours". |
|
|
296
|
+
| casting script | 45 chars | It produces the reference audio, which is then re-read on every later line. Char count is a bad proxy (60 chars measured 19.1 s, not the 13.7 s the ratio predicts), so the real duration is checked after casting and reported. |
|
|
297
|
+
| image size | 1024 px | 1280 pushed VRAM to 14.5/16.4 GiB; 2048 sent the driver into `restore_userptr_worker` thrashing with the process stuck in uninterruptible `D` state — worse than a clean OOM. |
|
|
298
|
+
| music length | 120 s | Not a safety limit: the engine silently truncates at 120 s and reports success. The limit turns that into an explicit `clamped` field. |
|
|
299
|
+
|
|
300
|
+
Imported audio below 24 kHz is accepted but flagged: upsampling cannot restore the octave
|
|
301
|
+
that was thrown away, so the clone comes out duller than the file you gave it. That is worth
|
|
302
|
+
a warning rather than a silent pass — it is the same failure shape as everything else this
|
|
303
|
+
plugin exists to catch.
|
|
304
|
+
|
|
305
|
+
**Oversized inputs are handled differently by type, on purpose.** An image that is too large is
|
|
306
|
+
resized and the result is reported back to you (`原图 2400x1600 → 存为 1024x682`) — a scaled
|
|
307
|
+
picture still depicts the same thing. Reference audio that is too long is **rejected, not
|
|
308
|
+
trimmed**: cutting the tail off the audio would leave the transcript describing something the
|
|
309
|
+
audio no longer says, and that alignment is exactly what the cloning depends on. Trimming it
|
|
310
|
+
silently would hand you an actor that imported successfully and sounds like someone else.
|
|
311
|
+
|
|
312
|
+
## Bring your own backend (optional)
|
|
313
|
+
|
|
314
|
+
Local engines are the default, but every backend is a URL (`SD_SERVER`, `AUDIO_SERVER`). Point
|
|
315
|
+
them at your own server and the local models are never loaded. One constraint if you do: the
|
|
316
|
+
audio engine resolves the reference-audio path itself, so it must see the same actors
|
|
317
|
+
directory (same machine, or a shared mount).
|
|
318
|
+
|
|
319
|
+
The image backend **must accept a reference image** (FLUX.2-style native `ref_images`,
|
|
320
|
+
IP-Adapter, or PuLID for faces). Without it, identity pinning cannot work — and the plugin
|
|
321
|
+
says so instead of silently degrading.
|
|
322
|
+
|
|
323
|
+
## Prior art
|
|
324
|
+
|
|
325
|
+
A survey of the current MCP ecosystem — MiniMax-MCP, openrouter-mcp-multimodal, AtlasCloud,
|
|
326
|
+
the dsh vision/draw plugins, and four game-asset servers — found voice cloning in several,
|
|
327
|
+
**visual subject pinning in none, and output verification in none**.
|
|
328
|
+
|
|
329
|
+
## Layout
|
|
330
|
+
|
|
331
|
+
```
|
|
332
|
+
bundle/ dsh bundle (npm) — one plugin row; dsh spawns and supervises the MCP server
|
|
333
|
+
src/ the MCP server: pinning, guardrails, verification, cutout, VRAM lifecycle
|
|
334
|
+
src/continuity_mcp/deploy/ compose + engine Dockerfile + weight manifest
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
## License
|
|
338
|
+
|
|
339
|
+
MIT
|