Skip to content

Analyze: full_report and moment descriptions in one batch each - #1233

Open
sidneyswift wants to merge 4 commits into
mainfrom
codex/batch-full-report-moments
Open

sidneyswift wants to merge 4 commits into
mainfrom
codex/batch-full-report-moments

Conversation

@sidneyswift

@sidneyswift sidneyswift commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

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_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 (Analyze and moments pricing: one batched model request 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.

Written for commit 2a3d988. Summary will update on new commits.

View guided diff Turn on auto-fix

full_report sent its 13 sections as 13 separate model calls: 13 audio
downloads and 13 base fees ($0.65 before GPU time). Song moments described
each moment with its own call: up to 7 downloads and 7 base fees. Both now
go through generate_batch: one download, one base fee.

- Modal generate_batch: a prompt may carry clip_start/clip_end (cut fresh
  from the one download) and its own temperature/top_p/do_sample. Clipped
  answers echo the window. Validation moved into pure, locally tested
  helpers (modal/test_serve_music_flamingo.py).
- executeFullReport: one batch with each preset's own settings; charges
  base + the seconds of sections that parsed; failed sections cost nothing.
- describeMoments replaces describeMoment: one batch, one clip per moment;
  an answer without the clip echo is dropped, so words never come from the
  whole song.
- Credit gates: analyze is one base fee for every request (the per-request
  helper is gone); moments is measurement + one base fee.
- The ten presets whose answers are stored as data decode greedily, so the
  same song gets the same answer; the three prose presets keep sampling.
- maxDuration 800 on /api/songs/analyze and /mcp: the sections now run one
  after another on one container.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
api Ready Ready Preview Nov 5, 2026 6:18am UTC

Request Review

@coderabbitai

coderabbitai Bot commented Nov 5, 2026 •

Copy link
Copy Markdown

Important

  • Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

Run configuration

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 44 files

Confidence score: 4/5

  • modal/serve_music_flamingo.py has grown to 551 lines with the new helpers, well beyond the 100-line guideline. Split the helpers into focused modules to keep the code maintainable.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="modal/serve_music_flamingo.py">

<violation number="1" location="modal/serve_music_flamingo.py:87">
P2: Custom agent: **Enforce Clear Code Style and Maintainability Practices**

This adds clip-processing and batch-validation helpers to a 551-line module, far beyond the guideline’s 100-line limit. Split the helpers into focused modules to keep this service module maintainable.</violation>
</file>
Architecture diagram
sequenceDiagram
    participant Client as Client / MCP
    participant API as Recoup API Route
    participant Credits as Credit Gate
    participant Batch as callFlamingoGenerateBatch
    participant Modal as Modal App (Music Flamingo)
    participant Billing as chargeForBatchAnswers
    participant DB as Usage Database

    Note over Client,DB: full_report and Moment Descriptions: Single Batch Flow

    Client->>API: POST /api/songs/analyze (full_report) or /mcp
    API->>Credits: Check FLAMINGO_BASE_CREDITS (fixed gate)
    Credits-->>API: Sufficient credits
    API->>Batch: Generate batch (13 prompts, one per section)

    Batch->>Modal: POST /generate_batch
    Note over Modal: Download audio ONCE, run prompts sequentially
    Modal->>Modal: Validate prompts (per-prompt clip/settings)
    Modal-->>Batch: Answers with elapsed_seconds (and clip echo)

    alt All sections answered
        Batch-->>API: Complete answers
        API->>API: Parse responses per preset
        API->>Billing: Charge for used answers (one row)
        Billing->>DB: Insert usage record (base + sum of seconds)
    else Some sections missing/failed
        Modal-->>Batch: Partial answers / batch error
        Batch-->>API: Missing answers flagged
        API->>API: Mark failed sections as { error }, not charged
        API->>Billing: Charge only for usable answers
    end

    API-->>Client: Report (successful + failed sections)

    Note over Client,DB: Moment Descriptions

    Client->>API: POST /api/songs/moments (with describe)
    API->>Credits: Check SONG_MEASURE_CREDITS + FLAMINGO_BASE_CREDITS
    Credits-->>API: Sufficient credits
    API->>Batch: Generate batch (one prompt per moment, each with clip window)

    Batch->>Modal: POST /generate_batch (with clip_start/clip_end per prompt)
    Modal-->>Batch: Answers echoing their clip windows

    alt Answer matches its clip window
        API->>API: Accept description
        API->>Billing: Charge for accepted descriptions
        Billing->>DB: Insert usage record
    else Answer does not match clip (no echo)
        API->>API: Drop answer (no words, not charged)
    end

    API-->>Client: Moments with descriptions (or without, if dropped)
Loading

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread modal/serve_music_flamingo.py Resolved
@@ -84,28 +84,125 @@ def download_model_weights():
print(f"Model weights saved to {MODEL_CACHE_DIR}/model")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Custom agent: Enforce Clear Code Style and Maintainability Practices

This adds clip-processing and batch-validation helpers to a 551-line module, far beyond the guideline’s 100-line limit. Split the helpers into focused modules to keep this service module maintainable.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At modal/serve_music_flamingo.py, line 87:

<comment>This adds clip-processing and batch-validation helpers to a 551-line module, far beyond the guideline’s 100-line limit. Split the helpers into focused modules to keep this service module maintainable.</comment>

<file context>
@@ -84,28 +84,125 @@ def download_model_weights():
     print(f"Model weights saved to {MODEL_CACHE_DIR}/model")
 
 
+def extract_clip(path, start, end):
+    """
+    Cut start..end seconds of `path` into a new 16 kHz mono WAV and return its
</file context>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not splitting this here: a second module would have to be mounted into the 16 GB model image (add_local_file after the weights layer), which is deploy risk for a style rule this repo doesn't have. The new helpers are pure, module-level and covered by modal/test_serve_music_flamingo.py.

🤖 Addressed by Claude Code

Comment thread lib/songMoments/describeMoments.ts Resolved
Comment thread lib/flamingo/executeFullReport.ts Resolved
Comment thread modal/README.md Resolved
Comment thread lib/flamingo/presets/audienceProfile.ts Resolved
…tch loop

- describeMoments drops a reply that parses to nothing usable, so it is
  neither merged into the moment nor billed.
- executeFullReport treats a parsed {"error": ...} answer as a failed
  section (listed in failed, not billed).
- isFlamingoGenerateBatchResult requires both clip ends as numbers or
  neither.
- Modal: is_number rejects NaN, infinity and ints too large for a float, so
  a huge clip_end is a 422 instead of an OverflowError; the batch loop moves
  into run_batch, tested with a fake model for per-prompt settings, clip
  echoes and clip cleanup (also on failure).
- Greedy presets use the neutral temperature 1.0, so transformers stops
  logging an ignored-flag notice on every call.
- Docs and test names: processAnalyzeMusicRequest billing note, the moments
  gate test name, the exact billed answer in the partial-drop test, and the
  ffmpeg/ffprobe prerequisite for the Modal tests.

Not changed: splitting serve_music_flamingo.py into modules would mean
mounting extra files into the 16 GB model image; the helpers stay in the
module, pure and tested.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 24 files (changes from recent commits).

Confidence score: 4/5

  • lib/flamingo/executeFullReport.ts now exceeds the 100-line maintainability limit. Refactor or split the module to bring it back under the limit.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="lib/flamingo/executeFullReport.ts">

<violation number="1" location="lib/flamingo/executeFullReport.ts:81">
P2: Custom agent: **Enforce Clear Code Style and Maintainability Practices**

This change expands `executeFullReport.ts` from 105 to 121 lines, exceeding Rule 3's under-100-line limit. Refactor or split the module so it stays below that limit.</violation>
</file>

Requires human review: Auto-approval blocked because this review re-detected 1 unresolved issue already reported by Cubic.

View guided diff | Turn on auto-fix | Re-trigger cubic

}
try {
const data = preset.parseResponse ? preset.parseResponse(answer.response) : answer.response;
if (isErrorShape(data)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Custom agent: Enforce Clear Code Style and Maintainability Practices

This change expands executeFullReport.ts from 105 to 121 lines, exceeding Rule 3's under-100-line limit. Refactor or split the module so it stays below that limit.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At lib/flamingo/executeFullReport.ts, line 81:

<comment>This change expands `executeFullReport.ts` from 105 to 121 lines, exceeding Rule 3's under-100-line limit. Refactor or split the module so it stays below that limit.</comment>

<file context>
@@ -77,9 +77,15 @@ export async function executeFullReport(
-        ? preset.parseResponse(answer.response)
-        : answer.response;
+      const data = preset.parseResponse ? preset.parseResponse(answer.response) : answer.response;
+      if (isErrorShape(data)) {
+        // A reply of {"error": "..."} would read as a failed section in the
+        // report, so it is one: listed in `failed` and not billed.
</file context>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaving this as one file: the 100-line limit isn't one of this repo's rules (CLAUDE.md asks for one exported function per file, which this keeps), and the extra lines are the failed-section handling plus two small private helpers in the same flow.

🤖 Addressed by Claude Code

recoupable/docs#36 rewrites the analyze/moments pricing and credits text;
the shared fixture records those source hashes. Contract tests pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sidneyswift added a commit to recoupable/docs that referenced this pull request Nov 5, 2026
Generated with build-contract-examples.py against recoupable/api#1233,
which carries the matching API fixture.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sidneyswift added a commit to recoupable/docs that referenced this pull request Nov 5, 2026
Pins recoupable/api#1233 head (83b330fe). Re-pin to its merge commit
after it merges.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Asserts the charged answer ids are every section but the failed one, and
fixes the test comment: that section is reported as failed and not billed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 issues found across 2 files (changes from recent commits).

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Requires human review: Batches full_report and moment descriptions into one Modal call and switches ten presets to greedy decoding, with billing, credit-gate, and timeout changes the author says were not verified on a GPU/production run.

View guided diff | Turn on auto-fix | Re-trigger cubic

This branch was successfully deployed

1 active deployment
Preview — 2a3d9886 Deployed Nov 5, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant