Spaces:
Running
Running
File size: 3,301 Bytes
54ca85f a9334c7 54ca85f a9334c7 54ca85f a9334c7 aa1c07c 9c4fab2 54ca85f aa1c07c 9c4fab2 54ca85f 9c4fab2 aa1c07c 54ca85f aa1c07c 54ca85f aa1c07c 54ca85f aa1c07c 54ca85f a9334c7 54ca85f aa1c07c fa3da87 a9334c7 54ca85f a9334c7 54ca85f | 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 | """
utils.py — PDF extraction, Gemini LLM, and Manim code helpers.
"""
from __future__ import annotations
import re
from google import genai
from google.genai import types
# ── PDF Text Extraction ───────────────────────────────────────────────────────
def extract_pdf_text(pdf_path: str) -> str:
"""Extract plain text from a PDF using pypdf."""
from pypdf import PdfReader
reader = PdfReader(pdf_path)
pages = []
for page in reader.pages:
text = page.extract_text()
if text:
pages.append(text)
return "\n\n".join(pages)
# ── Gemini LLM ────────────────────────────────────────────────────────────────
def generate_manim_code(prompt_text: str, api_key: str) -> str:
"""Stream Manim code from Gemini 3 Flash Preview and return it as a string."""
client = genai.Client(api_key=api_key)
model = "gemini-3-flash-preview"
# Robust system prompt
system_prompt = """
You are an expert Python developer and Manim animation engineer.
Always produce code that:
- Is fully correct and runnable with Manim v1.0+.
- Uses clear, readable structure, proper imports, and PEP8-compliant formatting.
- Produces visually correct animations based on the user's description.
- Minimizes unnecessary complexity but keeps clarity.
- Names the main scene class `OutputVideo`.
- Returns code only, without extra explanation or markdown fences.
- Handles edge cases gracefully.
- Uses comments sparingly, only when clarifying complex parts.
- When in doubt, use Google search effectively to verify facts, functions, or best practices.
Always ensure your output is precise, accurate, and complete.
"""
contents = [
types.Content(
role="system",
parts=[types.Part.from_text(text=system_prompt)]
),
types.Content(
role="user",
parts=[types.Part.from_text(text=prompt_text)]
)
]
config = types.GenerateContentConfig(
thinking_config=types.ThinkingConfig(
thinking_level="HIGH"
)
)
code = ""
for chunk in client.models.generate_content_stream(
model=model,
contents=contents,
config=config
):
if chunk.text:
code += chunk.text
print(chunk.text, end="", flush=True)
return code
# ── Manim Code Sanitisation ───────────────────────────────────────────────────
def sanitize_manim_code(raw: str) -> str:
"""
Strip markdown fences, ensure correct imports and class name.
"""
code = re.sub(r"^```(?:python)?\s*", "", raw.strip(), flags=re.MULTILINE)
code = re.sub(r"\s*```$", "", code.strip(), flags=re.MULTILINE)
if "from manim import" not in code and "import manim" not in code:
code = "from manim import *\n\n" + code
# Ensure class is named OutputVideo
code = re.sub(r"class\s+\w+\s*\(\s*Scene\s*\)", "class OutputVideo(Scene)", code)
return code
|