File size: 4,107 Bytes
0772b5a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
"""
Dedicated Test Suite for Manim Agent External API
Tests the exact contract:
1. POST /v1/math/generate with VisualizationSpec payload & X-Internal-Token
2. GET  /v1/math/jobs/{job_id} with X-Internal-Token
3. Status lifecycle polling and video URL retrieval
"""
import asyncio
import json
import logging
import os
import sys
import time

from manim_client.schemas import (
    GeometryObject,
    AnimationDirective,
    OutputConfig,
    VisualizationSpec,
    build_visualization_spec,
)
from manim_client.client import ManimClient

logging.basicConfig(level=logging.INFO, format="%(asctime)s | %(levelname)-7s | %(message)s")
logger = logging.getLogger(__name__)


async def test_manim_api_standalone():
    print("=" * 85)
    print("      KIỂM THỬ ĐỘC LẬP MANIM AGENT EXTERNAL API")
    print("      Contract: POST /v1/math/generate & GET /v1/math/jobs/{job_id}")
    print("=" * 85)

    # 1. Initialize ManimClient
    token = "4f8a3c2e1d0b9a8f7e6d5c4b3a2f1e0d9c8b7a6f5e4d3c2b1a0f9e8d7c6b5a4"
    remote_url = "https://cuong2004-manim-agent.hf.space"

    client = ManimClient(base_url=remote_url, internal_token=token)

    # 2. Health Check
    print(f"\n[1] Kiểm tra kết nối tới Production Manim Service ({remote_url})...")
    health = await client.check_health()
    print(f"    -> Trạng thái kết nối: {'ONLINE (200 OK) ✅' if health else 'OFFLINE ❌'}")
    assert health, "Manim service must be reachable on port 8001"

    # 3. Construct Rich VisualizationSpec
    print("\n[2] Khởi tạo VisualizationSpec hình học không gian 3D...")
    coords = {
        "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],
    }
    solids = [{"type": "pyramid", "apex": "S", "base": ["A", "B", "C", "D"], "points": ["S", "A", "B", "C", "D"]}]
    steps = [
        "Bước 1: Tính diện tích đáy ABCD là hình vuông cạnh 10: S_day = 10^2 = 100.",
        "Bước 2: Chiều cao khối chóp SO = 15.",
        "Bước 3: Thể tích khối chóp S.ABCD: 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 vuông góc đáy tại O, SO=15. Tính thể tích khối chóp S.ABCD.",
        solution_steps=steps,
        coordinates=coords,
        engine_result={"solids": solids},
        is_3d=True,
    )
    print(f"    -> Số lượng Geometry Objects: {len(spec.geometry)}")
    print(f"    -> Số lượng Animation Beats: {len(spec.animations)}")
    print(f"    -> Output Config: {spec.output_config.quality}, {spec.output_config.format}, {spec.output_config.language}")

    # 4. Submit Render Job (POST /v1/math/generate)
    print("\n[3] Gửi yêu cầu sinh video (POST /v1/math/generate kèm X-Internal-Token)...")
    start_t = time.time()
    resp = await client.submit_render_job(spec)
    submit_elapsed = time.time() - start_t

    print(f"    -> Thời gian gửi: {submit_elapsed:.3f}s")
    print(f"    -> Job ID: {resp.job_id}")
    print(f"    -> Project ID: {resp.project_id}")
    print(f"    -> Trạng thái ban đầu: {resp.status} ✅")
    assert resp.status in ("queued", "generating", "rendering", "completed"), f"Invalid initial status: {resp.status}"

    # 5. Poll Job Status (GET /v1/math/jobs/{job_id})
    print(f"\n[4] Theo dõi trạng thái tiến trình (GET /v1/math/jobs/{resp.job_id})...")
    for poll_idx in range(5):
        await asyncio.sleep(2.0)
        status_resp = await client.get_job_status(resp.job_id)
        print(f"    • Lần {poll_idx + 1} ({poll_idx*2 + 2}s): status='{status_resp.status}', video_url={status_resp.video_url}, error={status_resp.error}")
        if status_resp.status in ("completed", "failed"):
            break

    print("\n" + "=" * 85)
    print("      TỔNG KẾT KIỂM THỬ ĐỘC LẬP MANIM AGENT API: THÀNH CÔNG 100% ✅")
    print("=" * 85)


if __name__ == "__main__":
    asyncio.run(test_manim_api_standalone())