Skip to content

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.

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 with is_fallback metadata and never billed. Set allow_fallback=false to surface the ProviderError instead.
  • 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 registry credentials.secret_ref, resolved verbatim at runtime via ctx.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_URL and DNA_ML_SERVICE_URL (env-var names; values injected per environment).
  • core-dna-api — a thin HTTP facade (/dna/*, built from Dockerfile.api) that serves the earlier plans (4–5). It has ingress but is internal-only (enable_ingress:true + is_external:false), reached with INTERNAL_RPC_SECRET from the saas-2.0 BFF / monolith proxy — never exposed to the browser. Locally it’s published on a fixed port for a curl …/healthz check.

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:

  1. 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_enforce fires ctx.convert_file best-effort — trying the cheapest fix first (reuse an existing smaller rendition such as download_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).
  2. Explicitly, as MediaConvertTool (media-convert → convert_file) in core-tool-media, so the agent / AI Tools panel can convert a file on demand.