Skip to content

feat: add MuAPI video generation tool#196

Open
Anil-matcha wants to merge 1 commit into
video-db:mainfrom
Anil-matcha:feat/muapi-video-tool
Open

feat: add MuAPI video generation tool#196
Anil-matcha wants to merge 1 commit into
video-db:mainfrom
Anil-matcha:feat/muapi-video-tool

Conversation

@Anil-matcha

@Anil-matcha Anil-matcha commented Jun 17, 2026

Copy link
Copy Markdown

Summary

Adds a new video generation tool backed by muapi.ai — a generative media API aggregator that provides unified access to 400+ models across multiple providers.

What this adds

  • backend/director/tools/muapi_video.pyMuApiVideoGenerationTool class with text_to_video() and image_to_video() methods
  • Follows the same pattern as existing tools (fal_video.py, kling.py, etc.)
  • Supports text-to-video: Veo3, Kling, Wan, Seedance, Runway, Pixverse, HunyuanVideo, Minimax and more
  • Supports image-to-video: Kling, Wan, Seedance, Runway, Pixverse, Vidu, HunyuanVideo and more
  • Async submit → poll pattern matching muapi's API contract

Usage

tool = MuApiVideoGenerationTool(api_key=os.environ["MUAPI_API_KEY"])

# Text to video
tool.text_to_video(
    prompt="A drone shot over a mountain lake at sunrise",
    save_at="output.mp4",
    duration=5,
    config={"model_name": "veo3-fast"},
)

# Image to video
tool.image_to_video(
    image_url="https://example.com/photo.jpg",
    save_at="output.mp4",
    duration=5,
    config={"model_name": "kling-master"},
    prompt="pan right slowly",
)

Get an API key at muapi.ai/dashboard/api-keys.

Summary by CodeRabbit

  • New Features
    • Added video generation capabilities from text prompts with configurable duration and aspect ratio options.
    • Added video generation from image URLs with optional text prompts.
    • Support for multiple video generation models through integrated API endpoints.

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

A new file backend/director/tools/muapi_video.py (204 lines) is added, introducing MuApiVideoGenerationTool. It defines configuration constants (PARAMS_CONFIG, T2V_ENDPOINTS, I2V_ENDPOINTS), a requests.Session-based HTTP client, internal _submit/_poll methods, and two public methods: text_to_video and image_to_video.

Changes

MuApi Video Generation Tool

Layer / File(s) Summary
Config constants and endpoint mappings
backend/director/tools/muapi_video.py
Defines PARAMS_CONFIG for model, duration, and aspect ratio inputs for both generation modes; T2V_ENDPOINTS and I2V_ENDPOINTS map model names to muapi endpoint identifiers.
Session setup, _submit, and _poll
backend/director/tools/muapi_video.py
__init__ stores the API key in a persistent requests.Session header; _submit POSTs a generation request and returns request_id; _poll loops on GET until completed, failed/cancelled (raises), or deadline exceeded (raises), returning the first output URL.
text_to_video and image_to_video public methods
backend/director/tools/muapi_video.py
Each method builds its payload, resolves the muapi endpoint, calls _submit/_poll, downloads video bytes to save_at, and returns {"status": "success", "video_path": save_at}; exceptions are re-raised with type and message included.

Sequence Diagram

sequenceDiagram
  participant Caller
  participant MuApiVideoGenerationTool
  participant muapi_ai as muapi.ai API

  Caller->>MuApiVideoGenerationTool: text_to_video(prompt, save_at, duration, config)
  MuApiVideoGenerationTool->>muapi_ai: POST /t2v-endpoint (_submit)
  muapi_ai-->>MuApiVideoGenerationTool: {"request_id": "..."}
  loop _poll until completed or timeout
    MuApiVideoGenerationTool->>muapi_ai: GET /predictions/{request_id}
    muapi_ai-->>MuApiVideoGenerationTool: {status, output: [url]}
  end
  MuApiVideoGenerationTool->>muapi_ai: GET output_url (download bytes)
  muapi_ai-->>MuApiVideoGenerationTool: video bytes
  MuApiVideoGenerationTool-->>Caller: {"status": "success", "video_path": save_at}
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 Hop, hop, a new tool appears,
Sending prompts through the wires,
Polling the skies for videos bright,
Downloading frames by bytes of light,
From muapi's fields to save_at path —
A rabbit's joy in the aftermath! 🎬

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add MuAPI video generation tool' is clear, specific, and directly matches the main change: introducing a new MuApiVideoGenerationTool class.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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 `@backend/director/tools/muapi_video.py`:
- Around line 123-127: The _submit method directly accesses
resp.json()["request_id"] which will raise a raw KeyError if the key is missing
from the API response. Wrap the resp.json()["request_id"] access in a try-except
block to catch KeyError, and when caught, raise a more informative exception
that includes the actual response content to aid debugging. This will provide
clear context about what the API returned instead of the expected request_id.
- Around line 162-172: The exception handling in the video generation code is
losing the original traceback by re-raising a new Exception without preserving
the exception chain. Modify the raise statement in the except block (where the
message "Error generating video" is constructed) to use the `from e` syntax to
preserve the original exception context. Apply the same fix to the
`image_to_video` method (mentioned at line 202) where this issue also exists.
This ensures the full traceback is retained for better debugging.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: fda5b229-148f-4b43-aa38-9f0d6164ad14

📥 Commits

Reviewing files that changed from the base of the PR and between 70e0b3d and 387afb0.

📒 Files selected for processing (1)
  • backend/director/tools/muapi_video.py

Comment on lines +123 to +127
def _submit(self, endpoint: str, payload: dict) -> str:
"""Submit a generation request and return the request_id."""
resp = self.session.post(f"{self.BASE_URL}/{endpoint}", json=payload, timeout=30)
resp.raise_for_status()
return resp.json()["request_id"]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Handle missing request_id in API response.

If the API response doesn't contain the expected request_id key (e.g., due to an API change or unexpected error format), a raw KeyError is raised without context. Adding explicit handling improves debuggability.

Proposed fix
     def _submit(self, endpoint: str, payload: dict) -> str:
         """Submit a generation request and return the request_id."""
         resp = self.session.post(f"{self.BASE_URL}/{endpoint}", json=payload, timeout=30)
         resp.raise_for_status()
-        return resp.json()["request_id"]
+        data = resp.json()
+        if "request_id" not in data:
+            raise Exception(f"API response missing 'request_id': {data}")
+        return data["request_id"]
📝 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.

Suggested change
def _submit(self, endpoint: str, payload: dict) -> str:
"""Submit a generation request and return the request_id."""
resp = self.session.post(f"{self.BASE_URL}/{endpoint}", json=payload, timeout=30)
resp.raise_for_status()
return resp.json()["request_id"]
def _submit(self, endpoint: str, payload: dict) -> str:
"""Submit a generation request and return the request_id."""
resp = self.session.post(f"{self.BASE_URL}/{endpoint}", json=payload, timeout=30)
resp.raise_for_status()
data = resp.json()
if "request_id" not in data:
raise Exception(f"API response missing 'request_id': {data}")
return data["request_id"]
🤖 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 `@backend/director/tools/muapi_video.py` around lines 123 - 127, The _submit
method directly accesses resp.json()["request_id"] which will raise a raw
KeyError if the key is missing from the API response. Wrap the
resp.json()["request_id"] access in a try-except block to catch KeyError, and
when caught, raise a more informative exception that includes the actual
response content to aid debugging. This will provide clear context about what
the API returned instead of the expected request_id.

Comment on lines +162 to +172
try:
request_id = self._submit(endpoint, payload)
video_url = self._poll(request_id)
video_data = requests.get(video_url, timeout=120)
video_data.raise_for_status()
with open(save_at, "wb") as f:
f.write(video_data.content)
except Exception as e:
raise Exception(f"Error generating video: {type(e).__name__}: {str(e)}")

return {"status": "success", "video_path": save_at}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Preserve exception chain with raise ... from e.

The re-raised exception loses the original traceback, making debugging harder. Using exception chaining preserves the full context. The same issue applies to image_to_video (line 202).

Proposed fix for both methods
         except Exception as e:
-            raise Exception(f"Error generating video: {type(e).__name__}: {str(e)}")
+            raise Exception(f"Error generating video: {type(e).__name__}: {e}") from e

Apply to both line 170 and line 202.

🧰 Tools
🪛 Ruff (0.15.17)

[warning] 169-169: Do not catch blind exception: Exception

(BLE001)


[warning] 170-170: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


[warning] 170-170: Use explicit conversion flag

Replace with conversion flag

(RUF010)

🤖 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 `@backend/director/tools/muapi_video.py` around lines 162 - 172, The exception
handling in the video generation code is losing the original traceback by
re-raising a new Exception without preserving the exception chain. Modify the
raise statement in the except block (where the message "Error generating video"
is constructed) to use the `from e` syntax to preserve the original exception
context. Apply the same fix to the `image_to_video` method (mentioned at line
202) where this issue also exists. This ensures the full traceback is retained
for better debugging.

Source: Linters/SAST tools

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