Other tool workers (stitch, media, gpu, dna, conversion)
Beyond core-tool-sansaarm (song gen) and
core-tool-agent (the LLM orchestrator), the fleet
has a handful of smaller workers that each generate one class of asset. They all share the
same shape — a WorkerFleet process with no HTTP ingress that polls the task queue,
runs a model, uploads results, and reports usage — so if you’ve read
Add a tool worker and
the soundverse-py SDK, you already know 90% of each one.
This page is the field guide to what’s different: what each worker generates, which
provider it calls, and whether it’s actually live.
At a glance
Section titled “At a glance”| Worker (repo) | Shape | Registered tools (model → operation) |
Model / provider | Deploy |
|---|---|---|---|---|
| core-tool-stitch | queue worker | stitch-v1 → stitch |
in-process compositor (no external model) | In code |
| core-tool-media | queue worker | album-art → generate_album_art, prompt-to-image → generate_image, elevenlabs-sfx-v1 → sfx_gen, elevenlabs-tts-v1 → generate_speech, media-convert → convert_file |
Replicate + ElevenLabs | In code |
| core-tool-gpu | queue worker | htdemucs_6s → separate_stems |
Demucs (torch), self-hosted | Staging |
| core-tool-dna | worker + internal HTTP facade | DNA audio pipeline (Plans 3–8) | DNA monolith + ML service | Staging |
| core-conversion | gRPC service (has ingress) | ConversionService.Convert |
ffmpeg + Pillow, self-hosted | Staging |
Each model → operation pair is the tool’s registry id. Pricing is upserted from the
tool class on RegisterTool, so every tool here declares a LicensePricing row for all
five licenses — an unpriced tool fails every call with NotFound. See
Add pricing to a tool.
core-tool-stitch — media composition In code
Section titled “core-tool-stitch — media composition ”The stitching worker takes a Timeline IR (asset URLs + a timeline of cuts/overlays)
and renders a single combined media file. process() runs
download → probe → render → build manifest: it emits two outputs — the rendered file
(uploaded via ctx.upload_file) and a studioManifest, so the chat card and the Studio
editor both read one manifest, two views. There is no external model — it’s an
in-process ffmpeg-based compositor.
Pricing is COST_MODE_DYNAMIC across all five licenses. Stitch can’t use a
usage_field — render cost is a function of the whole timeline, not one bounded input
field — so the reserve is cost_base only, and settlement charges per unit of reported
complexity. The two knobs (_RESERVE_FLOOR_CREDITS, _CREDITS_PER_COMPLEXITY) are
placeholder values pending product sign-off; don’t treat them as final.
core-tool-media — album art, images, SFX, speech, convert In code
Section titled “core-tool-media — album art, images, SFX, speech, convert ”One worker hosting five media tools. album-art originally lived in core-tool-agent and
was moved here unchanged — same model/operation, so it keeps the same registry
tool_id (redeploy agent + media together to preserve that continuity).
| Tool | model → operation |
Provider | Billing |
|---|---|---|---|
| Album art | album-art → generate_album_art |
Replicate black-forest-labs/flux-schnell |
free (cost_base=0), curated fallback pool |
| Prompt → image | prompt-to-image → generate_image |
Replicate google/nano-banana-pro |
per image |
| Sound effects | elevenlabs-sfx-v1 → sfx_gen |
ElevenLabs text-to-sound-effects | per second (usage_field="duration_seconds") |
| AI speech / TTS | elevenlabs-tts-v1 → generate_speech |
ElevenLabs TTS (eleven_multilingual_v2) |
per character (usage_field="char_count") |
| Media convert | media-convert → convert_file |
delegates to core-conversion | per file |
Notes worth knowing:
- Images never touch the worker. For album art and prompt-to-image,
ctx.upload_file(url=…)hands the provider URL to core-storage, which fetches, dedupes, and re-hosts it — the worker only sees a Soundverse asset URL back. - Album art degrades gracefully. On provider failure (default
allow_fallback=true) it returns a cover from a curated fallback pool, uploaded withis_fallbackmetadata and never billed. Setallow_fallback=falseto surface theProviderErrorinstead. - SFX and TTS are audio-final. If ElevenLabs fails, the task fails and core-mcp refunds the reserve — there’s no fallback for a paid deliverable.
- Credentials. Provider keys (
REPLICATE_API_TOKEN, the ElevenLabs key) are injected by name and also carried on the tool’s registrycredentials.secret_ref, resolved verbatim at runtime viactx.resolve_credentials().
core-tool-gpu — stem separation Staging
Section titled “core-tool-gpu — stem separation ”Splits a track into its component stems (vocals / drums / bass / other / guitar / piano)
using Demucs htdemucs_6s (torch). It registers htdemucs_6s → separate_stems and
uploads each stem imperatively with ctx.upload_file(role=<stem>), so the agent chat can
group them into one compact list keyed on role
rather than emitting six full song cards. Duration is derived from the uploaded audio, not
an agent input, so usage_field="" and billing is per second of input (cost_base
reserve floor + cost_increment per second — both placeholder values today).
core-tool-dna — the DNA pipeline Staging
Section titled “core-tool-dna — the DNA pipeline ”DNA is unusual: one repo, two deployables, both built from the same source.
- core-tool-dna — an ingress-less queue worker running the DNA audio pipeline
(generation-by-HTTP; Plans 6–7). It reaches its backends through
DNA_MONOLITH_BASE_URLandDNA_ML_SERVICE_URL(env-var names; values injected per environment). - core-dna-api — a thin HTTP facade (
/dna/*, built fromDockerfile.api) that serves the earlier plans (4–5). It has ingress but is internal-only (enable_ingress:true+is_external:false), reached withINTERNAL_RPC_SECRETfrom the saas-2.0 BFF / monolith proxy — never exposed to the browser. Locally it’s published on a fixed port for acurl …/healthzcheck.
Both are deployed to staging (bumped into the super-repo as the “deployed DNA service”).
Because the two share a repo, the deploy script runs two workflows against it —
deploy-aca-staging.yml (worker) and deploy-aca-api-staging.yml (facade).
core-conversion — the media-conversion service Staging
Section titled “core-conversion — the media-conversion service ”A gRPC service (scaffolded from core-storage) that does many-input → many-output media
conversion, v1 = audio + image, via ffmpeg + Pillow. It’s blob-ref in / blob-ref out
(no bytes cross the wire), content-dedups its renditions the same way core-storage does
(sha256(content:container)), and can either return a variant or persist a derived file
(DERIVED_FROM lineage). It’s reached two ways:
- Automatically, as an input-enforcement seam. When a tool receives a file input that
is the wrong format or over its size cap, soundverse-py’s
_convert_and_enforcefiresctx.convert_filebest-effort — trying the cheapest fix first (reuse an existing smaller rendition such asdownload_m4a, else re-encode) — then rebinds the input’s blob hash / mime / size and re-checks. If core-conversion is unreachable it no-ops and falls back to rejecting the input, so the seam never hard-fails a tool. This is what lets sansaarm’s reference inputs accept large or wrong-format uploads (its inputs carry a 15 MB cap that makes the seam engage). - Explicitly, as
MediaConvertTool(media-convert → convert_file) in core-tool-media, so the agent / AI Tools panel can convert a file on demand.
Related
Section titled “Related”- Add a tool worker — the shape every worker here shares
- The soundverse-py SDK —
WorkerFleet+TaskContext - Add pricing to a tool — why every tool declares a pricing row
- Storage & media plane — dedup, SAS, and renditions
- core-mcp — restart it after a tool’s schema changes
- Tool catalog — the full list of registered tools