thanhkt commited on
Commit
b684a16
·
verified ·
1 Parent(s): 04a1880

Upload 2 files

Browse files
Files changed (2) hide show
  1. Dockerfile +55 -66
  2. ai_agent.py +831 -26
Dockerfile CHANGED
@@ -1,67 +1,56 @@
1
- FROM python:3.12-slim
2
-
3
- # Install all dependencies in one layer
4
- RUN apt-get update && apt-get install -y --no-install-recommends \
5
- gcc \
6
- libffi-dev \
7
- curl \
8
- ca-certificates \
9
- python3-dev \
10
- pkg-config \
11
- libcairo2-dev \
12
- libpango1.0-dev \
13
- ffmpeg \
14
- texlive-full \
15
- dvisvgm \
16
- fonts-dejavu \
17
- && apt-get clean \
18
- && rm -rf /var/lib/apt/lists/* \
19
- && which dvisvgm
20
-
21
- WORKDIR /app
22
-
23
- ADD https://astral.sh/uv/install.sh /uv-installer.sh
24
- RUN sh /uv-installer.sh && rm /uv-installer.sh
25
- ENV PATH="/root/.local/bin:${PATH}"
26
-
27
- # Copy virtual environment and project files
28
- COPY requirements.txt .
29
-
30
- RUN uv venv /app/manimations \
31
- && . /app/manimations/bin/activate \
32
- && uv pip install --no-cache -r requirements.txt \
33
- && uv pip install --no-cache pycairo pangocffi manim
34
-
35
- # Add manim to PATH
36
- ENV PATH="/app/manimations/bin:${PATH}"
37
-
38
- # Copy requirements and install dependencies
39
- COPY requirements.txt .
40
- RUN uv pip install --no-cache -r requirements.txt \
41
- && uv pip install --no-cache pycairo pangocffi manim
42
-
43
- ENV PYTHONPATH=/app
44
- ENV MPLBACKEND=Agg
45
- ENV GRADIO_SERVER_NAME=0.0.0.0
46
- ENV GRADIO_SERVER_PORT=7860
47
- ENV TOGETHER_API_KEY=cee1393e4d4e7a94121882052a03f30a1d51f5dbd251140844ec616e17f60e9b
48
-
49
- # Copy application code
50
- COPY . /app/
51
-
52
- # Create the appuser user and group
53
- RUN groupadd -r appuser && \
54
- useradd -r -g appuser -m appuser
55
-
56
- # Create directory for generated videos with proper permissions
57
- RUN mkdir -p /app/generated_videos && \
58
- chown -R appuser:appuser /app
59
-
60
- # Switch to non-root user
61
- USER appuser
62
-
63
- # Expose the port for the Gradio interface
64
- EXPOSE 7860
65
-
66
- # Command to run the application with the virtual environment
67
  CMD ["/app/manimations/bin/python", "ai_agent.py"]
 
1
+ FROM python:3.12-slim
2
+
3
+ # Install all dependencies in one layer
4
+ RUN apt-get update && apt-get install -y --no-install-recommends \
5
+ gcc \
6
+ libffi-dev \
7
+ curl \
8
+ ca-certificates \
9
+ python3-dev \
10
+ pkg-config \
11
+ libcairo2-dev \
12
+ libpango1.0-dev \
13
+ ffmpeg \
14
+ texlive-full \
15
+ dvisvgm \
16
+ fonts-dejavu \
17
+ && apt-get clean \
18
+ && rm -rf /var/lib/apt/lists/* \
19
+ && which dvisvgm
20
+
21
+ WORKDIR /app
22
+
23
+ # Install uv for faster pip operations
24
+ ADD https://astral.sh/uv/install.sh /uv-installer.sh
25
+ RUN sh /uv-installer.sh && rm /uv-installer.sh
26
+ ENV PATH="/root/.local/bin:${PATH}"
27
+
28
+ # Copy virtual environment and project files
29
+ COPY requirements.txt .
30
+
31
+
32
+ # Activate the existing venv and install any missing packages
33
+ RUN uv venv /app/manimations \
34
+ && . /app/manimations/bin/activate \
35
+ && uv pip install --no-cache -r requirements.txt \
36
+ && uv pip install --no-cache pycairo pangocffi manim
37
+
38
+ ENV PATH="/app/manimations/bin:${PATH}"
39
+ COPY *.py /app/
40
+ # Set environment variables
41
+ ENV PYTHONPATH=/app
42
+ ENV MPLBACKEND=Agg
43
+ ENV GRADIO_SERVER_NAME=0.0.0.0
44
+ ENV GRADIO_SERVER_PORT=7860
45
+
46
+ # Create directory for generated videos
47
+ RUN mkdir -p /app/generated_videos
48
+
49
+ # Copy .env file
50
+ COPY .env /app/.env
51
+
52
+ # Expose the port for the Gradio interface
53
+ EXPOSE 7860
54
+
55
+ # Command to run the application with the virtual environment
 
 
 
 
 
 
 
 
 
 
 
56
  CMD ["/app/manimations/bin/python", "ai_agent.py"]
ai_agent.py CHANGED
@@ -49,6 +49,27 @@ class AnimationResult(BaseModel):
49
  code: str = Field(..., description="Generated Manim code")
50
  video_path: str = Field(..., description="Path to the generated video file")
51
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  model = OpenAIModel(
53
  'deepseek-ai/DeepSeek-V3',
54
  provider=OpenAIProvider(
@@ -66,6 +87,30 @@ manim_agent = Agent(
66
  ),
67
  )
68
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
  # Configure OpenAI client to use Together API
70
  client = openai.OpenAI(
71
  api_key=os.environ.get("TOGETHER_API_KEY"),
@@ -98,6 +143,50 @@ def add_timestamp() -> str:
98
  """Add a timestamp to the system prompt."""
99
  return f"Current date and time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
100
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  @manim_agent.tool
102
  def extract_scenario(ctx: RunContext[AnimationPrompt]) -> AnimationScenario:
103
  """Extract a structured animation scenario from a text prompt."""
@@ -273,6 +362,302 @@ def generate_code(ctx: RunContext[AnimationPrompt], scenario: AnimationScenario)
273
  )
274
  return response.choices[0].message.content
275
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
276
  @manim_agent.tool_plain
277
  def render_animation(code: str, quality="medium_quality") -> str:
278
  """Render Manim code into a video. This doesn't need the context."""
@@ -461,14 +846,23 @@ memory = ConversationMemory()
461
  def refine_animation(code: str, feedback: str, quality: str = "medium_quality") -> tuple:
462
  """Refine animation based on user feedback."""
463
  try:
464
- # Get context from memory
465
- context = memory.get_context_for_refinement()
466
-
467
- # Use LLM to refine the code based on feedback
468
- response = client.chat.completions.create(
469
- model=llm,
470
- messages=[
471
- {"role": "system", "content": """
 
 
 
 
 
 
 
 
 
472
  You are a Manim code expert. Your task is to refine animation code based on user feedback.
473
  Keep the overall structure and purpose of the animation, but implement the changes requested.
474
  Make sure the code remains valid and follows Manim best practices.
@@ -478,20 +872,21 @@ IMPORTANT REQUIREMENTS:
478
  2. Ensure class name and structure remains consistent
479
  3. All changes must be compatible with Manim Community edition
480
  4. Do not explain your changes in comments outside of helpful inline comments
481
- """},
482
- {"role": "user", "content": f"Here is the current Manim animation code:\n\n```python\n{code}\n```\n\n{context}\nPlease refine this code based on this feedback: \"{feedback}\"\n\nReturn only the improved code."}
483
- ]
484
- )
485
-
486
- refined_code = response.choices[0].message.content.strip()
487
-
488
- # Remove any markdown code formatting if present
489
- if refined_code.startswith("```python"):
490
- refined_code = refined_code.split("```python", 1)[1]
491
- if refined_code.endswith("```"):
492
- refined_code = refined_code.rsplit("```", 1)[0]
493
-
494
- refined_code = refined_code.strip()
 
495
 
496
  # Render the refined code
497
  video_path = render_manim_video(refined_code, quality)
@@ -602,14 +997,26 @@ VISUAL STRUCTURE AND LAYOUT:
602
  1. Structure the animation as a narrative with clear sections (introduction, explanation, conclusion)
603
  2. Create title screens with engaging typography and animations
604
  3. Position ALL elements with EXPLICIT coordinates using shift() or move_to() methods
605
- 4. Ensure AT LEAST 1.5 units of space between separate visual elements
606
  5. For equations, use MathTex with proper scaling (scale(0.8) for complex equations)
607
  6. Group related objects using VGroup and arrange them with arrange() method
608
  7. When showing multiple equations, use arrange_in_grid() or arrange() with DOWN/RIGHT
609
  8. For graphs, set explicit x_range and y_range with generous padding around functions
610
  9. Scale ALL text elements appropriately (Title: 1.2, Headers: 1.0, Body: 0.8)
611
  10. Use colors consistently and meaningfully (BLUE for emphasis, RED for important points)
612
- 11. Preventing overlaps of element, choose position for each element carefully, display element and text then move to next element
 
 
 
 
 
 
 
 
 
 
 
 
613
 
614
  ANIMATION TECHNIQUES:
615
  1. Use FadeIn for introductions of new elements
@@ -617,7 +1024,7 @@ ANIMATION TECHNIQUES:
617
  3. Use Create for drawing geometric objects
618
  4. Implement smooth transitions between different concepts with ReplacementTransform
619
  5. Highlight important parts with Indicate or Circumscribe
620
- 6. Add pauses (self.wait()) after important points for comprehension
621
  7. For complex animations, break them into smaller steps with appropriate timing
622
  8. Use MoveAlongPath for demonstrating motion or change over time
623
  9. Create emphasis with scale_about_point or succession of animations
@@ -642,7 +1049,224 @@ RESPOND WITH CLEAN, WELL-STRUCTURED CODE ONLY. DO NOT INCLUDE EXPLANATIONS OUTSI
642
  {"role": "user", "content": f"Create a comprehensive Manim animation for '{scenario.title}' that teaches this concept: '{prompt}'. \n\nUse these mathematical objects: {objects_str}. \nImplement these transformations/animations: {transformations_str}. \nFeature these equations: {equations_str}. \n\nComplexity level: {complexity}. \n\nEnsure all elements are properly spaced and positioned to prevent overlap. Structure the animation with a clear introduction, step-by-step explanation, and conclusion."}
643
  ]
644
  )
645
- return response.choices[0].message.content
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
646
 
647
  # Function to re-render animation with edited code
648
  def rerender_animation(edited_code: str, quality: str = "medium_quality") -> tuple:
@@ -665,6 +1289,152 @@ def gradio_interface(prompt: str, complexity: str = "medium", quality: str = "me
665
  else:
666
  return code, None, log_output
667
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
668
  # Replace the Gradio interface creation with a Blocks interface for better layout control
669
  if __name__ == "__main__":
670
  with gr.Blocks(title="Manimation Generator", theme=gr.themes.Base()) as demo:
@@ -707,6 +1477,17 @@ if __name__ == "__main__":
707
  label="Your Feedback"
708
  )
709
  refine_btn = gr.Button("Apply Feedback", variant="secondary")
 
 
 
 
 
 
 
 
 
 
 
710
 
711
  # Code editor (common to both tabs)
712
  code_output = gr.Code(
@@ -766,6 +1547,23 @@ if __name__ == "__main__":
766
  )
767
  return video_path, log, new_history
768
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
769
  # Connect the components to the function
770
  generate_btn.click(
771
  fn=generate_and_update_chat,
@@ -785,6 +1583,12 @@ if __name__ == "__main__":
785
  outputs=[video_output, log_output, chat_history]
786
  )
787
 
 
 
 
 
 
 
788
  # Add footer with social media links
789
  with gr.Row(equal_height=True):
790
  gr.Markdown("""
@@ -799,3 +1603,4 @@ if __name__ == "__main__":
799
 
800
  demo.launch(server_name="0.0.0.0", server_port=7860)
801
 
 
 
49
  code: str = Field(..., description="Generated Manim code")
50
  video_path: str = Field(..., description="Path to the generated video file")
51
 
52
+ # New layout configuration model
53
+ class LayoutConfiguration(BaseModel):
54
+ """Configuration for layout optimization of animation elements."""
55
+ min_spacing: float = Field(0.5, description="Minimum spacing between elements in Manim units")
56
+ vertical_alignment: List[str] = Field(["TOP", "CENTER", "BOTTOM"], description="Vertical alignment options")
57
+ horizontal_alignment: List[str] = Field(["LEFT", "CENTER", "RIGHT"], description="Horizontal alignment options")
58
+ staggered_animations: bool = Field(True, description="Whether to stagger animations for better clarity")
59
+ screen_regions: List[str] = Field(["UL", "UP", "UR", "LEFT", "CENTER", "RIGHT", "DL", "DOWN", "DR"],
60
+ description="Screen regions for element positioning")
61
+ canvas_size: tuple = Field((14, 8), description="Canvas size in Manim units (width, height)")
62
+
63
+ # New evaluation result model
64
+ class EvaluationResult(BaseModel):
65
+ """Results of code evaluation."""
66
+ has_errors: bool = Field(False, description="Whether the code has any errors")
67
+ syntax_errors: List[str] = Field([], description="Syntax errors found in the code")
68
+ positioning_issues: List[str] = Field([], description="Issues with element positioning")
69
+ overlap_issues: List[str] = Field([], description="Potential element overlaps")
70
+ suggestions: List[str] = Field([], description="Suggestions for improvement")
71
+ fixed_code: Optional[str] = Field(None, description="Fixed code if available")
72
+
73
  model = OpenAIModel(
74
  'deepseek-ai/DeepSeek-V3',
75
  provider=OpenAIProvider(
 
87
  ),
88
  )
89
 
90
+ # Create a layout optimization agent
91
+ layout_agent = Agent(
92
+ model,
93
+ deps_type=AnimationPrompt,
94
+ system_prompt=(
95
+ "You are a specialized AI agent for optimizing layout and animations in Manim code. "
96
+ "Your goal is to analyze and improve element positioning, prevent overlaps, "
97
+ "and create step-by-step animations that are clear and educational. "
98
+ "You understand how to use Manim's coordinate system and positioning methods effectively."
99
+ ),
100
+ )
101
+
102
+ # Create an evaluation agent
103
+ evaluation_agent = Agent(
104
+ model,
105
+ deps_type=AnimationPrompt,
106
+ system_prompt=(
107
+ "You are a specialized AI agent for evaluating Manim animation code. "
108
+ "Your goal is to detect errors, check for proper element positioning, "
109
+ "and ensure the code follows best practices for clear mathematical animations. "
110
+ "You understand Manim's syntax and common pitfalls in animation creation."
111
+ ),
112
+ )
113
+
114
  # Configure OpenAI client to use Together API
115
  client = openai.OpenAI(
116
  api_key=os.environ.get("TOGETHER_API_KEY"),
 
143
  """Add a timestamp to the system prompt."""
144
  return f"Current date and time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
145
 
146
+ @layout_agent.system_prompt
147
+ def add_layout_guidance(ctx: RunContext[AnimationPrompt]) -> str:
148
+ """Add layout guidance based on complexity."""
149
+ complexity = ctx.deps.complexity
150
+ if complexity == "simple":
151
+ return (
152
+ "Optimize layout for simple animations with minimal elements. "
153
+ "Use large spacing and clear positioning. "
154
+ "Each step should be very distinct and have ample wait time between transitions."
155
+ )
156
+ elif complexity == "complex":
157
+ return (
158
+ "Optimize layout for complex animations with many elements. "
159
+ "Use thoughtful positioning with elements grouped by relevance. "
160
+ "Break animations into logical steps with clear transitions between concepts."
161
+ )
162
+ else: # medium
163
+ return (
164
+ "Balance spacing and density in your layout. "
165
+ "Position elements with sufficient spacing while utilizing screen space efficiently. "
166
+ "Present animations in a step-by-step manner with appropriate timing."
167
+ )
168
+
169
+ @evaluation_agent.system_prompt
170
+ def add_evaluation_guidance(ctx: RunContext[AnimationPrompt]) -> str:
171
+ """Add evaluation guidance based on complexity."""
172
+ complexity = ctx.deps.complexity
173
+ if complexity == "simple":
174
+ return (
175
+ "Focus on finding basic errors and ensuring clear positioning of minimal elements. "
176
+ "Simple animations should have ample spacing and no overlapping elements."
177
+ )
178
+ elif complexity == "complex":
179
+ return (
180
+ "Look for subtle issues in complex code, including potential element overlaps "
181
+ "when multiple transformations occur. Check for proper timing between steps "
182
+ "and verify that complex mathematical notations are correctly formatted."
183
+ )
184
+ else: # medium
185
+ return (
186
+ "Balance between checking for technical errors and verifying good animation principles. "
187
+ "Ensure elements are properly spaced and animations follow a logical step-by-step flow."
188
+ )
189
+
190
  @manim_agent.tool
191
  def extract_scenario(ctx: RunContext[AnimationPrompt]) -> AnimationScenario:
192
  """Extract a structured animation scenario from a text prompt."""
 
362
  )
363
  return response.choices[0].message.content
364
 
365
+ @layout_agent.tool
366
+ def analyze_element_layout(ctx: RunContext[AnimationPrompt], code: str) -> dict:
367
+ """Analyze Manim code for potential layout issues and element positioning."""
368
+ prompt = ctx.deps
369
+
370
+ response = client.chat.completions.create(
371
+ model=llm,
372
+ messages=[
373
+ {"role": "system", "content": """
374
+ Analyze Manim code for layout issues and element positioning. Look for:
375
+ 1. Overlapping elements or text
376
+ 2. Elements positioned too close to each other
377
+ 3. Elements positioned off-screen or at extreme edges
378
+ 4. Poor use of screen space
379
+ 5. Too many elements appearing simultaneously
380
+ 6. Lack of clear positioning commands
381
+
382
+ Respond with a JSON object containing:
383
+ - issues: List of detected layout issues
384
+ - suggestions: List of positioning improvements
385
+ - animation_flow: List of animation sequence improvements
386
+ - spacing: Suggested minimum spacing between elements
387
+ - regions: Suggested screen regions to use for key elements
388
+ """
389
+ },
390
+ {"role": "user", "content": f"Analyze this Manim code for layout issues:\n\n```python\n{code}\n```\n\nPrompt: {prompt.description}, Complexity: {prompt.complexity}"}
391
+ ]
392
+ )
393
+
394
+ content = response.choices[0].message.content
395
+
396
+ try:
397
+ # Extract JSON from response
398
+ json_match = re.search(r'\{.*\}', content, re.DOTALL)
399
+ if json_match:
400
+ json_str = json_match.group(0)
401
+ analysis = json.loads(json_str)
402
+ return analysis
403
+ except Exception as e:
404
+ logger.error(f"Error parsing layout analysis: {e}")
405
+
406
+ # Fallback with default values
407
+ return {
408
+ "issues": ["Potential element overlap", "Undefined positioning"],
409
+ "suggestions": ["Use explicit coordinates for all elements", "Add spacing between elements"],
410
+ "animation_flow": ["Break complex animations into steps", "Add wait time between steps"],
411
+ "spacing": 1.0,
412
+ "regions": ["UP", "DOWN", "LEFT", "RIGHT", "CENTER"]
413
+ }
414
+
415
+ @layout_agent.tool
416
+ def optimize_layout(ctx: RunContext[AnimationPrompt], code: str, analysis: dict) -> str:
417
+ """Optimize the layout of elements in Manim code."""
418
+ prompt = ctx.deps
419
+
420
+ # Serialize the analysis for the prompt
421
+ analysis_str = json.dumps(analysis, indent=2)
422
+
423
+ response = client.chat.completions.create(
424
+ model=llm,
425
+ messages=[
426
+ {"role": "system", "content": """
427
+ Optimize the layout and animation flow in Manim code. Follow these rules:
428
+ 1. Explicitly position ALL elements with coordinates (e.g., .move_to(), .shift(), .to_edge())
429
+ 2. Ensure minimum spacing (1.0 units) between all elements
430
+ 3. Use screen regions effectively (UP, DOWN, LEFT, RIGHT, UL, UR, DL, DR)
431
+ 4. Group related elements using VGroup and arrange them logically
432
+ 5. Break complex animations into steps with self.wait() between them
433
+ 6. Use sequential animations for clarity (one concept at a time)
434
+ 7. Use consistent positioning and transitions throughout the animation
435
+ 8. Add comments explaining positioning choices
436
+
437
+ Preserve all mathematical content and educational purpose of the animation.
438
+ Only make changes to improve layout, positioning, and animation flow.
439
+ """
440
+ },
441
+ {"role": "user", "content": f"Original code:\n\n```python\n{code}\n```\n\nOptimize the layout based on this analysis:\n{analysis_str}\n\nPrompt: {prompt.description}, Complexity: {prompt.complexity}\n\nReturn the optimized code that fixes all layout issues."}
442
+ ]
443
+ )
444
+
445
+ optimized_code = response.choices[0].message.content
446
+
447
+ # Clean up the response to extract just the code
448
+ if "```python" in optimized_code:
449
+ optimized_code = optimized_code.split("```python", 1)[1]
450
+ if "```" in optimized_code:
451
+ optimized_code = optimized_code.split("```", 1)[0]
452
+
453
+ return optimized_code.strip()
454
+
455
+ @evaluation_agent.tool
456
+ def check_syntax_errors(ctx: RunContext[AnimationPrompt], code: str) -> List[str]:
457
+ """Check for Python and Manim-specific syntax errors."""
458
+ prompt = ctx.deps
459
+
460
+ response = client.chat.completions.create(
461
+ model=llm,
462
+ messages=[
463
+ {"role": "system", "content": """
464
+ Analyze this Manim code for syntax errors and logical mistakes. Look for:
465
+
466
+ 1. Python syntax errors (missing colons, parentheses, indentation problems)
467
+ 2. Manim-specific errors (incorrect class usage, invalid animation methods)
468
+ 3. Undefined variables or objects that are used before definition
469
+ 4. Incorrect parameter types or values
470
+ 5. Missing imports or misused Manim classes
471
+ 6. LaTeX syntax errors in MathTex objects
472
+ 7. Animation errors (using wrong objects in animations, incorrect method calls)
473
+
474
+ For each error found, provide:
475
+ 1. The line number or code region with the error
476
+ 2. A description of what's wrong
477
+ 3. A suggested fix
478
+
479
+ Be thorough but only focus on actual errors, not style issues.
480
+ """
481
+ },
482
+ {"role": "user", "content": f"Check this Manim code for syntax errors:\n\n```python\n{code}\n```\n\nPrompt: {prompt.description}, Complexity: {prompt.complexity}"}
483
+ ]
484
+ )
485
+
486
+ # Extract errors from response
487
+ error_content = response.choices[0].message.content
488
+ error_lines = error_content.split('\n')
489
+
490
+ # Filter for actual errors
491
+ errors = []
492
+ current_error = ""
493
+ for line in error_lines:
494
+ if line.strip().startswith(("Error", "Issue", "Problem", "Bug", "Line", "1.", "2.", "3.", "4.", "5.")):
495
+ if current_error:
496
+ errors.append(current_error.strip())
497
+ current_error = line.strip()
498
+ elif current_error and line.strip():
499
+ current_error += " " + line.strip()
500
+
501
+ # Add the last error if there is one
502
+ if current_error:
503
+ errors.append(current_error.strip())
504
+
505
+ return errors
506
+
507
+ @evaluation_agent.tool
508
+ def check_positioning(ctx: RunContext[AnimationPrompt], code: str) -> dict:
509
+ """Check for proper positioning and potential overlaps in the animation."""
510
+ prompt = ctx.deps
511
+
512
+ response = client.chat.completions.create(
513
+ model=llm,
514
+ messages=[
515
+ {"role": "system", "content": """
516
+ Analyze this Manim code specifically for positioning and spacing issues. Look for:
517
+
518
+ 1. Objects without explicit position commands (move_to, shift, to_edge, etc.)
519
+ 2. Elements that might overlap based on their coordinates
520
+ 3. Text or equations positioned too close to each other
521
+ 4. Elements positioned too close to the edge of the screen
522
+ 5. Improper grouping of related elements
523
+ 6. Elements with undefined positioning that might appear at origin (0,0)
524
+ 7. Animations where multiple elements move to the same location
525
+
526
+ Analyze the coordinates and create a mental map of where objects are positioned.
527
+ Flag any positions where elements might overlap or be too close (less than 1.0 units apart).
528
+
529
+ Respond with a JSON object containing:
530
+ - positioning_issues: List of positioning problems found
531
+ - overlap_issues: List of specific coordinates or elements that might overlap
532
+ - suggestions: Specific suggestions to improve positioning
533
+ """
534
+ },
535
+ {"role": "user", "content": f"Analyze this Manim code for positioning and spacing issues:\n\n```python\n{code}\n```\n\nPrompt: {prompt.description}, Complexity: {prompt.complexity}"}
536
+ ]
537
+ )
538
+
539
+ content = response.choices[0].message.content
540
+
541
+ try:
542
+ # Extract JSON from response
543
+ json_match = re.search(r'\{.*\}', content, re.DOTALL)
544
+ if json_match:
545
+ json_str = json_match.group(0)
546
+ positioning_analysis = json.loads(json_str)
547
+ return positioning_analysis
548
+ except Exception as e:
549
+ logger.error(f"Error parsing positioning analysis: {e}")
550
+
551
+ # If no valid JSON is found, extract information manually
552
+ positioning_issues = []
553
+ overlap_issues = []
554
+ suggestions = []
555
+
556
+ # Simple pattern matching to extract issues
557
+ for line in content.split('\n'):
558
+ line = line.strip()
559
+ if "position" in line.lower() or "coordinate" in line.lower() or "overlap" in line.lower():
560
+ if line.startswith(("- ", "* ", "1. ", "2. ")):
561
+ positioning_issues.append(line.lstrip("- *123456789. "))
562
+ if "overlap" in line.lower():
563
+ if line.startswith(("- ", "* ", "1. ", "2. ")):
564
+ overlap_issues.append(line.lstrip("- *123456789. "))
565
+ if "suggest" in line.lower() or "should" in line.lower() or "could" in line.lower():
566
+ if line.startswith(("- ", "* ", "1. ", "2. ")):
567
+ suggestions.append(line.lstrip("- *123456789. "))
568
+
569
+ return {
570
+ "positioning_issues": positioning_issues,
571
+ "overlap_issues": overlap_issues,
572
+ "suggestions": suggestions
573
+ }
574
+
575
+ @evaluation_agent.tool
576
+ def fix_code_issues(ctx: RunContext[AnimationPrompt], code: str, syntax_errors: List[str], positioning_issues: dict) -> str:
577
+ """Fix detected issues in the code."""
578
+ prompt = ctx.deps
579
+
580
+ # Format issues for the prompt
581
+ syntax_errors_str = "\n".join([f"- {error}" for error in syntax_errors])
582
+
583
+ positioning_issues_str = ""
584
+ if "positioning_issues" in positioning_issues:
585
+ positioning_issues_str += "\nPositioning Issues:\n" + "\n".join([f"- {issue}" for issue in positioning_issues["positioning_issues"]])
586
+
587
+ if "overlap_issues" in positioning_issues:
588
+ positioning_issues_str += "\nOverlap Issues:\n" + "\n".join([f"- {issue}" for issue in positioning_issues["overlap_issues"]])
589
+
590
+ if "suggestions" in positioning_issues:
591
+ positioning_issues_str += "\nSuggestions:\n" + "\n".join([f"- {suggestion}" for suggestion in positioning_issues["suggestions"]])
592
+
593
+ response = client.chat.completions.create(
594
+ model=llm,
595
+ messages=[
596
+ {"role": "system", "content": """
597
+ Fix the provided Manim code by addressing all identified issues. Follow these guidelines:
598
+
599
+ 1. Fix all syntax errors and logical mistakes first
600
+ 2. Fix positioning issues by adding explicit positioning commands
601
+ 3. Resolve element overlaps by repositioning elements with adequate spacing
602
+ 4. Implement all positioning suggestions to improve clarity
603
+ 5. Maintain the original educational intent and mathematical content
604
+ 6. Ensure all animations follow a logical step-by-step flow
605
+ 7. Add comments explaining your fixes for complex changes
606
+
607
+ Return the complete, corrected code ready for rendering.
608
+ """
609
+ },
610
+ {"role": "user", "content":
611
+ f"Fix the following Manim code by addressing these issues:\n\n"
612
+ f"Syntax Errors:\n{syntax_errors_str}\n\n"
613
+ f"Positioning Issues:{positioning_issues_str}\n\n"
614
+ f"Original Code:\n```python\n{code}\n```\n\n"
615
+ f"Original Prompt: {prompt.description}, Complexity: {prompt.complexity}\n\n"
616
+ f"Return the complete fixed code."
617
+ }
618
+ ]
619
+ )
620
+
621
+ fixed_code = response.choices[0].message.content
622
+
623
+ # Clean up the response to extract just the code
624
+ if "```python" in fixed_code:
625
+ fixed_code = fixed_code.split("```python", 1)[1]
626
+ if "```" in fixed_code:
627
+ fixed_code = fixed_code.split("```", 1)[0]
628
+
629
+ return fixed_code.strip()
630
+
631
+ @evaluation_agent.tool
632
+ def evaluate_code(ctx: RunContext[AnimationPrompt], code: str) -> EvaluationResult:
633
+ """Evaluate Manim code for errors and positioning issues."""
634
+ # Check for syntax errors
635
+ syntax_errors = check_syntax_errors(ctx, code)
636
+
637
+ # Check for positioning issues
638
+ positioning_analysis = check_positioning(ctx, code)
639
+
640
+ positioning_issues = positioning_analysis.get("positioning_issues", [])
641
+ overlap_issues = positioning_analysis.get("overlap_issues", [])
642
+ suggestions = positioning_analysis.get("suggestions", [])
643
+
644
+ # Determine if there are errors
645
+ has_errors = len(syntax_errors) > 0 or len(positioning_issues) > 0 or len(overlap_issues) > 0
646
+
647
+ # If there are errors, fix the code
648
+ fixed_code = None
649
+ if has_errors:
650
+ fixed_code = fix_code_issues(ctx, code, syntax_errors, positioning_analysis)
651
+
652
+ return EvaluationResult(
653
+ has_errors=has_errors,
654
+ syntax_errors=syntax_errors,
655
+ positioning_issues=positioning_issues,
656
+ overlap_issues=overlap_issues,
657
+ suggestions=suggestions,
658
+ fixed_code=fixed_code
659
+ )
660
+
661
  @manim_agent.tool_plain
662
  def render_animation(code: str, quality="medium_quality") -> str:
663
  """Render Manim code into a video. This doesn't need the context."""
 
846
  def refine_animation(code: str, feedback: str, quality: str = "medium_quality") -> tuple:
847
  """Refine animation based on user feedback."""
848
  try:
849
+ # Special case for layout/positioning feedback
850
+ if any(keyword in feedback.lower() for keyword in ["position", "layout", "overlap", "spacing", "step by step", "clear"]):
851
+ # Try to apply specialized layout optimization
852
+ prompt = memory.history[-1]["prompt"] if memory.history else "Mathematical animation"
853
+ complexity = "medium" # Default complexity
854
+
855
+ refined_code = optimize_element_positioning(code, prompt, complexity)
856
+ else:
857
+ # Original feedback processing code
858
+ # Get context from memory
859
+ context = memory.get_context_for_refinement()
860
+
861
+ # Use LLM to refine the code based on feedback
862
+ response = client.chat.completions.create(
863
+ model=llm,
864
+ messages=[
865
+ {"role": "system", "content": """
866
  You are a Manim code expert. Your task is to refine animation code based on user feedback.
867
  Keep the overall structure and purpose of the animation, but implement the changes requested.
868
  Make sure the code remains valid and follows Manim best practices.
 
872
  2. Ensure class name and structure remains consistent
873
  3. All changes must be compatible with Manim Community edition
874
  4. Do not explain your changes in comments outside of helpful inline comments
875
+ """
876
+ },
877
+ {"role": "user", "content": f"Here is the current Manim animation code:\n\n```python\n{code}\n```\n\n{context}\nPlease refine this code based on this feedback: \"{feedback}\"\n\nReturn only the improved code."}
878
+ ]
879
+ )
880
+
881
+ refined_code = response.choices[0].message.content.strip()
882
+
883
+ # Remove any markdown code formatting if present
884
+ if refined_code.startswith("```python"):
885
+ refined_code = refined_code.split("```python", 1)[1]
886
+ if refined_code.endswith("```"):
887
+ refined_code = refined_code.rsplit("```", 1)[0]
888
+
889
+ refined_code = refined_code.strip()
890
 
891
  # Render the refined code
892
  video_path = render_manim_video(refined_code, quality)
 
997
  1. Structure the animation as a narrative with clear sections (introduction, explanation, conclusion)
998
  2. Create title screens with engaging typography and animations
999
  3. Position ALL elements with EXPLICIT coordinates using shift() or move_to() methods
1000
+ 4. Ensure AT LEAST 2.0 units of space between separate visual elements
1001
  5. For equations, use MathTex with proper scaling (scale(0.8) for complex equations)
1002
  6. Group related objects using VGroup and arrange them with arrange() method
1003
  7. When showing multiple equations, use arrange_in_grid() or arrange() with DOWN/RIGHT
1004
  8. For graphs, set explicit x_range and y_range with generous padding around functions
1005
  9. Scale ALL text elements appropriately (Title: 1.2, Headers: 1.0, Body: 0.8)
1006
  10. Use colors consistently and meaningfully (BLUE for emphasis, RED for important points)
1007
+
1008
+ CRITICAL: ELEMENT MANAGEMENT AND STEP-BY-STEP REQUIREMENTS:
1009
+ 1. NEVER show too many elements on screen at once - max 3-4 related elements at any time
1010
+ 2. ALWAYS use self.play(FadeOut(element)) to explicitly remove elements when moving to a new concept
1011
+ 3. DO NOT use self.clear() as it doesn't actually remove elements from the scene
1012
+ 4. Implement strict SEQUENTIAL animation - introduce only ONE concept or element at a time
1013
+ 5. Use self.wait(0.7) to 1.5 for short pauses and self.wait(2) for important concepts
1014
+ 6. Organize the screen into distinct regions (TOP for titles, CENTER for main content, BOTTOM for explanations)
1015
+ 7. For sequential steps in derivations or proofs, use transform_matching_tex() to smoothly evolve equations
1016
+ 8. Use MoveToTarget() for repositioning elements that need to stay on screen between steps
1017
+ 9. At the end of each section, EXPLICITLY remove all elements with self.play(FadeOut(elem1, elem2, ...))
1018
+ 10. When positioning new elements, verify they won't overlap existing elements
1019
+ 11. For elements that must appear together, use VGroup but animate their creation one by one
1020
 
1021
  ANIMATION TECHNIQUES:
1022
  1. Use FadeIn for introductions of new elements
 
1024
  3. Use Create for drawing geometric objects
1025
  4. Implement smooth transitions between different concepts with ReplacementTransform
1026
  5. Highlight important parts with Indicate or Circumscribe
1027
+ 6. Add appropriate pauses: self.wait(0.7) after minor steps, self.wait(1.5) after important points
1028
  7. For complex animations, break them into smaller steps with appropriate timing
1029
  8. Use MoveAlongPath for demonstrating motion or change over time
1030
  9. Create emphasis with scale_about_point or succession of animations
 
1049
  {"role": "user", "content": f"Create a comprehensive Manim animation for '{scenario.title}' that teaches this concept: '{prompt}'. \n\nUse these mathematical objects: {objects_str}. \nImplement these transformations/animations: {transformations_str}. \nFeature these equations: {equations_str}. \n\nComplexity level: {complexity}. \n\nEnsure all elements are properly spaced and positioned to prevent overlap. Structure the animation with a clear introduction, step-by-step explanation, and conclusion."}
1050
  ]
1051
  )
1052
+
1053
+ initial_code = response.choices[0].message.content
1054
+
1055
+ # Analyze and optimize the layout using the layout agent
1056
+ try:
1057
+ # Create prompt object for context
1058
+ prompt_obj = AnimationPrompt(description=prompt, complexity=complexity)
1059
+
1060
+ # Fix: Don't pass 'code' as a keyword argument to run_sync
1061
+ # Instead, include the code in the prompt text
1062
+ layout_result = layout_agent.run_sync(
1063
+ f"Analyze and optimize the layout of this Manim code for the prompt: {prompt}\n\n"
1064
+ f"```python\n{initial_code}\n```",
1065
+ deps=prompt_obj
1066
+ )
1067
+
1068
+ # If the layout agent successfully returned optimized code, use that
1069
+ if isinstance(layout_result, str) and "from manim import" in layout_result:
1070
+ # Extract the code part if it returned markdown-formatted code
1071
+ if "```python" in layout_result:
1072
+ layout_result = layout_result.split("```python", 1)[1].split("```", 1)[0].strip()
1073
+ return layout_result
1074
+
1075
+ # Fix: Don't manually create a RunContext
1076
+ # Instead use a direct approach for optimization
1077
+ optimized_code = direct_optimize_layout(initial_code, prompt, complexity)
1078
+
1079
+ if optimized_code and "from manim import" in optimized_code:
1080
+ return optimized_code
1081
+ except Exception as e:
1082
+ logger.error(f"Error during layout optimization: {e}")
1083
+ # If optimization fails, return the initial code
1084
+
1085
+ return initial_code
1086
+
1087
+ # Add direct implementation of layout optimization functions
1088
+ def direct_analyze_layout(code: str, prompt: str, complexity: str = "medium") -> dict:
1089
+ """Analyze Manim code for layout issues without using agent tools."""
1090
+ try:
1091
+ response = client.chat.completions.create(
1092
+ model=llm,
1093
+ messages=[
1094
+ {"role": "system", "content": """
1095
+ Analyze Manim code for layout issues and element positioning. Look for:
1096
+ 1. Overlapping elements or text
1097
+ 2. Elements positioned too close to each other
1098
+ 3. Elements positioned off-screen or at extreme edges
1099
+ 4. Poor use of screen space
1100
+ 5. Too many elements appearing simultaneously
1101
+ 6. Lack of clear positioning commands
1102
+
1103
+ Respond with a JSON object containing:
1104
+ - issues: List of detected layout issues
1105
+ - suggestions: List of positioning improvements
1106
+ - animation_flow: List of animation sequence improvements
1107
+ - spacing: Suggested minimum spacing between elements
1108
+ - regions: Suggested screen regions to use for key elements
1109
+ """
1110
+ },
1111
+ {"role": "user", "content": f"Analyze this Manim code for layout issues:\n\n```python\n{code}\n```\n\nPrompt: {prompt}, Complexity: {complexity}"}
1112
+ ]
1113
+ )
1114
+
1115
+ content = response.choices[0].message.content
1116
+
1117
+ try:
1118
+ # Extract JSON from response
1119
+ json_match = re.search(r'\{.*\}', content, re.DOTALL)
1120
+ if json_match:
1121
+ json_str = json_match.group(0)
1122
+ analysis = json.loads(json_str)
1123
+ return analysis
1124
+ except Exception as e:
1125
+ logger.error(f"Error parsing layout analysis: {e}")
1126
+
1127
+ # Fallback with default values
1128
+ return {
1129
+ "issues": ["Potential element overlap", "Undefined positioning"],
1130
+ "suggestions": ["Use explicit coordinates for all elements", "Add spacing between elements"],
1131
+ "animation_flow": ["Break complex animations into steps", "Add wait time between steps"],
1132
+ "spacing": 1.0,
1133
+ "regions": ["UP", "DOWN", "LEFT", "RIGHT", "CENTER"]
1134
+ }
1135
+ except Exception as e:
1136
+ logger.error(f"Error in direct_analyze_layout: {e}")
1137
+ return {
1138
+ "issues": ["Analysis failed"],
1139
+ "suggestions": ["Check code manually"],
1140
+ "animation_flow": [],
1141
+ "spacing": 1.0,
1142
+ "regions": ["CENTER"]
1143
+ }
1144
+
1145
+ def direct_optimize_layout(code: str, prompt: str, complexity: str = "medium") -> str:
1146
+ """Optimize layout in Manim code without using agent tools."""
1147
+ try:
1148
+ # First, analyze the layout
1149
+ analysis = direct_analyze_layout(code, prompt, complexity)
1150
+
1151
+ # Serialize the analysis for the prompt
1152
+ analysis_str = json.dumps(analysis, indent=2)
1153
+
1154
+ response = client.chat.completions.create(
1155
+ model=llm,
1156
+ messages=[
1157
+ {"role": "system", "content": """
1158
+ Optimize the layout and animation flow in Manim code. Follow these strict rules:
1159
+
1160
+ ELEMENT POSITIONING AND SPACING:
1161
+ 1. Explicitly position ALL elements with coordinates (e.g., .move_to(), .shift(), .to_edge())
1162
+ 2. Ensure minimum spacing of 2.0 units between all elements
1163
+ 3. Use screen regions effectively (UP, DOWN, LEFT, RIGHT, UL, UR, DL, DR)
1164
+ 4. Group related elements using VGroup and arrange them with arrange(direction, buff=1.0)
1165
+ 5. Add buffer around elements: .move_to(point).shift(UP*0.5) to ensure spacing
1166
+ 6. Use coordinate grid to map element positions: x values from -6 to 6, y values from -3.5 to 3.5
1167
+ 7. Shrink elements to 80% size when needed with scale(0.8)
1168
+
1169
+ STEP-BY-STEP ANIMATION FLOW:
1170
+ 1. CRITICAL: Use self.play(FadeOut(element)) to explicitly remove elements when done with them
1171
+ 2. DO NOT use self.clear() as it doesn't actually remove elements from the scene
1172
+ 3. Divide the animation into clear sequences with comments like "# Step 1: Introduction"
1173
+ 4. Use appropriate wait times: self.wait(0.7) for minor steps, self.wait(1.5) for new concepts
1174
+ 5. At the end of each major section, add: self.play(FadeOut(*[all_objects_in_current_section]))
1175
+ 6. Max 3-4 elements should be visible simultaneously
1176
+ 7. For each step, state element positions clearly in comments
1177
+ 8. Use sequential animations (one element at a time) rather than AnimationGroup
1178
+
1179
+ FIXES FOR COMMON PROBLEMS:
1180
+ 1. Add .to_edge(direction) to all Tex/MathTex elements
1181
+ 2. For Title elements, always use .to_edge(UP)
1182
+ 3. For equations, use .scale(0.8).next_to(previous_element, DOWN*2)
1183
+ 4. For diagrams, center them with .move_to(ORIGIN)
1184
+ 5. For graphs, explicitly set axes ranges with x_range=[-5, 5, 1], y_range=[-3, 3, 1]
1185
+ 6. For multiple text elements, align them with .align_to(reference, direction)
1186
+ 7. For explanatory text, position at the bottom with .to_edge(DOWN)
1187
+ 8. Before introducing new sections, add: self.play(FadeOut(*[all_current_elements]))
1188
+
1189
+ Preserve all mathematical content and educational purpose of the animation.
1190
+ Only make changes to improve layout, positioning, and animation flow.
1191
+ """
1192
+ },
1193
+ {"role": "user", "content": f"Original code:\n\n```python\n{code}\n```\n\nOptimize the layout based on this analysis:\n{analysis_str}\n\nPrompt: {prompt}, Complexity: {complexity}\n\nReturn the optimized code that ensures a step-by-step animation with proper spacing and element removal to prevent overlaps."}
1194
+ ]
1195
+ )
1196
+
1197
+ optimized_code = response.choices[0].message.content
1198
+
1199
+ # Clean up the response to extract just the code
1200
+ if "```python" in optimized_code:
1201
+ optimized_code = optimized_code.split("```python", 1)[1]
1202
+ if "```" in optimized_code:
1203
+ optimized_code = optimized_code.split("```", 1)[0]
1204
+
1205
+ return optimized_code.strip()
1206
+ except Exception as e:
1207
+ logger.error(f"Error in direct_optimize_layout: {e}")
1208
+ return code # Return original code if optimization fails
1209
+
1210
+ # Add a new function to specifically check and fix positioning issues in existing code
1211
+ def optimize_element_positioning(code: str, prompt: str, complexity: str = "medium") -> str:
1212
+ """Analyze and optimize element positioning in Manim code."""
1213
+ try:
1214
+ response = client.chat.completions.create(
1215
+ model=llm,
1216
+ messages=[
1217
+ {"role": "system", "content": """
1218
+ You are a Manim layout expert. Review the provided code and improve element positioning and animation flow.
1219
+ Focus on these critical aspects:
1220
+
1221
+ 1. STEP-BY-STEP ANIMATION:
1222
+ - CRITICAL: Add explicit self.play(FadeOut(...)) to remove elements when they're no longer needed
1223
+ - DO NOT use self.clear() as it doesn't actually remove elements from the scene
1224
+ - Ensure only 3-4 related elements are visible at once
1225
+ - Sequence animations to show just one new element at a time
1226
+ - Use appropriate wait times: self.wait(0.7) for minor points, self.wait(1.5) for important concepts
1227
+
1228
+ 2. POSITIONING AND SPACING:
1229
+ - Position ALL elements with explicit coordinates: move_to(), shift(), to_edge(), etc.
1230
+ - Maintain AT LEAST 2.0 units of space between elements
1231
+ - Use all screen regions effectively: UP, DOWN, LEFT, RIGHT, UL, UR, DL, DR, etc.
1232
+ - Use coordinate grid system: x values from -6 to 6, y values from -3.5 to 3.5
1233
+ - Scale elements with .scale(0.8) when needed to prevent overlap
1234
+
1235
+ 3. ELEMENT ORGANIZATION:
1236
+ - Group related elements using VGroup and arrange them with arrange(direction, buff=1.0)
1237
+ - Position titles at the top with .to_edge(UP)
1238
+ - Position explanatory text at the bottom with .to_edge(DOWN)
1239
+ - Center diagrams with .move_to(ORIGIN)
1240
+ - For multiple text elements, use .align_to(reference, direction)
1241
+
1242
+ 4. ELEMENT CLEANUP:
1243
+ - At the end of each section, add: self.play(FadeOut(*[all_objects_in_section]))
1244
+ - For elements that transform, use ReplacementTransform not Transform
1245
+ - Keep track of all created elements and remove them when not needed
1246
+ - Add comments before element removal: "# Remove all elements from this section"
1247
+
1248
+ DO NOT change the mathematical content or educational purpose of the animation.
1249
+ Only modify layout, positioning, and animation flow to ensure a clear, step-by-step experience.
1250
+
1251
+ Return ONLY the improved code without explanations outside of code comments.
1252
+ """
1253
+ },
1254
+ {"role": "user", "content": f"Review and optimize element positioning and step-by-step flow in this Manim code:\n\n```python\n{code}\n```\n\nThe animation is about: '{prompt}' with complexity level '{complexity}'.\n\nFocus on preventing element overlap by ensuring proper spacing, explicit positioning, AND ADDING FadeOut() calls to remove elements when moving between sections. DO NOT use self.clear() since it doesn't work properly."}
1255
+ ]
1256
+ )
1257
+
1258
+ optimized_code = response.choices[0].message.content
1259
+
1260
+ # Clean up the response to extract just the code
1261
+ if "```python" in optimized_code:
1262
+ optimized_code = optimized_code.split("```python", 1)[1]
1263
+ if "```" in optimized_code:
1264
+ optimized_code = optimized_code.split("```", 1)[0]
1265
+
1266
+ return optimized_code.strip()
1267
+ except Exception as e:
1268
+ logger.error(f"Error optimizing element positioning: {e}")
1269
+ return code # Return original code if optimization fails
1270
 
1271
  # Function to re-render animation with edited code
1272
  def rerender_animation(edited_code: str, quality: str = "medium_quality") -> tuple:
 
1289
  else:
1290
  return code, None, log_output
1291
 
1292
+ # Function to evaluate and fix Manim code
1293
+ def evaluate_and_fix_manim_code(code: str, prompt: str, complexity: str = "medium") -> tuple:
1294
+ """Evaluate Manim code for errors and fix them if found."""
1295
+ try:
1296
+ # Create prompt object for context
1297
+ prompt_obj = AnimationPrompt(description=prompt, complexity=complexity)
1298
+
1299
+ # Run evaluation through the agent
1300
+ evaluation_result = evaluation_agent.run_sync(
1301
+ f"Evaluate this Manim code for the prompt: {prompt}",
1302
+ deps=prompt_obj,
1303
+ code=code
1304
+ )
1305
+
1306
+ # Check if we got a proper evaluation result
1307
+ if isinstance(evaluation_result, EvaluationResult):
1308
+ if evaluation_result.has_errors and evaluation_result.fixed_code:
1309
+ return evaluation_result.fixed_code, format_evaluation_results(evaluation_result)
1310
+ elif evaluation_result.has_errors:
1311
+ # If no fixed code was provided but errors exist, try direct fixing
1312
+ prompt_ctx = RunContext(deps=prompt_obj)
1313
+ fixed_code = fix_code_issues(
1314
+ prompt_ctx,
1315
+ code,
1316
+ evaluation_result.syntax_errors,
1317
+ {
1318
+ "positioning_issues": evaluation_result.positioning_issues,
1319
+ "overlap_issues": evaluation_result.overlap_issues,
1320
+ "suggestions": evaluation_result.suggestions
1321
+ }
1322
+ )
1323
+ return fixed_code, format_evaluation_results(evaluation_result)
1324
+ else:
1325
+ # No errors found
1326
+ return code, "## Code Evaluation\n\nNo errors or positioning issues found. Code looks good!"
1327
+
1328
+ # Fallback to direct evaluation if agent result isn't an EvaluationResult
1329
+ return direct_evaluate_and_fix(code, prompt, complexity)
1330
+
1331
+ except Exception as e:
1332
+ logger.error(f"Error during code evaluation: {e}")
1333
+ # If evaluation fails, return the original code
1334
+ return code, f"## Error During Evaluation\n\nCould not complete code evaluation: {str(e)}"
1335
+
1336
+ def direct_evaluate_and_fix(code: str, prompt: str, complexity: str = "medium") -> tuple:
1337
+ """Direct implementation of code evaluation and fixing without using agents."""
1338
+ try:
1339
+ response = client.chat.completions.create(
1340
+ model=llm,
1341
+ messages=[
1342
+ {"role": "system", "content": """
1343
+ Evaluate this Manim code for errors and positioning issues. Look for:
1344
+ 1. Python syntax errors
1345
+ 2. Manim-specific errors (incorrect class usage, invalid animation methods)
1346
+ 3. Positioning issues (elements without explicit positioning)
1347
+ 4. Potential element overlaps
1348
+ 5. Timing and animation flow issues
1349
+
1350
+ If you find any issues, fix them and return both an evaluation report and the fixed code.
1351
+ If no issues are found, say so and return the original code.
1352
+
1353
+ Format your response as:
1354
+ ```evaluation
1355
+ [List all issues found with explanations]
1356
+ ```
1357
+
1358
+ ```python
1359
+ [The fixed code or original code if no issues]
1360
+ ```
1361
+ """
1362
+ },
1363
+ {"role": "user", "content": f"Evaluate this Manim code:\n\n```python\n{code}\n```\n\nThe animation is about: '{prompt}' with complexity level '{complexity}'.\n\nCheck for errors and positioning issues, especially element overlaps."}
1364
+ ]
1365
+ )
1366
+
1367
+ content = response.choices[0].message.content
1368
+
1369
+ # Extract evaluation report
1370
+ evaluation_report = ""
1371
+ if "```evaluation" in content:
1372
+ evaluation_parts = content.split("```evaluation", 1)[1].split("```", 1)
1373
+ if len(evaluation_parts) > 0:
1374
+ evaluation_report = evaluation_parts[0].strip()
1375
+
1376
+ # Extract fixed code
1377
+ fixed_code = code # Default to original code
1378
+ if "```python" in content:
1379
+ code_parts = content.split("```python", 1)[1].split("```", 1)
1380
+ if len(code_parts) > 0:
1381
+ potential_fixed_code = code_parts[0].strip()
1382
+ # Only use the fixed code if it's valid (contains basic Manim imports)
1383
+ if "from manim import" in potential_fixed_code or "import manim" in potential_fixed_code:
1384
+ fixed_code = potential_fixed_code
1385
+
1386
+ # Format the evaluation report
1387
+ if evaluation_report:
1388
+ formatted_report = f"## Code Evaluation\n\n{evaluation_report}\n\n"
1389
+ if fixed_code != code:
1390
+ formatted_report += "Issues were found and fixed in the code."
1391
+ return fixed_code, formatted_report
1392
+ else:
1393
+ return fixed_code, "## Code Evaluation\n\nNo significant issues found in the code."
1394
+
1395
+ except Exception as e:
1396
+ logger.error(f"Error during direct evaluation: {e}")
1397
+ return code, f"## Error During Evaluation\n\nCould not complete code evaluation: {str(e)}"
1398
+
1399
+ def format_evaluation_results(result: EvaluationResult) -> str:
1400
+ """Format evaluation results for display."""
1401
+ output = "## Code Evaluation Results\n\n"
1402
+
1403
+ if not result.has_errors:
1404
+ output += "✅ No errors or positioning issues detected. Code looks good!\n\n"
1405
+ return output
1406
+
1407
+ if result.syntax_errors:
1408
+ output += "### Syntax Errors\n\n"
1409
+ for i, error in enumerate(result.syntax_errors):
1410
+ output += f"{i+1}. {error}\n"
1411
+ output += "\n"
1412
+
1413
+ if result.positioning_issues:
1414
+ output += "### Positioning Issues\n\n"
1415
+ for i, issue in enumerate(result.positioning_issues):
1416
+ output += f"{i+1}. {issue}\n"
1417
+ output += "\n"
1418
+
1419
+ if result.overlap_issues:
1420
+ output += "### Potential Element Overlaps\n\n"
1421
+ for i, issue in enumerate(result.overlap_issues):
1422
+ output += f"{i+1}. {issue}\n"
1423
+ output += "\n"
1424
+
1425
+ if result.suggestions:
1426
+ output += "### Suggestions for Improvement\n\n"
1427
+ for i, suggestion in enumerate(result.suggestions):
1428
+ output += f"{i+1}. {suggestion}\n"
1429
+ output += "\n"
1430
+
1431
+ if result.fixed_code:
1432
+ output += "✅ These issues have been automatically fixed in the updated code.\n"
1433
+ else:
1434
+ output += "❌ Could not automatically fix all issues. Please review the code manually.\n"
1435
+
1436
+ return output
1437
+
1438
  # Replace the Gradio interface creation with a Blocks interface for better layout control
1439
  if __name__ == "__main__":
1440
  with gr.Blocks(title="Manimation Generator", theme=gr.themes.Base()) as demo:
 
1477
  label="Your Feedback"
1478
  )
1479
  refine_btn = gr.Button("Apply Feedback", variant="secondary")
1480
+
1481
+ # Add the missing "Evaluate Code" tab
1482
+ with gr.TabItem("Evaluate Code"):
1483
+ gr.Markdown("""
1484
+ Check your Manim code for:
1485
+ - Syntax errors
1486
+ - Positioning issues
1487
+ - Element overlaps
1488
+ - Animation flow problems
1489
+ """)
1490
+ evaluate_btn = gr.Button("Check Code for Errors", variant="secondary")
1491
 
1492
  # Code editor (common to both tabs)
1493
  code_output = gr.Code(
 
1547
  )
1548
  return video_path, log, new_history
1549
 
1550
+ def evaluate_and_update_chat(code, history):
1551
+ # Extract prompt from memory
1552
+ prompt = memory.history[-1]["prompt"] if memory.history else "Mathematical animation"
1553
+ complexity = "medium" # Default complexity
1554
+
1555
+ # Evaluate the code
1556
+ fixed_code, evaluation_report = evaluate_and_fix_manim_code(code, prompt, complexity)
1557
+
1558
+ new_history = update_chat_history(
1559
+ history,
1560
+ "**Request:** Check code for errors and positioning issues",
1561
+ f"**Evaluation complete**",
1562
+ None
1563
+ )
1564
+
1565
+ return fixed_code, evaluation_report, new_history
1566
+
1567
  # Connect the components to the function
1568
  generate_btn.click(
1569
  fn=generate_and_update_chat,
 
1583
  outputs=[video_output, log_output, chat_history]
1584
  )
1585
 
1586
+ evaluate_btn.click(
1587
+ fn=evaluate_and_update_chat,
1588
+ inputs=[code_output, chat_history],
1589
+ outputs=[code_output, log_output, chat_history]
1590
+ )
1591
+
1592
  # Add footer with social media links
1593
  with gr.Row(equal_height=True):
1594
  gr.Markdown("""
 
1603
 
1604
  demo.launch(server_name="0.0.0.0", server_port=7860)
1605
 
1606
+