Analyze: full_report and moment descriptions in one batch each
What this changes
full_report and song-moment descriptions each used one model call per question. Every call downloaded the song again and paid the $0.05 base fee. Both now go through generate_batch, so each downloads once and pays one base fee.
| Before | After | |
|---|---|---|
full_report | 13 calls: 13 downloads, $0.65 in base fees + seconds | 1 batch: 1 download, $0.05 + the seconds of sections that came back usable |
| Moments with descriptions | up to 7 calls: 7 downloads, up to $0.35 in base fees + seconds | 1 batch: 1 download, $0.05 + seconds |
| Credit check (gate) before the call | full_report $0.65; moments $0.36 | $0.05 for every analyze request; moments $0.06 |
Modal (modal/serve_music_flamingo.py)
- Each
generate_batchprompt can now carryclip_start/clip_end. The clip is cut fresh from the single download for each prompt. - Each prompt can also carry its own
temperature/top_p/do_sample, overriding the batch's settings for that prompt only. - Answers from a clip echo back the window they heard.
- Validation moved into pure helpers, tested locally in
modal/test_serve_music_flamingo.py(21 tests). The existing 44 measurement tests still pass. cut_clip(used by/generate) now reuses the newextract_clip.
API
executeFullReport: one batch. Each section keeps its preset's prompt, token limit, temperature and sampling choice.- A section whose answer is missing or doesn't parse returns
{ error }, is listed infailed, and isn't charged. - If the whole batch fails, every section fails and nothing is charged.
processAnalyzeMusicRequeststill returns 502 in that case.
- A section whose answer is missing or doesn't parse returns
describeMomentsreplacesdescribeMoment: one batch with one clip per moment.- An answer that doesn't echo its clip is dropped, so a description can never come from the whole song.
- A moment without a description comes back with no words and isn't charged.
chargeForBatchAnswers(new): one usage row, priced at base + the seconds of the answers that were used.- Credit gates:
minimumCreditsForAnalyzeRequestis removed; its four callers now check forFLAMINGO_BASE_CREDITS. The moments gate is the measurement price + one base fee when anything will be described. - Greedy decoding: the ten presets whose answers are stored as data now always pick the most likely answer (
do_sample: false), so the same song gets the same answer on every run. The three prose presets (song_description,playlist_pitch,artist_development_notes) keep sampling.- Checked against the pinned
transformers==5.16.1: setting a temperature alongside greedy decoding only logs a notice, it doesn't raise.
- Checked against the pinned
maxDurationraised to 800 s on/api/songs/analyzeand/mcp. The 13 sections now run one after another on one container, so a report takes the sum of the section times, not the time of the longest one. 800 s stays above Modal's 600 s timeout.
Deploy order
A merge deploys Modal (Actions) and the API (Vercel) at the same time. The changes are safe in either order:
- API live before Modal:
- The old
generate_batchignores the new per-prompt fields.full_reportstill works, all greedy. - Moment answers come back without the clip echo, so they're dropped and not charged.
- The old
- Modal live before API: the new fields are optional and nothing sends them yet.
Not verified
- Not run on a GPU. Check after deploy on a real song with
full_reportand/api/songs/moments(describe on):- total wall time,
- the single usage row,
- that every moment has words.
- Whether greedy decoding changes how often presets return unparseable JSON (RA-04) needs that same production run.
Follow-up
The docs still describe the old pricing:
credits.mdx: "full_reportis the sum of its 13 calls"- the analyze 402 description: "
$0.65forfull_report" - the moments pricing
That needs a docs PR plus the published-docs fixture refresh after this merges.
Tests
pnpm test: 1453 files, 9283 tests passing.- lint, format and
typecheck:response-contractsclean. tscreports no errors in the changed files. The 214 remaining errors already exist onmain, which has 215.
🤖 Generated with Claude Code
Summary by cubic
full_report and song-moment descriptions each ran one model call per question, downloading the song and paying the $0.05 base fee per call. Both now run as a single generate_batch, so each downloads the audio once and pays one base fee per request.
Behavior
- Modal's
generate_batchaccepts per-promptclip_start/clip_end(clips are cut fresh from the one download) and per-prompttemperature/top_p/do_sampleoverrides; clipped answers echo their window. Clip bounds must be finite numbers — NaN, infinity, or a value too large for a float is a 422. full_reportsends all 13 sections in one batch, each with its preset's prompt and settings; a section that is missing or returns a parsed{ error }is listed infailedand isn't charged. A failed batch fails every section and charges nothing.describeMomentsreplacesdescribeMoment: one batch with one clip per moment; an answer that doesn't echo its clip or parses to nothing usable is dropped, and an undescribed moment comes back without words and isn't charged.- Billing is one usage row per batch via
chargeForBatchAnswers: base price plus the seconds of the answers that were used, billing only the answers that parsed into usable results. - Analyze and moments credit gates now check one base fee (moments adds the measurement price when anything is described) instead of one fee per section or moment.
- The ten presets whose answers are stored as data now decode greedily with the neutral temperature 1.0, so the same song always gets the same answer without an ignored-flag notice; the three prose presets keep sampling.
maxDurationis 800 s on/api/songs/analyzeand/mcpsince sections now run sequentially on one container.- The docs pricing and credits text (recoupable/docs#36) reflects the batch billing, and the
published-docsfixture is refreshed.
Deploy order
- Either order is safe: old Modal ignores the new per-prompt fields, and the new Modal fields are optional until the API sends them. Not yet validated on a GPU in production.