Spaces:
Sleeping
Sleeping
File size: 7,492 Bytes
f485648 |
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 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
import os
import google.generativeai as genai
from loguru import logger
from dotenv import load_dotenv
from typing import Optional, TYPE_CHECKING
import re
if TYPE_CHECKING:
from .context_manager import ConversationContext
load_dotenv()
class GeminiClient:
def __init__(self, api_key: str = None, context_manager: Optional['ConversationContext'] = None):
self.api_key = api_key or os.getenv("GEMINI_API_KEY")
if not self.api_key:
raise ValueError("GEMINI_API_KEY not found in environment variables")
genai.configure(api_key=self.api_key)
self.model = genai.GenerativeModel("gemini-2.0-flash-thinking-exp")
self.context_manager = context_manager
logger.info("Gemini client initialized")
def generate_manim_code(self, user_request: str) -> str:
"""Generate Manim code based on the user's request"""
# Системный промпт с инструкциями
system_prompt = """You are a Manim code generator. Your ONLY task is to generate executable Python code using the Manim library.
CRITICAL RULES:
- You MUST respond with ONLY Python code, nothing else
- NO explanations, NO text, NO comments outside the code
- The code MUST be ready to execute immediately
- The response should start with "from manim import *" and contain a complete Scene class
CODE REQUIREMENTS:
1. Always import: from manim import *
2. Create a class that inherits from Scene (default name: VideoScene)
3. Implement the construct(self) method
4. Use self.play() for animations
5. End with self.wait(1) or self.wait(2)
6. Use Text() instead of MathTex() for all text (MathTex requires LaTeX setup)
7. For mathematical formulas, use Text() with Unicode symbols: ², ³, ∫, ∑, π, etc.
8. Use simple geometric shapes: Square(), Circle(), Rectangle(), Line(), etc.
9. Common animations: Write(), Create(), Transform(), FadeIn(), FadeOut(), DrawBorderThenFill()
10. Position objects with .move_to(), .shift(), .to_edge(), .next_to()
EXAMPLE OUTPUT FORMAT (this is exactly how your response should look):
```python
from manim import *
class VideoScene(Scene):
def construct(self):
title = Text("Example Title")
self.play(Write(title))
self.wait(2)
```
IMPORTANT:
- Your entire response must be valid Python code
- Do not include any text before or after the code
- If the request is in any language other than English, still generate code with English variable names and comments
- Focus on creating visually appealing animations that demonstrate the requested concept"""
# Получаем контекст предыдущих сообщений
messages = []
if self.context_manager:
messages = self.context_manager.get_context_for_gemini()
# Добавляем системный промпт и текущий запрос если истории нет
if not messages:
messages = [
{"role": "user", "parts": [{"text": f"{system_prompt}\n\nCreate a video for the request: {user_request}"}]}
]
# Debug логирование полного контекста
logger.debug(f"Sending {len(messages)} messages to Gemini:")
for i, message in enumerate(messages):
logger.debug(f"Message {i+1} ({message['role']}): {message['parts'][0]['text'][:200]}{'...' if len(message['parts'][0]['text']) > 200 else ''}")
logger.info(f"Sending request to Gemini with {len(messages)} context messages")
response = self.model.generate_content(messages)
# Extract code from the response
code = response.text.strip()
# Улучшенное извлечение кода
if code.startswith("```python"):
# Стандартный случай: код начинается с ```python
code = code[9:]
if code.endswith("```"):
code = code[:-3]
elif code.startswith("```"):
# Код начинается с ```
code = code[3:]
if code.endswith("```"):
code = code[:-3]
else:
# Ищем первый блок кода внутри текста
python_match = re.search(r'```python\s*\n(.*?)\n```', code, re.DOTALL)
if python_match:
code = python_match.group(1)
else:
# Ищем любой блок ```
code_match = re.search(r'```\s*\n(.*?)\n```', code, re.DOTALL)
if code_match:
code = code_match.group(1)
# Если нет блоков кода, оставляем как есть (весь ответ)
code = code.strip()
logger.info("Manim code generated successfully")
return code
def fix_manim_code(self, current_code: str, error_trace: str, user_hint: str | None = None) -> str:
"""Fix Manim code using the error trace and optional user hint"""
# Получаем контекст
messages = []
if self.context_manager:
messages = self.context_manager.get_context_for_gemini()
# Если контекста нет, создаем базовое сообщение
if not messages:
hint_block = f"\nUser hint: {user_hint}" if user_hint else ""
prompt = f"""
You are an assistant that helps fix errors in Manim code.
Current code:
```python
{current_code}
```
Execution error:
{error_trace}
{hint_block}
Provide the corrected code. Return ONLY the Python code without explanations.
"""
messages = [{"role": "user", "parts": [{"text": prompt}]}]
# Debug логирование полного контекста
logger.debug(f"Sending {len(messages)} messages to Gemini for code fix:")
for i, message in enumerate(messages):
logger.debug(f"Message {i+1} ({message['role']}): {message['parts'][0]['text'][:200]}{'...' if len(message['parts'][0]['text']) > 200 else ''}")
logger.info("Sending code fix request to Gemini")
response = self.model.generate_content(messages)
fixed = response.text.strip()
# Улучшенное извлечение кода
if fixed.startswith("```python"):
# Стандартный случай: код начинается с ```python
fixed = fixed[9:]
if fixed.endswith("```"):
fixed = fixed[:-3]
elif fixed.startswith("```"):
# Код начинается с ```
fixed = fixed[3:]
if fixed.endswith("```"):
fixed = fixed[:-3]
else:
# Ищем первый блок кода внутри текста
python_match = re.search(r'```python\s*\n(.*?)\n```', fixed, re.DOTALL)
if python_match:
fixed = python_match.group(1)
else:
# Ищем любой блок ```
code_match = re.search(r'```\s*\n(.*?)\n```', fixed, re.DOTALL)
if code_match:
fixed = code_match.group(1)
# Если нет блоков кода, оставляем как есть (весь ответ)
fixed = fixed.strip()
logger.info("Received fixed code from Gemini")
return fixed
|