Analyze: full_report and moment descriptions in one batch each

SI
2d ago
+1146-451

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.

BeforeAfter
full_report13 calls: 13 downloads, $0.65 in base fees + seconds1 batch: 1 download, $0.05 + the seconds of sections that came back usable
Moments with descriptionsup to 7 calls: 7 downloads, up to $0.35 in base fees + seconds1 batch: 1 download, $0.05 + seconds
Credit check (gate) before the callfull_report $0.65; moments $0.36$0.05 for every analyze request; moments $0.06

Modal (modal/serve_music_flamingo.py)

  • Each generate_batch prompt can now carry clip_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 new extract_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 in failed, and isn't charged.
    • If the whole batch fails, every section fails and nothing is charged. processAnalyzeMusicRequest still returns 502 in that case.
  • describeMoments replaces describeMoment: 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: minimumCreditsForAnalyzeRequest is removed; its four callers now check for FLAMINGO_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.
  • maxDuration raised to 800 s on /api/songs/analyze and /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_batch ignores the new per-prompt fields. full_report still works, all greedy.
    • Moment answers come back without the clip echo, so they're dropped and not charged.
  • 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_report and /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_report is the sum of its 13 calls"
  • the analyze 402 description: "$0.65 for full_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-contracts clean.
  • tsc reports no errors in the changed files. The 214 remaining errors already exist on main, 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_batch accepts per-prompt clip_start/clip_end (clips are cut fresh from the one download) and per-prompt temperature/top_p/do_sample overrides; 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_report sends 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 in failed and isn't charged. A failed batch fails every section and charges nothing.
  • describeMoments replaces describeMoment: 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.
  • maxDuration is 800 s on /api/songs/analyze and /mcp since sections now run sequentially on one container.
  • The docs pricing and credits text (recoupable/docs#36) reflects the batch billing, and the published-docs fixture 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.