Manim-Agent / shared /manim_module.py
github-actions[bot]
deploy: release 8c0c1b7
f1f74fb
Raw
History Blame Contribute Delete
2.76 kB
"""Python SDK / Module Client for Math Agent integration."""
from __future__ import annotations
import asyncio
import logging
from typing import Any
from uuid import UUID
import httpx
from shared.schemas.math_agent import (
MathRenderRequest,
MathRenderResponse,
VisualizationSpec,
)
logger = logging.getLogger(__name__)
class ManimModule:
"""High-level client for Math Agent to invoke the Manim video generation service."""
def __init__(
self,
base_url: str = "http://localhost:8000",
internal_token: str | None = None,
bearer_token: str | None = None,
timeout: float = 60.0,
) -> None:
self.base_url = base_url.rstrip("/")
self.headers: dict[str, str] = {}
if internal_token:
self.headers["X-Internal-Token"] = internal_token
if bearer_token:
self.headers["Authorization"] = f"Bearer {bearer_token}"
self.timeout = timeout
async def generate(
self,
spec: VisualizationSpec | dict[str, Any],
callback_url: str | None = None,
) -> MathRenderResponse:
"""Submit a VisualizationSpec to start asynchronous video generation."""
parsed_spec = spec if isinstance(spec, VisualizationSpec) else VisualizationSpec.model_validate(spec)
req = MathRenderRequest(spec=parsed_spec, callback_url=callback_url)
async with httpx.AsyncClient(base_url=self.base_url, headers=self.headers, timeout=self.timeout) as client:
resp = await client.post("/v1/math/generate", json=req.model_dump(mode="json"))
resp.raise_for_status()
return MathRenderResponse.model_validate(resp.json())
async def get_status(self, job_id: UUID | str) -> MathRenderResponse:
"""Query current status, progress, or completed video URL."""
async with httpx.AsyncClient(base_url=self.base_url, headers=self.headers, timeout=self.timeout) as client:
resp = await client.get(f"/v1/math/jobs/{job_id}")
resp.raise_for_status()
return MathRenderResponse.model_validate(resp.json())
async def wait_for_completion(
self,
job_id: UUID | str,
poll_interval: float = 3.0,
timeout: float = 600.0,
) -> MathRenderResponse:
"""Poll until the video rendering is completed or failed."""
elapsed = 0.0
while elapsed < timeout:
status_resp = await self.get_status(job_id)
if status_resp.status in {"completed", "failed"}:
return status_resp
await asyncio.sleep(poll_interval)
elapsed += poll_interval
raise TimeoutError(f"Rendering job {job_id} did not finish within {timeout} seconds")