Spaces:
Running
Running
| """ | |
| Code Generation Tools for Manim MCP Server | |
| This module provides tools for generating and refining Manim animation code. | |
| """ | |
| import logging | |
| from typing import Any, Dict, Optional | |
| from mcp.types import CallToolResult, TextContent | |
| from utils.hf_wrapper import HFInferenceWrapper, ModelConfig | |
| logger = logging.getLogger(__name__) | |
| async def generate_manim_code( | |
| hf_wrapper: HFInferenceWrapper, arguments: Dict[str, Any] | |
| ) -> CallToolResult: | |
| """ | |
| Generate Manim Python code for an animation concept. | |
| Uses a code LLM to generate complete, runnable Manim code based on: | |
| - A concept description | |
| - Scene details | |
| - Desired visual elements | |
| - Optional error feedback for retries | |
| Args: | |
| hf_wrapper: HuggingFace inference wrapper instance | |
| arguments: Dictionary containing: | |
| - concept (str): The animation concept | |
| - scene_description (str): Detailed scene description | |
| - visual_elements (list, optional): List of visual elements to include | |
| - model (str, optional): Hugging Face model to use | |
| - previous_code (str, optional): Previous code attempt (for retries) | |
| - error_message (str, optional): Error from previous attempt (for retries) | |
| Returns: | |
| CallToolResult with the generated Manim code | |
| """ | |
| concept = arguments["concept"] | |
| scene_description = arguments["scene_description"] | |
| visual_elements = arguments.get("visual_elements", []) | |
| model = arguments.get("model") | |
| previous_code = arguments.get("previous_code") | |
| error_message = arguments.get("error_message") | |
| try: | |
| model_config = ModelConfig() | |
| selected_model = model or model_config.code_models[0] | |
| # Build prompt based on whether this is a retry | |
| if previous_code and error_message: | |
| prompt = f""" | |
| You are an expert animation engineer using Manim Community Edition (v0.18.0+). | |
| The previous code attempt had an error. Your task is to FIX the code. | |
| PREVIOUS CODE: | |
| ```python | |
| {previous_code} | |
| ``` | |
| ERROR ENCOUNTERED: | |
| {error_message} | |
| TASK: Fix the error in the code above. Pay special attention to: | |
| - Closing all parentheses, brackets, and braces | |
| - Completing all function calls | |
| - Proper indentation | |
| - Valid Python syntax | |
| Concept: {concept} | |
| Scene Description: {scene_description} | |
| Visual Elements: {", ".join(visual_elements)} | |
| STRICT CODE REQUIREMENTS: | |
| 1. Header: MUST start with `from manim import *` | |
| 2. Class Structure: Define a class inheriting from `MovingCameraScene` (use this instead of `Scene` to enable camera zoom/pan with `self.camera.frame`) | |
| 3. Method: All logic must be inside the `def construct(self):` method | |
| 4. SYNTAX: Ensure ALL parentheses, brackets, and function calls are properly closed | |
| 5. Colors: Use ONLY valid Manim colors (WHITE, BLACK, RED, GREEN, BLUE, YELLOW, ORANGE, PINK, PURPLE, TEAL, GOLD, etc.) | |
| 6. Text: Use `Text()` objects for strings | |
| 7. Positioning: Use `.next_to()`, `.move_to()`, or `.shift()` | |
| 8. Animations: Use Write(), Create(), FadeIn(), FadeOut(), Transform(), Flash(), Indicate() - capitalize properly! | |
| 9. Pacing: Include `self.wait(1)` between animations | |
| OUTPUT FORMAT: | |
| Provide ONLY the complete, corrected Python code. No markdown blocks. No explanations. | |
| """ | |
| else: | |
| prompt = f""" | |
| You are an expert animation engineer using Manim Community Edition (v0.18.0+). | |
| Generate a complete, runnable Python script for the following request. | |
| Concept: {concept} | |
| Scene Description: {scene_description} | |
| Visual Elements: {", ".join(visual_elements)} | |
| STRICT CODE REQUIREMENTS: | |
| 1. Header: MUST start with `from manim import *` | |
| 2. Class Structure: Define a class inheriting from `MovingCameraScene` (e.g., `class GenScene(MovingCameraScene):`) - this enables camera operations like zoom/pan via `self.camera.frame` | |
| 3. Method: All logic must be inside the `def construct(self):` method | |
| 4. SYNTAX: Ensure ALL parentheses, brackets, and function calls are properly closed | |
| 5. Colors: Use ONLY these valid Manim color constants: | |
| - Basic: WHITE, BLACK, GRAY, GREY, LIGHT_GRAY, DARK_GRAY | |
| - Primary: RED, GREEN, BLUE, YELLOW, ORANGE, PINK, PURPLE, TEAL, GOLD, MAROON | |
| - Variants: RED_A, RED_B, RED_C, RED_D, RED_E, GREEN_A, GREEN_B, GREEN_C, GREEN_D, GREEN_E, | |
| BLUE_A, BLUE_B, BLUE_C, BLUE_D, BLUE_E, YELLOW_A, YELLOW_B, YELLOW_C, YELLOW_D, YELLOW_E | |
| - NEVER use: DARK_GREEN, LIGHT_GREEN, DARK_BLUE, LIGHT_BLUE, DARK_RED, LIGHT_RED (these don't exist!) | |
| 6. Text: Use `Text()` objects for strings. Avoid `Tex()` or `MathTex()` unless necessary | |
| 7. Positioning: Use `.next_to()`, `.move_to()`, or `.shift()` to arrange elements | |
| 8. Animations: Use ONLY these valid animations: | |
| - Write(), Create(), FadeIn(), FadeOut(), GrowFromCenter(), ShrinkToCenter() | |
| - Transform(), ReplacementTransform(), MoveToTarget(), ApplyMethod() | |
| - Rotate(), Indicate(), Flash(), ShowCreation() - DO NOT use lowercase like 'flash' | |
| - For custom effects use .animate.method() (e.g., obj.animate.scale(2), obj.animate.shift(UP)) | |
| 9. Pacing: Include `self.wait(1)` between major animation groups | |
| OUTPUT FORMAT: | |
| Provide ONLY the raw Python code. Do not wrap in markdown blocks (no ```python). Do not include conversational text. | |
| """ | |
| response = await hf_wrapper.text_generation( | |
| model=selected_model, | |
| prompt=prompt, | |
| max_new_tokens=2048, | |
| temperature=0.3, | |
| ) | |
| logger.info(f"Successfully generated Manim code for concept: {concept}") | |
| return CallToolResult( | |
| content=[ | |
| TextContent( | |
| type="text", | |
| text=f"Generated Manim Code:\n\n```python\n{response}\n```", | |
| ) | |
| ] | |
| ) | |
| except Exception as e: | |
| logger.error(f"Code generation failed: {str(e)}") | |
| return CallToolResult( | |
| content=[ | |
| TextContent(type="text", text=f"Code generation failed: {str(e)}") | |
| ], | |
| isError=True, | |
| ) | |
| async def refine_animation( | |
| hf_wrapper: HFInferenceWrapper, arguments: Dict[str, Any] | |
| ) -> CallToolResult: | |
| """ | |
| Refine animation code based on feedback. | |
| Uses a code LLM to improve existing Manim code based on: | |
| - User feedback or error messages | |
| - Specific improvement goals | |
| - Visual or educational quality issues | |
| Args: | |
| hf_wrapper: HuggingFace inference wrapper instance | |
| arguments: Dictionary containing: | |
| - original_code (str): The original Manim code to refine | |
| - feedback (str): Feedback or error message about the code | |
| - improvement_goals (list, optional): List of specific improvement goals | |
| - model (str, optional): Hugging Face model to use | |
| Returns: | |
| CallToolResult with the refined Manim code | |
| """ | |
| original_code = arguments["original_code"] | |
| feedback = arguments["feedback"] | |
| improvement_goals = arguments.get("improvement_goals", []) | |
| model = arguments.get("model") | |
| try: | |
| model_config = ModelConfig() | |
| selected_model = model or model_config.code_models[0] | |
| prompt = f""" | |
| You are a Manim Code Repair Agent. Your task is to rewrite the FULL Python script to fix issues or apply improvements. | |
| Previous Code: | |
| {original_code} | |
| User Feedback/Error: | |
| {feedback} | |
| Improvement Goals: | |
| {", ".join(improvement_goals)} | |
| INSTRUCTIONS: | |
| 1. Output the COMPLETE corrected script, including `from manim import *`. | |
| 2. Do not output diffs or partial snippets. | |
| 3. Ensure the class inherits from `MovingCameraScene` and uses `def construct(self):`. | |
| 4. Fix logic errors based on the feedback. | |
| 5. Animations: Use ONLY valid animations like Write(), FadeIn(), FadeOut(), Create(), Flash(), Transform() - NEVER lowercase! | |
| 6. Colors: Use ONLY these valid Manim color constants: | |
| - Basic: WHITE, BLACK, GRAY, GREY, LIGHT_GRAY, DARK_GRAY | |
| - Primary: RED, GREEN, BLUE, YELLOW, ORANGE, PINK, PURPLE, TEAL, GOLD, MAROON | |
| - Variants: RED_A, RED_B, RED_C, RED_D, RED_E, GREEN_A, GREEN_B, GREEN_C, GREEN_D, GREEN_E, | |
| BLUE_A, BLUE_B, BLUE_C, BLUE_D, BLUE_E, YELLOW_A, YELLOW_B, YELLOW_C, YELLOW_D, YELLOW_E | |
| - NEVER use: DARK_GREEN, LIGHT_GREEN, DARK_BLUE, LIGHT_BLUE, DARK_RED, LIGHT_RED (these don't exist!) | |
| - For darker/lighter variants, use the letter suffixes (e.g., GREEN_E for dark green, GREEN_A for light green). | |
| OUTPUT: | |
| Return ONLY the raw Python code. No markdown backticks. No explanation. | |
| """ | |
| response = await hf_wrapper.text_generation( | |
| model=selected_model, | |
| prompt=prompt, | |
| max_new_tokens=2048, | |
| temperature=0.3, | |
| ) | |
| logger.info("Successfully refined animation code") | |
| return CallToolResult( | |
| content=[ | |
| TextContent( | |
| type="text", | |
| text=f"Refined Manim Code:\n\n```python\n{response}\n```", | |
| ) | |
| ] | |
| ) | |
| except Exception as e: | |
| logger.error(f"Code refinement failed: {str(e)}") | |
| return CallToolResult( | |
| content=[ | |
| TextContent(type="text", text=f"Code refinement failed: {str(e)}") | |
| ], | |
| isError=True, | |
| ) | |