feat(tools): add MuAPI image and video generation tools#6199
feat(tools): add MuAPI image and video generation tools#6199Anil-matcha wants to merge 4 commits into
Conversation
📝 WalkthroughWalkthroughTwo new CrewAI tools, ChangesMuAPI Image and Video Tools
Sequence Diagram(s)sequenceDiagram
participant Agent as CrewAI Agent
participant ImageTool as MuApiImageTool
participant VideoTool as MuApiVideoTool
participant Helper as _submit_and_poll
participant MuAPI as MuAPI REST API
Agent->>ImageTool: _run(prompt, model, width, height)
ImageTool->>Helper: POST /image endpoint + payload
Helper->>MuAPI: submit job
MuAPI-->>Helper: request_id
Helper->>MuAPI: poll GET /result/{request_id}
MuAPI-->>Helper: output URL
Helper-->>ImageTool: image URL
ImageTool-->>Agent: JSON {image_url, model, prompt}
Agent->>VideoTool: _run(prompt, model, duration, aspect_ratio)
VideoTool->>Helper: POST /video endpoint + payload
Helper->>MuAPI: submit job
MuAPI-->>Helper: request_id
Helper->>MuAPI: poll GET /result/{request_id}
MuAPI-->>Helper: output URL
Helper-->>VideoTool: video URL
VideoTool-->>Agent: JSON {video_url, model, prompt}
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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: 2
🧹 Nitpick comments (1)
lib/crewai-tools/src/crewai_tools/tools/muapi_tool/muapi_tool.py (1)
81-81: ⚡ Quick winRemove redundant json import.
The module already imports
jsonat line 1. The local reimport as_jsonis unnecessary and could confuse readers.♻️ Simplify by using the module-level import
def _submit_and_poll(api_key: str, endpoint: str, payload: dict, timeout: int = 300) -> str: """Submit a muapi.ai job and poll until completion, returning the output URL.""" - import json as _json headers = {"x-api-key": api_key, "Content-Type": "application/json"} - body = _json.dumps(payload).encode() + body = json.dumps(payload).encode() # Submit req = urllib.request.Request(f"{BASE_URL}/{endpoint}", data=body, headers=headers, method="POST") with urllib.request.urlopen(req, timeout=30) as resp: - data = _json.loads(resp.read()) + data = json.loads(resp.read()) request_id = data["request_id"] # Poll deadline = time.time() + timeout while time.time() < deadline: time.sleep(3) poll_req = urllib.request.Request( f"{BASE_URL}/predictions/{request_id}/result", headers={"x-api-key": api_key}, ) with urllib.request.urlopen(poll_req, timeout=15) as resp: - result = _json.loads(resp.read()) + result = json.loads(resp.read())🤖 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/muapi_tool/muapi_tool.py` at line 81, The module has a redundant import statement where json is imported as _json at line 81, while json is already imported at the module level at line 1. Remove the redundant import statement import json as _json and instead use the existing json module import that is already available at the module level. Update any references from _json to json to use the module-level import.
🤖 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/muapi_tool/muapi_tool.py`:
- Around line 87-90: The code block containing the urllib.request.urlopen call
and subsequent JSON parsing lacks error handling for HTTP errors, timeouts, and
malformed responses. Wrap the entire block starting with the urlopen call
through the data["request_id"] assignment in a try/except block that catches
HTTPError, URLError, socket.timeout, and json.JSONDecodeError exceptions, as
well as KeyError for missing "request_id" in the response. Each exception should
be logged or handled appropriately to provide clear feedback about what failed
during the API request.
- Around line 96-109: The polling request block starting with the `poll_req`
creation and `urllib.request.urlopen(poll_req, timeout=15)` call lacks error
handling for HTTP errors, network errors, and timeout exceptions. Wrap the
urlopen call and the result processing logic in a try/except block to catch
these exceptions gracefully and provide meaningful error messages to the user,
similar to how error handling should be implemented for the initial submission
request.
---
Nitpick comments:
In `@lib/crewai-tools/src/crewai_tools/tools/muapi_tool/muapi_tool.py`:
- Line 81: The module has a redundant import statement where json is imported as
_json at line 81, while json is already imported at the module level at line 1.
Remove the redundant import statement import json as _json and instead use the
existing json module import that is already available at the module level.
Update any references from _json to json to use the module-level import.
🪄 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: a959b638-65bb-4cbf-8995-9c792a091b1b
📒 Files selected for processing (4)
lib/crewai-tools/src/crewai_tools/tools/__init__.pylib/crewai-tools/src/crewai_tools/tools/muapi_tool/README.MDlib/crewai-tools/src/crewai_tools/tools/muapi_tool/__init__.pylib/crewai-tools/src/crewai_tools/tools/muapi_tool/muapi_tool.py
| req = urllib.request.Request(f"{BASE_URL}/{endpoint}", data=body, headers=headers, method="POST") | ||
| with urllib.request.urlopen(req, timeout=30) as resp: | ||
| data = _json.loads(resp.read()) | ||
| request_id = data["request_id"] |
There was a problem hiding this comment.
Add error handling for the submission request.
The urlopen call can raise HTTPError, URLError, or timeout exceptions, and JSON parsing can fail if the response is malformed. Additionally, data["request_id"] will raise KeyError if the API response doesn't include that field.
🛡️ Wrap HTTP call and response parsing in try/except
+ from urllib.error import HTTPError, URLError
+
headers = {"x-api-key": api_key, "Content-Type": "application/json"}
body = json.dumps(payload).encode()
# Submit
- req = urllib.request.Request(f"{BASE_URL}/{endpoint}", data=body, headers=headers, method="POST")
- with urllib.request.urlopen(req, timeout=30) as resp:
- data = json.loads(resp.read())
- request_id = data["request_id"]
+ try:
+ req = urllib.request.Request(f"{BASE_URL}/{endpoint}", data=body, headers=headers, method="POST")
+ with urllib.request.urlopen(req, timeout=30) as resp:
+ data = json.loads(resp.read())
+ request_id = data.get("request_id")
+ if not request_id:
+ raise RuntimeError("API response missing 'request_id'")
+ except HTTPError as e:
+ raise RuntimeError(f"HTTP error during submission: {e.code} {e.reason}")
+ except URLError as e:
+ raise RuntimeError(f"Network error during submission: {e.reason}")
+ except (ValueError, KeyError) as e:
+ raise RuntimeError(f"Invalid JSON response from API: {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/muapi_tool/muapi_tool.py` around
lines 87 - 90, The code block containing the urllib.request.urlopen call and
subsequent JSON parsing lacks error handling for HTTP errors, timeouts, and
malformed responses. Wrap the entire block starting with the urlopen call
through the data["request_id"] assignment in a try/except block that catches
HTTPError, URLError, socket.timeout, and json.JSONDecodeError exceptions, as
well as KeyError for missing "request_id" in the response. Each exception should
be logged or handled appropriately to provide clear feedback about what failed
during the API request.
| poll_req = urllib.request.Request( | ||
| f"{BASE_URL}/predictions/{request_id}/result", | ||
| headers={"x-api-key": api_key}, | ||
| ) | ||
| with urllib.request.urlopen(poll_req, timeout=15) as resp: | ||
| result = _json.loads(resp.read()) | ||
| status = result.get("status", "pending") | ||
| if status == "completed": | ||
| outputs = result.get("outputs", []) | ||
| if not outputs: | ||
| raise RuntimeError("Generation completed but returned no outputs") | ||
| return outputs[0] | ||
| if status in ("failed", "cancelled"): | ||
| raise RuntimeError(f"Generation {status}: {result.get('error', '')}") |
There was a problem hiding this comment.
Add error handling for polling requests.
Similar to the submission request, urlopen during polling can raise HTTP errors, network errors, or timeout exceptions. The polling loop should handle these gracefully to provide useful error messages.
🛡️ Wrap polling requests in try/except
+ from urllib.error import HTTPError, URLError
+
# Poll
deadline = time.time() + timeout
while time.time() < deadline:
time.sleep(3)
- poll_req = urllib.request.Request(
- f"{BASE_URL}/predictions/{request_id}/result",
- headers={"x-api-key": api_key},
- )
- with urllib.request.urlopen(poll_req, timeout=15) as resp:
- result = json.loads(resp.read())
- status = result.get("status", "pending")
- if status == "completed":
- outputs = result.get("outputs", [])
- if not outputs:
- raise RuntimeError("Generation completed but returned no outputs")
- return outputs[0]
- if status in ("failed", "cancelled"):
- raise RuntimeError(f"Generation {status}: {result.get('error', '')}")
+ try:
+ poll_req = urllib.request.Request(
+ f"{BASE_URL}/predictions/{request_id}/result",
+ headers={"x-api-key": api_key},
+ )
+ with urllib.request.urlopen(poll_req, timeout=15) as resp:
+ result = json.loads(resp.read())
+ except (HTTPError, URLError) as e:
+ # Transient errors during polling should not immediately fail; continue polling
+ continue
+ except (ValueError, KeyError):
+ # Invalid JSON response; continue polling in case it's transient
+ continue
+
+ status = result.get("status", "pending")
+ if status == "completed":
+ outputs = result.get("outputs", [])
+ if not outputs:
+ raise RuntimeError("Generation completed but returned no outputs")
+ return outputs[0]
+ if status in ("failed", "cancelled"):
+ raise RuntimeError(f"Generation {status}: {result.get('error', '')}")🤖 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/muapi_tool/muapi_tool.py` around
lines 96 - 109, The polling request block starting with the `poll_req` creation
and `urllib.request.urlopen(poll_req, timeout=15)` call lacks error handling for
HTTP errors, network errors, and timeout exceptions. Wrap the urlopen call and
the result processing logic in a try/except block to catch these exceptions
gracefully and provide meaningful error messages to the user, similar to how
error handling should be implemented for the initial submission request.
Summary
Adds
MuApiImageToolandMuApiVideoTool— two new generation tools backed by muapi.ai, a unified API aggregator for 400+ generative media models.What this adds
lib/crewai-tools/src/crewai_tools/tools/muapi_tool/muapi_tool.py— twoBaseToolsubclasseslib/crewai-tools/src/crewai_tools/tools/muapi_tool/README.MDtools/__init__.pyMuApiImageTool
Generates images from text prompts. Supports: Flux Schnell/Dev/Kontext, HiDream, Midjourney, GPT-4o Image, Google Imagen 4, Seedream, Reve, Ideogram, Hunyuan, Wan, Qwen.
MuApiVideoTool
Generates short MP4 videos from text prompts. Supports: Veo3, Kling, Wan 2.1/2.2, Seedance Pro, Runway, Pixverse, Sora, Minimax.
Usage
Setup:
export MUAPI_API_KEY=your_key— get one at muapi.ai/dashboard/api-keys.Implementation notes
urllib.requestto keep dependencies minimal (nohttpx/requestsrequired)/api/v1/{model}→ poll/api/v1/predictions/{id}/resultMUAPI_API_KEYdeclared viaenv_varsfor proper crewAI credential handlingSummary by CodeRabbit
New Features
Documentation