@kolbo/mcp 1.71.4 → 1.72.2
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.
- package/README.md +3 -0
- package/package.json +1 -1
- package/skill/GENERATED.md +1 -1
- package/skill/SKILL.md +4 -0
- package/skill/references/workflows/audio-stems.md +81 -0
- package/src/index.js +2 -0
- package/src/toolAnnotations.js +1 -0
- package/src/tools/_shared.js +2 -1
- package/src/tools/audio_stems.js +159 -0
package/README.md
CHANGED
|
@@ -132,6 +132,9 @@ Without the optional skill, the config block alone already exposes every tool
|
|
|
132
132
|
| `generate_sound` | Text → sound effect. Duration, prompt influence, and per-provider controls (Stable Audio guidance, Kie loop/tempo/key, Seed-Audio voice/speed/volume/pitch + reference audio/image) |
|
|
133
133
|
| `generate_3d` | Text or reference images → 3D model (GLB/FBX/OBJ/USDZ) |
|
|
134
134
|
| `transcribe_audio` | Audio/video URL or file → text + SRT subtitles. Language, speaker diarization, audio-event tagging, and SRT formatting (words/line, lines/subtitle, caption stretch) |
|
|
135
|
+
| `separate_audio_stems` | Audio/video URL (or a Kolbo `generation_id`) → Dialogue / Music / Effects / without-dialogue (M&E) layers. Kolbo's own masking pipeline with a speech classifier on top, so a centred engine or ambience is not handed back mislabelled as dialogue. 5 credits, runs inline |
|
|
136
|
+
| `clean_dialogue_leftovers` | Strip voices still faintly audible in an M&E layer. 17 credits — escalation only, it trades bed fidelity to remove the leak |
|
|
137
|
+
| `separate_ambience` | Pull room tone / atmosphere out of the Effects (or Music) bed as its own layer. 17 credits |
|
|
135
138
|
|
|
136
139
|
Every image/video/creative-director tool accepts `visual_dna_ids` and `moodboard_id` for character/style consistency across outputs — you can compose `create_visual_dna` → `generate_image` (with the DNA applied server-side) in a single agent turn. `generate_creative_director` also accepts `moodboard_ids` plural for blending.
|
|
137
140
|
|
package/package.json
CHANGED
package/skill/GENERATED.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# AUTO-GENERATED — do not edit
|
|
2
2
|
|
|
3
|
-
This tree is mirrored from kolbo-code@
|
|
3
|
+
This tree is mirrored from kolbo-code@94bfcae, the single source of truth.
|
|
4
4
|
Canonical source: packages/opencode/skills/kolbo/
|
|
5
5
|
Distribution: .github/workflows/sync-skill-to-plugin.yml
|
|
6
6
|
|
package/skill/SKILL.md
CHANGED
|
@@ -76,6 +76,7 @@ For multi-scene / batch work this pairs with `generate_creative_director` (see b
|
|
|
76
76
|
| Use **Visual DNA** / character consistency / `@name` syntax | `references/workflows/visual-dna.md` |
|
|
77
77
|
| Start or continue a **multi-step production** (storyboard → scenes → final cut) | `references/workflows/production-log.md` |
|
|
78
78
|
| **Transcribe** or **analyze** audio/video | `references/workflows/transcription.md` |
|
|
79
|
+
| **Split a soundtrack into layers** — remove/isolate speech, strip narration, instrumental bed, stems for dubbing | `references/workflows/audio-stems.md` |
|
|
79
80
|
| **Scrape brand/product info** before generating + persist as `.kolbo/brand-kits/<slug>.md` | `references/workflows/research-first.md` |
|
|
80
81
|
| Browse, manage, or present existing **media library** items | `references/workflows/media-library.md` |
|
|
81
82
|
| Confirm **cost** or validate **resolution / aspect / duration** against model caps | `references/workflows/cost-and-validation.md` |
|
|
@@ -101,6 +102,9 @@ Each `references/models/*.md` mirrors the matching skill prompt in `kolbo-api/sr
|
|
|
101
102
|
| `generate_speech` | TTS. Use `list_voices` to pick a voice. |
|
|
102
103
|
| `generate_sound` | Sound effects. |
|
|
103
104
|
| `generate_3d` | 3D models from text / single image / multi-view. Returns GLB/FBX/OBJ/USDZ. |
|
|
105
|
+
| `separate_audio_stems` | Split a soundtrack into Dialogue / Music / Effects / without-dialogue (M&E). The route for removing or isolating speech, instrumental beds, and stems for dubbing. 5cr, inline. See `workflows/audio-stems.md`. |
|
|
106
|
+
| `clean_dialogue_leftovers` | Strip voices still faintly audible in an M&E layer. 17cr — only when the user reports the leak, it trades fidelity. |
|
|
107
|
+
| `separate_ambience` | Pull room tone out of the Effects bed as its own lane. 17cr. |
|
|
104
108
|
|
|
105
109
|
### Discovery, Library, Visual DNA, Moodboards, Chat, Publishing
|
|
106
110
|
| Tool | Purpose |
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Audio Stem Separation — splitting a soundtrack into layers
|
|
2
|
+
|
|
3
|
+
Load this file when the user wants to **pull a soundtrack apart**: remove or isolate speech,
|
|
4
|
+
strip narration, mute the music, get a clean instrumental / M&E bed, prepare a track for
|
|
5
|
+
dubbing or localisation, clean up a podcast, or hand an editor stems.
|
|
6
|
+
|
|
7
|
+
This is Kolbo's own pipeline, not a vendor passthrough. It is a **masking** separation — the
|
|
8
|
+
layers sum back to the original at −30 to −37 dB, so nothing is resynthesised — with a speech
|
|
9
|
+
classifier layered on top. That classifier is the part that matters: a "vocals" mask is not
|
|
10
|
+
the same thing as dialogue. Engines, impacts and centred ambience land in it at full volume,
|
|
11
|
+
and a plain vocal remover hands those back labelled as speech. When no words are actually
|
|
12
|
+
spoken, Kolbo folds that mask into Effects instead.
|
|
13
|
+
|
|
14
|
+
## Decision tree
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
"Remove the vocals / speech / narration" → separate_audio_stems, hand back the `me` layer
|
|
18
|
+
"Isolate just the dialogue" → separate_audio_stems, hand back the `dialogue` layer
|
|
19
|
+
"Get me the instrumental / music bed" → separate_audio_stems, hand back the `music` layer
|
|
20
|
+
"Give the editor stems" / dubbing / localisation → separate_audio_stems, hand back ALL layers
|
|
21
|
+
"I can still hear voices in the clean track" → clean_dialogue_leftovers on the `me` URL
|
|
22
|
+
"I want the room tone / atmosphere on its own" → separate_ambience on the `sfx` URL
|
|
23
|
+
Just want the words as text, not the audio → transcribe_audio (references/workflows/transcription.md)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Never reach for `edit_video({ operation: "generate_audio" })` for any of these — that ADDS a
|
|
27
|
+
generated audio layer, it does not take one away.
|
|
28
|
+
|
|
29
|
+
## The layers
|
|
30
|
+
|
|
31
|
+
`separate_audio_stems` returns whichever of these the material actually contains:
|
|
32
|
+
|
|
33
|
+
| type | What it is |
|
|
34
|
+
|---|---|
|
|
35
|
+
| `dialogue` | Speech only. Absent when the clip has no spoken words. |
|
|
36
|
+
| `music` | Score / song bed. Absent when the clip has no music. |
|
|
37
|
+
| `sfx` | Effects and Foley. |
|
|
38
|
+
| `me` | Everything except dialogue ("M&E") — the track you dub over. |
|
|
39
|
+
| `original` | The untouched mix, for reference. |
|
|
40
|
+
|
|
41
|
+
A missing lane is a real finding, not an error: a talking-head clip with no score genuinely
|
|
42
|
+
has no Music layer, and shipping an empty row plus an `sfx` byte-identical to `me` would be
|
|
43
|
+
two lies instead of one honest pair. Say what came back rather than implying a lane failed.
|
|
44
|
+
|
|
45
|
+
## Two inputs, one tool
|
|
46
|
+
|
|
47
|
+
- **`audio_url`** — any public media URL, audio or video: an upload, a Kolbo generation, a
|
|
48
|
+
direct link. Stateless: nothing is stored against it, there is **no cache**, so calling
|
|
49
|
+
twice on the same file separates twice and bills twice. Sources up to **15 minutes**; trim
|
|
50
|
+
longer files (`trim_video`, or ffmpeg locally) and separate each section.
|
|
51
|
+
- **`generation_id`** — a Kolbo video you already generated. The layers are saved onto that
|
|
52
|
+
generation, so a repeat call returns them free and the web app's mixer sees the same thing.
|
|
53
|
+
|
|
54
|
+
Prefer `generation_id` whenever the source IS a Kolbo generation — it is cacheable and it
|
|
55
|
+
keeps the API and the web app in agreement.
|
|
56
|
+
|
|
57
|
+
## Cost and when NOT to escalate
|
|
58
|
+
|
|
59
|
+
| Tool | Credits | When |
|
|
60
|
+
|---|---|---|
|
|
61
|
+
| `separate_audio_stems` | 5 | Always the first call. |
|
|
62
|
+
| `clean_dialogue_leftovers` | 17 | ONLY when the user actually hears voice bleeding through `me`. |
|
|
63
|
+
| `separate_ambience` | 17 | ONLY when the user wants room tone as its own lane. |
|
|
64
|
+
|
|
65
|
+
`clean_dialogue_leftovers` is deliberately not automatic. It trades fidelity to do its job —
|
|
66
|
+
the model that removes the leak reconstructs the bed less cleanly (~−12 dB) than the masking
|
|
67
|
+
model that produced it (~−30 dB) — so running it by reflex makes the common case worse to fix
|
|
68
|
+
a rare one. Typical on dense crowd scenes; unnecessary on clean dialogue. Ask, or wait for the
|
|
69
|
+
user to report the leak. Both 17-credit tools bill even when the analysis finds nothing to
|
|
70
|
+
remove (the pass still runs); the response reports `already_clean` / `skipped` when that
|
|
71
|
+
happens, so say so rather than presenting a no-op as a result.
|
|
72
|
+
|
|
73
|
+
## Practical notes
|
|
74
|
+
|
|
75
|
+
- These tools run **inline** — no job id, nothing to poll. Each call holds for 20–90 seconds
|
|
76
|
+
on a typical clip. Don't call `get_generation_status` on the result.
|
|
77
|
+
- Layer URLs are WAV on the Kolbo CDN. Hand the user the URLs directly.
|
|
78
|
+
- A file whose audio track is digital silence is refused **before** any charge (`NO_AUDIO`).
|
|
79
|
+
That is the system working, not a failure to retry.
|
|
80
|
+
- Local file on the user's machine? Same rule as everywhere else — `upload_media` /
|
|
81
|
+
`media_upload_widget` / `create_upload_ticket` first, then pass the returned https:// URL.
|
package/src/index.js
CHANGED
|
@@ -76,6 +76,7 @@ const { registerReviewTools } = require('./tools/review');
|
|
|
76
76
|
const { registerVoiceTools } = require('./tools/voices');
|
|
77
77
|
const { registerMusicLibraryTools } = require('./tools/music_library');
|
|
78
78
|
const { registerStockLibraryTools } = require('./tools/stock_library');
|
|
79
|
+
const { registerAudioStemTools } = require('./tools/audio_stems');
|
|
79
80
|
const { registerApps, attachToolWidgetMeta } = require('./apps');
|
|
80
81
|
const { attachToolAnnotations } = require('./toolAnnotations');
|
|
81
82
|
|
|
@@ -162,6 +163,7 @@ function createServer(opts = {}) {
|
|
|
162
163
|
registerReviewTools(server, client, toolOptions);
|
|
163
164
|
registerMusicLibraryTools(server, client, toolOptions);
|
|
164
165
|
registerStockLibraryTools(server, client, toolOptions);
|
|
166
|
+
registerAudioStemTools(server, client, toolOptions);
|
|
165
167
|
|
|
166
168
|
// MCP Apps widget resources (ui://kolbo/*). Registering resources is inert
|
|
167
169
|
// for text-only hosts — they never fetch them.
|
package/src/toolAnnotations.js
CHANGED
|
@@ -66,6 +66,7 @@ const DESTRUCTIVE_WRITE = [
|
|
|
66
66
|
'edit_image', 'edit_video', 'trim_video', 'clone_voice',
|
|
67
67
|
'chat_send_message', 'generate_character_sheet',
|
|
68
68
|
'acquire_clean_music_track', 'import_music_track_to_library',
|
|
69
|
+
'separate_audio_stems', 'clean_dialogue_leftovers', 'separate_ambience',
|
|
69
70
|
|
|
70
71
|
// Deletes and whole-value replacement updates are conservatively destructive.
|
|
71
72
|
'delete_voice', 'delete_visual_dna', 'delete_visual_dna_folder',
|
package/src/tools/_shared.js
CHANGED
|
@@ -54,7 +54,8 @@ const FILE_INPUT_TOOLS = [
|
|
|
54
54
|
'generate_elements', 'generate_first_last_frame', 'generate_lipsync',
|
|
55
55
|
'generate_3d', 'edit_image', 'edit_video', 'transcribe_audio',
|
|
56
56
|
'create_visual_dna', 'generate_character_sheet', 'clone_voice',
|
|
57
|
-
'chat_send_message', 'create_moodboard'
|
|
57
|
+
'chat_send_message', 'create_moodboard',
|
|
58
|
+
'separate_audio_stems', 'clean_dialogue_leftovers', 'separate_ambience'
|
|
58
59
|
];
|
|
59
60
|
|
|
60
61
|
const REMOTE_FILE_HINT =
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/* ⛔ BACKWARD COMPATIBILITY: Tool names and arg names below are a PUBLIC
|
|
2
|
+
* CONTRACT. Never rename, remove, or break an existing tool/arg. Full rules: ../index.js top-of-file.
|
|
3
|
+
*
|
|
4
|
+
* Audio stem separation — Kolbo's own pipeline, not a passthrough to one vendor.
|
|
5
|
+
*
|
|
6
|
+
* Demucs supplies the masks (its stems sum back to the original at −30 to −37 dB, so nothing
|
|
7
|
+
* is resynthesised) and a speech classifier supplies the semantic gate Demucs lacks: a
|
|
8
|
+
* "vocals" mask is NOT the same thing as dialogue, because engines, impacts and centred
|
|
9
|
+
* ambience land there at full volume. When no words are actually spoken, that mask is folded
|
|
10
|
+
* back into Effects rather than handed over mislabeled as Dialogue. That gate is why these
|
|
11
|
+
* tools exist as their own surface instead of a `separate` operation on edit_video.
|
|
12
|
+
*
|
|
13
|
+
* These three routes answer INLINE — there is no job id and nothing to poll. Each call holds
|
|
14
|
+
* its connection for the whole run, so they carry an explicit long request timeout rather
|
|
15
|
+
* than the client default.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const { z } = require('zod');
|
|
19
|
+
const { projectIdField } = require('./_shared');
|
|
20
|
+
|
|
21
|
+
// The API answers inline and its own socket ceiling is 10 minutes. Sit just above that so a
|
|
22
|
+
// server-side failure surfaces as the server's message instead of a client-side abort with
|
|
23
|
+
// no explanation. Matches the pattern used for other long single-request tools.
|
|
24
|
+
const STEMS_TIMEOUT_MS = 11 * 60 * 1000;
|
|
25
|
+
|
|
26
|
+
const SOURCE_HINT =
|
|
27
|
+
'Either a public media URL (audio or video — a Kolbo generation, an upload_media result, '
|
|
28
|
+
+ 'or any direct link) OR `generation_id` of a Kolbo video you already generated. '
|
|
29
|
+
+ 'Pass exactly one.';
|
|
30
|
+
|
|
31
|
+
function summarize(result) {
|
|
32
|
+
return {
|
|
33
|
+
layers: (result.layers || []).map((l) => ({ type: l.type, label: l.label, url: l.url })),
|
|
34
|
+
...(result.generation_id ? { generation_id: result.generation_id } : {}),
|
|
35
|
+
...(typeof result.credits_used === 'number' ? { credits_used: result.credits_used } : {}),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function textResult(payload) {
|
|
40
|
+
return { content: [{ type: 'text', text: JSON.stringify(payload, null, 2) }] };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** One source, two accepted shapes. Reject "both" rather than silently preferring one. */
|
|
44
|
+
function buildBody({ audio_url, generation_id, project_id }, label) {
|
|
45
|
+
if (!audio_url && !generation_id) {
|
|
46
|
+
throw new Error(`${label}: provide audio_url (a media URL) or generation_id (a Kolbo video).`);
|
|
47
|
+
}
|
|
48
|
+
if (audio_url && generation_id) {
|
|
49
|
+
throw new Error(`${label}: pass audio_url OR generation_id, not both — they are different sources and the result would be ambiguous.`);
|
|
50
|
+
}
|
|
51
|
+
const body = generation_id ? { generation_id } : { audio_url };
|
|
52
|
+
if (project_id) body.project_id = project_id;
|
|
53
|
+
return body;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function registerAudioStemTools(server, client) {
|
|
57
|
+
// ─── separate_audio_stems ─────────────────────────────────────
|
|
58
|
+
server.tool(
|
|
59
|
+
'separate_audio_stems',
|
|
60
|
+
'Split a soundtrack into separate audio layers: Dialogue, Music, Effects, and a '
|
|
61
|
+
+ '"without dialogue" (M&E) track — Kolbo\'s own separation pipeline, built for dubbing, '
|
|
62
|
+
+ 'localisation, podcast cleanup and re-scoring. Use this whenever someone wants to '
|
|
63
|
+
+ 'remove/isolate speech, strip narration, mute the music, get a clean instrumental bed, '
|
|
64
|
+
+ 'or hand an editor stems. A speech classifier decides whether the vocal mask is really '
|
|
65
|
+
+ 'dialogue, so an engine roar or centred ambience is not mislabelled as speech — that is '
|
|
66
|
+
+ 'what makes this better than a plain vocal remover. Costs 5 credits. Runs inline and '
|
|
67
|
+
+ 'returns the finished layer URLs (usually 20–90 seconds); there is nothing to poll. '
|
|
68
|
+
+ 'Sources up to 15 minutes long — trim longer files first. NOTE: there is no cache on the '
|
|
69
|
+
+ 'URL form, so calling it twice on the same file separates twice and bills twice.',
|
|
70
|
+
{
|
|
71
|
+
audio_url: z.string().optional()
|
|
72
|
+
.describe('Public URL of the audio or video to separate. ' + SOURCE_HINT),
|
|
73
|
+
generation_id: z.string().optional()
|
|
74
|
+
.describe('ID of a Kolbo video generation to separate instead of a URL. Layers are also saved onto that generation, so a repeat call is free.'),
|
|
75
|
+
project_id: projectIdField,
|
|
76
|
+
},
|
|
77
|
+
async ({ audio_url, generation_id, project_id }) => {
|
|
78
|
+
const body = buildBody({ audio_url, generation_id, project_id }, 'separate_audio_stems');
|
|
79
|
+
const result = await client.post('/v1/audio/separate', body, { timeoutMs: STEMS_TIMEOUT_MS });
|
|
80
|
+
return textResult({
|
|
81
|
+
...summarize(result),
|
|
82
|
+
has_dialogue: result.has_dialogue,
|
|
83
|
+
has_music: result.has_music,
|
|
84
|
+
cached: result.cached || false,
|
|
85
|
+
// The lane names are not self-explanatory to a caller seeing them for the first time.
|
|
86
|
+
layer_guide: {
|
|
87
|
+
dialogue: 'Speech only.',
|
|
88
|
+
music: 'Score / song bed. Absent when the clip has no music.',
|
|
89
|
+
sfx: 'Effects and Foley.',
|
|
90
|
+
me: 'Everything except dialogue (M&E) — the track to dub over.',
|
|
91
|
+
original: 'The untouched mix, for reference.',
|
|
92
|
+
},
|
|
93
|
+
next_steps: 'If voices are still faintly audible in the "me" layer, run clean_dialogue_leftovers on its URL. To pull room tone out of the Effects bed, run separate_ambience on it.',
|
|
94
|
+
});
|
|
95
|
+
},
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
// ─── clean_dialogue_leftovers ─────────────────────────────────
|
|
99
|
+
server.tool(
|
|
100
|
+
'clean_dialogue_leftovers',
|
|
101
|
+
'Strip voices that are still faintly audible in a "without dialogue" (M&E) track produced '
|
|
102
|
+
+ 'by separate_audio_stems. Use it only when the user actually hears speech bleeding '
|
|
103
|
+
+ 'through — it is not part of the normal flow, and it trades fidelity to do its job '
|
|
104
|
+
+ '(the generative model that removes the leak reconstructs the bed less cleanly than the '
|
|
105
|
+
+ 'masking model that made it). Typical on dense crowd scenes; unnecessary on clean '
|
|
106
|
+
+ 'dialogue. Costs 17 credits, charged even when the track turns out to be already clean '
|
|
107
|
+
+ '(the analysis pass still runs) — the response says which happened. Runs inline.',
|
|
108
|
+
{
|
|
109
|
+
audio_url: z.string().optional()
|
|
110
|
+
.describe('URL of the "me" / without-dialogue layer returned by separate_audio_stems.'),
|
|
111
|
+
generation_id: z.string().optional()
|
|
112
|
+
.describe('ID of a Kolbo video whose stems were already separated — cleans that generation\'s M&E layer in place.'),
|
|
113
|
+
project_id: projectIdField,
|
|
114
|
+
},
|
|
115
|
+
async ({ audio_url, generation_id, project_id }) => {
|
|
116
|
+
const body = buildBody({ audio_url, generation_id, project_id }, 'clean_dialogue_leftovers');
|
|
117
|
+
const result = await client.post('/v1/audio/clean-dialogue', body, { timeoutMs: STEMS_TIMEOUT_MS });
|
|
118
|
+
return textResult({
|
|
119
|
+
...summarize(result),
|
|
120
|
+
...(result.passes !== undefined && { cleanup_passes: result.passes }),
|
|
121
|
+
...(result.already_clean !== undefined && { already_clean: result.already_clean }),
|
|
122
|
+
cached: result.cached || false,
|
|
123
|
+
});
|
|
124
|
+
},
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
// ─── separate_ambience ────────────────────────────────────────
|
|
128
|
+
server.tool(
|
|
129
|
+
'separate_ambience',
|
|
130
|
+
'Pull room tone / atmosphere out of an Effects or Music bed, leaving the hard effects '
|
|
131
|
+
+ 'behind — a fourth lane on top of separate_audio_stems, for when someone wants the '
|
|
132
|
+
+ 'ambience of a location on its own, or wants it gone. Run separate_audio_stems first '
|
|
133
|
+
+ 'and pass the resulting "sfx" (or "music") layer URL. Costs 17 credits, charged even '
|
|
134
|
+
+ 'when the bed turns out to have no separable ambience — the response reports '
|
|
135
|
+
+ 'skipped: true in that case. Runs inline.',
|
|
136
|
+
{
|
|
137
|
+
audio_url: z.string().optional()
|
|
138
|
+
.describe('URL of the Effects ("sfx") or Music layer returned by separate_audio_stems.'),
|
|
139
|
+
source_type: z.enum(['sfx', 'music', 'me']).optional()
|
|
140
|
+
.describe('Which lane audio_url came from, so the leftover is labelled correctly. Default: "sfx".'),
|
|
141
|
+
generation_id: z.string().optional()
|
|
142
|
+
.describe('ID of a Kolbo video whose stems were already separated — picks the right source lane automatically.'),
|
|
143
|
+
project_id: projectIdField,
|
|
144
|
+
},
|
|
145
|
+
async ({ audio_url, source_type, generation_id, project_id }) => {
|
|
146
|
+
const body = buildBody({ audio_url, generation_id, project_id }, 'separate_ambience');
|
|
147
|
+
if (source_type && !generation_id) body.source_type = source_type;
|
|
148
|
+
const result = await client.post('/v1/audio/ambience', body, { timeoutMs: STEMS_TIMEOUT_MS });
|
|
149
|
+
return textResult({
|
|
150
|
+
...summarize(result),
|
|
151
|
+
...(result.skipped !== undefined && { skipped: result.skipped }),
|
|
152
|
+
...(result.levels && { levels_db: result.levels }),
|
|
153
|
+
cached: result.cached || false,
|
|
154
|
+
});
|
|
155
|
+
},
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
module.exports = { registerAudioStemTools };
|