feat(tools): add tunova music generation tool#6513
Conversation
Adds TunovaMusicGenerationTool, which generates complete songs from text prompts via the Tunova API (hosted Suno-quality music generation, billed only on successful renders). Submits a generation job, polls until the render completes or times out, and returns the track title and audio URL. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
📝 WalkthroughWalkthroughAdds ChangesTunova music generation
Sequence Diagram(s)sequenceDiagram
participant Caller
participant TunovaMusicGenerationTool
participant TunovaAPI
Caller->>TunovaMusicGenerationTool: run(prompt, options)
TunovaMusicGenerationTool->>TunovaAPI: POST /api/generate
TunovaAPI-->>TunovaMusicGenerationTool: job_id
loop Until completion or deadline
TunovaMusicGenerationTool->>TunovaAPI: GET /api/jobs/{job_id}
TunovaAPI-->>TunovaMusicGenerationTool: job status and clips
end
TunovaMusicGenerationTool-->>Caller: generation result
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@lib/crewai-tools/src/crewai_tools/tools/tunova_music_generation_tool/tunova_music_generation_tool.py`:
- Around line 102-106: Update the submission error handler around _submit_job to
catch ValueError alongside requests.exceptions.RequestException, preserving the
existing logging and user-friendly failure-string behavior for missing job_id
responses.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 0f6c0e4b-a758-4f25-9d36-1baa320aadcf
📒 Files selected for processing (6)
lib/crewai-tools/src/crewai_tools/__init__.pylib/crewai-tools/src/crewai_tools/tools/__init__.pylib/crewai-tools/src/crewai_tools/tools/tunova_music_generation_tool/README.mdlib/crewai-tools/src/crewai_tools/tools/tunova_music_generation_tool/__init__.pylib/crewai-tools/src/crewai_tools/tools/tunova_music_generation_tool/tunova_music_generation_tool.pylib/crewai-tools/tests/tools/tunova_music_generation_tool_test.py
| try: | ||
| job_id = self._submit_job(prompt, make_instrumental) | ||
| except requests.exceptions.RequestException as e: | ||
| logger.error(f"Error submitting generation job to Tunova API: {e}") | ||
| return f"Failed to submit music generation job: {e}" |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Catch ValueError alongside RequestException in the submission error handler.
_submit_job raises ValueError at line 74 when the API returns a 2xx response without a job_id field. The except block here only catches requests.exceptions.RequestException, so that ValueError propagates unhandled and crashes the tool instead of returning a user-friendly error string — breaking the agent's workflow.
🐛 Proposed fix
try:
job_id = self._submit_job(prompt, make_instrumental)
- except requests.exceptions.RequestException as e:
+ except (requests.exceptions.RequestException, ValueError) as e:
logger.error(f"Error submitting generation job to Tunova API: {e}")
return f"Failed to submit music generation job: {e}"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| try: | |
| job_id = self._submit_job(prompt, make_instrumental) | |
| except requests.exceptions.RequestException as e: | |
| logger.error(f"Error submitting generation job to Tunova API: {e}") | |
| return f"Failed to submit music generation job: {e}" | |
| try: | |
| job_id = self._submit_job(prompt, make_instrumental) | |
| except (requests.exceptions.RequestException, ValueError) as e: | |
| logger.error(f"Error submitting generation job to Tunova API: {e}") | |
| return f"Failed to submit music generation job: {e}" |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@lib/crewai-tools/src/crewai_tools/tools/tunova_music_generation_tool/tunova_music_generation_tool.py`
around lines 102 - 106, Update the submission error handler around _submit_job
to catch ValueError alongside requests.exceptions.RequestException, preserving
the existing logging and user-friendly failure-string behavior for missing
job_id responses.
What
Adds
TunovaMusicGenerationTooltocrewai-tools— the catalog's first music/audio-generation tool. It generates a complete song (Suno v5.5 quality) from a text prompt via the hosted Tunova API and returns the title + audio URL.POST /api/generate→ pollGET /api/jobs/{id}every 10s (configurablewait_secondscap, default 360)TUNOVA_API_KEYenv var (free tier: 50 tokens ≈ 5 songs, no card — reviewers can actually run it)requests(already a base dependency) — no new dependenciesWhy
crewai-toolshas search, scraping, files, images and video-adjacent tools, but no music/audio generation. Music is a common ask in content-pipeline crews (videos, podcasts, jingles), and the current DIY path is wiring brittle unofficial Suno wrappers by hand.Testing
serper_dev_tool_test.py) — 6/6 passruff check+ruff format --check(repo's pinned 0.15.1) — cleanmypy --config-file pyproject.tomlon the new file — cleanfrom crewai_tools import TunovaMusicGenerationToolNotes for maintainers
llm-generatedlabel — this PR was LLM-assisted (per CONTRIBUTING.md; I can't set labels on this repo).tool.specs.jsonuntouched: thegenerate-tool-specs.ymlbot skips fork PRs — happy for it to be regenerated via workflow_dispatch after merge.docs/en/deliberately not touched to keep the diff minimal (298 lines, 6 files) — glad to follow up with a docs PR if you want one.🤖 Generated with Claude Code