Spaces:
Running
Running
| """End-to-End Test for Manim Video Generation Module Integration.""" | |
| import asyncio | |
| import json | |
| import logging | |
| import sys | |
| from manim_client.schemas import ( | |
| GeometryObject, | |
| AnimationDirective, | |
| OutputConfig, | |
| VisualizationSpec, | |
| build_visualization_spec, | |
| ) | |
| from manim_client.client import ManimClient | |
| from agents.orchestrator import Orchestrator | |
| logging.basicConfig(level=logging.INFO, format="%(asctime)s | %(levelname)-7s | %(message)s") | |
| logger = logging.getLogger(__name__) | |
| async def test_visualization_spec_building(): | |
| logger.info("================================================================================") | |
| logger.info("🧪 TEST 1: VisualizationSpec Building & Prompt Serialization") | |
| logger.info("================================================================================") | |
| coords_3d = { | |
| "S": [0.0, 0.0, 15.0], | |
| "A": [-5.0, -5.0, 0.0], | |
| "B": [5.0, -5.0, 0.0], | |
| "C": [5.0, 5.0, 0.0], | |
| "D": [-5.0, 5.0, 0.0], | |
| "O": [0.0, 0.0, 0.0], | |
| } | |
| engine_mock = { | |
| "solids": [{"type": "pyramid", "apex": "S", "base": ["A", "B", "C", "D"], "points": ["S", "A", "B", "C", "D"]}], | |
| "drawing_phases": [ | |
| {"phase": 1, "label": "Hình cơ bản", "points": ["A", "B", "C", "D"], "segments": [["A", "B"], ["B", "C"], ["C", "D"], ["D", "A"]]}, | |
| {"phase": 2, "label": "Điểm và đoạn phụ", "points": ["S", "O"], "segments": [["S", "A"], ["S", "B"], ["S", "C"], ["S", "D"]]}, | |
| ], | |
| } | |
| steps = [ | |
| "Bước 1: Tính diện tích đáy ABCD là hình vuông: S = 10^2 = 100.", | |
| "Bước 2: Xác định chiều cao SO = 15.", | |
| "Bước 3: Áp dụng công thức thể tích khối chóp V = (1/3) * 100 * 15 = 500.", | |
| ] | |
| spec = build_visualization_spec( | |
| problem_text="Cho hình chóp S.ABCD có đáy ABCD là hình vuông cạnh 10, SO=15. Tính thể tích khối chóp.", | |
| solution_steps=steps, | |
| coordinates=coords_3d, | |
| engine_result=engine_mock, | |
| is_3d=True, | |
| ) | |
| prompt = spec.to_prompt() | |
| logger.info(f"✅ Spec built successfully with {len(spec.geometry)} geometry objects and {len(spec.animations)} animation beats.") | |
| logger.info(f"Generated Spec Prompt preview:\n{prompt[:300]}...\n") | |
| assert len(spec.geometry) >= 6, "Missing geometry points" | |
| assert len(spec.animations) >= 3, "Missing animation beats" | |
| assert spec.output_config.quality == "720p" | |
| return spec | |
| async def test_manim_client_offline_resilience(spec: VisualizationSpec): | |
| logger.info("================================================================================") | |
| logger.info("🧪 TEST 2: ManimClient Resilience & Contract Handling") | |
| logger.info("================================================================================") | |
| # Test with standard config | |
| client = ManimClient() | |
| resp = await client.submit_render_job(spec) | |
| logger.info(f"Manim service response: job_id={resp.job_id}, status={resp.status}, error={resp.error}") | |
| assert resp.status in ("queued", "generating", "rendering", "completed", "failed"), "Invalid status" | |
| logger.info("✅ ManimClient handled external API submission gracefully without throwing unhandled exceptions.") | |
| async def test_e2e_orchestrator_integration(): | |
| logger.info("================================================================================") | |
| logger.info("🧪 TEST 3: Full E2E Pipeline (OCR/Problem -> Solver -> VisualizationSpec -> Manim)") | |
| logger.info("================================================================================") | |
| problem = "Cho hình chóp S.ABCD có đáy ABCD là hình vuông cạnh 10. Chiều cao SO vuông góc với đáy tại tâm O, SO=15. Tính thể tích khối chóp S.ABCD." | |
| orchestrator = Orchestrator() | |
| res = await orchestrator.run( | |
| text=problem, | |
| job_id="test_manim_e2e", | |
| generate_video=True, | |
| ) | |
| logger.info(f"Pipeline Result Status: {res.get('status')}") | |
| logger.info(f"Answer: {res.get('solution', {}).get('answer')}") | |
| viz = res.get("visualization") | |
| logger.info(f"Visualization Section Present: {viz is not None}") | |
| if viz: | |
| logger.info(f" - Job ID: {viz.get('job_id')}") | |
| logger.info(f" - Status: {viz.get('status')}") | |
| logger.info(f" - Spec Problem: {viz.get('spec', {}).get('problem')}") | |
| logger.info(f" - Animation beats: {len(viz.get('spec', {}).get('animations', []))}") | |
| logger.info(f" - Geometry objects: {len(viz.get('spec', {}).get('geometry', []))}") | |
| assert res.get("status") == "success" | |
| assert viz is not None | |
| assert viz.get("spec") is not None | |
| logger.info("✅ E2E Manim Integration Test Succeeded 100%!") | |
| async def main(): | |
| spec = await test_visualization_spec_building() | |
| await test_manim_client_offline_resilience(spec) | |
| await test_e2e_orchestrator_integration() | |
| logger.info("\n🎉 ALL MANIM INTEGRATION TESTS PASSED!") | |
| if __name__ == "__main__": | |
| asyncio.run(main()) | |