Tune generation speed and timeouts
Browse filesCo-authored-by: Codex <codex@openai.com>
- app.py +36 -3
- executor.py +2 -2
- generator.py +27 -4
app.py
CHANGED
|
@@ -385,7 +385,7 @@ def get_agent():
|
|
| 385 |
return agent
|
| 386 |
|
| 387 |
|
| 388 |
-
@spaces.GPU(duration=
|
| 389 |
def run_visual_lab(prompt: str, retries: int) -> Tuple[str | None, str, str]:
|
| 390 |
try:
|
| 391 |
visual_agent = get_agent()
|
|
@@ -404,8 +404,41 @@ def run_visual_lab(prompt: str, retries: int) -> Tuple[str | None, str, str]:
|
|
| 404 |
def load_example(example: str) -> str:
|
| 405 |
examples = {
|
| 406 |
"Fourier": "Visualize how a square wave emerges from the first seven Fourier sine components, with each partial sum drawn in neon colors.",
|
| 407 |
-
"Orbit": "
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 408 |
"Eigen": "Explain eigenvectors of a 2D linear transformation using a grid, two vectors, and the transformed plane.",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 409 |
}
|
| 410 |
return examples.get(example, "")
|
| 411 |
|
|
@@ -451,7 +484,7 @@ with gr.Blocks(css=custom_css, title="SciVisual-Agent") as demo:
|
|
| 451 |
clear_btn = gr.Button("Clear", elem_id="clear-btn")
|
| 452 |
|
| 453 |
example_choice = gr.Radio(
|
| 454 |
-
choices=["
|
| 455 |
label="Quick Load",
|
| 456 |
value=None,
|
| 457 |
interactive=True,
|
|
|
|
| 385 |
return agent
|
| 386 |
|
| 387 |
|
| 388 |
+
@spaces.GPU(duration=600)
|
| 389 |
def run_visual_lab(prompt: str, retries: int) -> Tuple[str | None, str, str]:
|
| 390 |
try:
|
| 391 |
visual_agent = get_agent()
|
|
|
|
| 404 |
def load_example(example: str) -> str:
|
| 405 |
examples = {
|
| 406 |
"Fourier": "Visualize how a square wave emerges from the first seven Fourier sine components, with each partial sum drawn in neon colors.",
|
| 407 |
+
"Orbit": """Create a beautiful, scientifically accurate 2D physics animation of a satellite in an elliptical orbit around Earth using ManimCE.
|
| 408 |
+
|
| 409 |
+
Requirements:
|
| 410 |
+
1. Scaling & Geometry (CRITICAL to avoid overlapping):
|
| 411 |
+
- Represent Earth as a clear sphere or circle in the center, but keep its radius small (e.g., radius=0.6) so it doesn't swallow the orbit.
|
| 412 |
+
- Create an explicit, highly elongated elliptical orbit path (`Ellipse(width=6.0, height=3.5)`) shifted slightly so that Earth sits exactly at one of the focal points (Foci) of the ellipse, NOT at the geometric center (Kepler's First Law).
|
| 413 |
+
|
| 414 |
+
2. Dynamic Vectors & Updaters:
|
| 415 |
+
- Represent the satellite as a distinct, colored Dot (e.g., Cyber Cyan) moving along the elliptical path using a custom tracker or `ValueTracker` for the orbital angle.
|
| 416 |
+
- Create two dynamic arrows (`Vector` or `Arrow`) attached to the satellite dot:
|
| 417 |
+
* Gravitational Force Vector (Color: Neon Red): Must always point directly from the satellite's current position to Earth's center. Its length must dynamically increase when close to Earth and decrease when far away (Inverse-square law).
|
| 418 |
+
* Velocity Vector (Color: Bright Green): Must always be perfectly tangent to the elliptical orbit path in the direction of motion. Its length must dynamically represent orbital speed (faster at perigee, slower at apogee).
|
| 419 |
+
- Use `add_updater` on both vectors so their positions, directions, and lengths update smoothly at every single frame based on the satellite's position.
|
| 420 |
+
|
| 421 |
+
3. Labels & Overlay:
|
| 422 |
+
- Add text labels for "Velocity (v)" and "Gravity (Fg)" matching the vector colors.
|
| 423 |
+
- In the top-right corner, add a LaTeX mathematical text displaying Newton's Law of Universal Gravitation: "F_g = G * (M_1 * M_2) / r²".
|
| 424 |
+
|
| 425 |
+
4. Animation flow: Run the animation for 2 complete orbital periods (about 10-12 seconds) so the viewer can clearly observe the dramatic speeding up at the close approach and slowing down at the far end. Wrap everything inside a single clean Scene class.""",
|
| 426 |
"Eigen": "Explain eigenvectors of a 2D linear transformation using a grid, two vectors, and the transformed plane.",
|
| 427 |
+
"Simple Pendulum": """Create a high-quality Physics simulation of a Damped Simple Pendulum using ManimCE.
|
| 428 |
+
|
| 429 |
+
Requirements:
|
| 430 |
+
1. Physics Setup: Define explicit variables for gravitational acceleration (g=9.81), rod length (L=3.0), initial angle (theta = pi/4), and a damping coefficient (b=0.15) to simulate real-world air resistance. Use a clear numerical integration method (like Euler-Cromer) inside an object updater (`add_updater`) to dynamically recalculate the angular velocity and displacement at every frame (`dt`).
|
| 431 |
+
|
| 432 |
+
2. Visual Elements (Strict Coordinate Updates):
|
| 433 |
+
- A fixed ceiling line or pivot dot at the top center.
|
| 434 |
+
- A colored dot (e.g., Cyber Cyan or Neon Pink) representing the heavy bob. Make it big enough for viewers to see clearly.
|
| 435 |
+
- A clean line representing the pendulum rod that attaches the pivot to the bob. CRITICAL: You must attach an updater to this rod using `add_updater` so that its end position dynamically calls `rod.put_start_and_end_on(pivot.get_center(), bob.get_center())` at every single frame. The rod must always move dynamically with the bob.
|
| 436 |
+
- Add an elegant fading trace/trail (`TracedPath`) attached to the bob to visually map its decaying sinusoidal path across the screen over time.
|
| 437 |
+
|
| 438 |
+
3. Mathematical Overlay: In the upper left corner, display the differential equation governing the motion: "d²θ/dt² + (b/m)dθ/dt + (g/L)sin(θ) = 0" rendered beautifully via LaTeX.
|
| 439 |
+
|
| 440 |
+
4. Animation flow: Let the simulation run smoothly for 12 seconds to clearly demonstrate the kinetic energy converting to thermal energy as the oscillation slowly dampens to a complete halt. Ensure the code is self-contained and wrapped inside a single executable Scene class.
|
| 441 |
+
"""
|
| 442 |
}
|
| 443 |
return examples.get(example, "")
|
| 444 |
|
|
|
|
| 484 |
clear_btn = gr.Button("Clear", elem_id="clear-btn")
|
| 485 |
|
| 486 |
example_choice = gr.Radio(
|
| 487 |
+
choices=["Simple Pendulum", "Orbit", "Eigen"],
|
| 488 |
label="Quick Load",
|
| 489 |
value=None,
|
| 490 |
interactive=True,
|
executor.py
CHANGED
|
@@ -47,7 +47,7 @@ def _manim_command_prefix() -> list[str]:
|
|
| 47 |
|
| 48 |
def render_code(
|
| 49 |
code: str,
|
| 50 |
-
timeout_seconds: int =
|
| 51 |
output_dir: Path = OUTPUT_DIR,
|
| 52 |
quality: str = "-ql",
|
| 53 |
) -> RenderResult:
|
|
@@ -137,7 +137,7 @@ def render_code(
|
|
| 137 |
Path(temp_script_name).unlink(missing_ok=True)
|
| 138 |
|
| 139 |
|
| 140 |
-
def render_manim_scene(code: str, timeout_seconds: int =
|
| 141 |
"""Compatibility wrapper returning the tuple shape used by the app."""
|
| 142 |
result = render_code(code, timeout_seconds=timeout_seconds)
|
| 143 |
if result.success and result.video_path:
|
|
|
|
| 47 |
|
| 48 |
def render_code(
|
| 49 |
code: str,
|
| 50 |
+
timeout_seconds: int = 600,
|
| 51 |
output_dir: Path = OUTPUT_DIR,
|
| 52 |
quality: str = "-ql",
|
| 53 |
) -> RenderResult:
|
|
|
|
| 137 |
Path(temp_script_name).unlink(missing_ok=True)
|
| 138 |
|
| 139 |
|
| 140 |
+
def render_manim_scene(code: str, timeout_seconds: int = 600) -> Tuple[bool, str]:
|
| 141 |
"""Compatibility wrapper returning the tuple shape used by the app."""
|
| 142 |
result = render_code(code, timeout_seconds=timeout_seconds)
|
| 143 |
if result.success and result.video_path:
|
generator.py
CHANGED
|
@@ -33,7 +33,7 @@ class LocalModelConfig:
|
|
| 33 |
prompt_mode: str = "chat"
|
| 34 |
device_map: str = "auto"
|
| 35 |
load_in_4bit: bool = True
|
| 36 |
-
max_new_tokens: int =
|
| 37 |
temperature: float = 0.0
|
| 38 |
top_p: float = 0.9
|
| 39 |
timeout_seconds: int = 300
|
|
@@ -210,6 +210,9 @@ class LocalManimModel:
|
|
| 210 |
if not self.ready:
|
| 211 |
raise RuntimeError(self.error or "Local model is not ready.")
|
| 212 |
|
|
|
|
|
|
|
|
|
|
| 213 |
if self.no_system_role_support and len(messages) >= 2 and messages[0]["role"] == "system":
|
| 214 |
messages = [{"role": "user", "content": messages[0]["content"] + "\n\n" + messages[1]["content"]}]
|
| 215 |
|
|
@@ -227,6 +230,9 @@ class LocalManimModel:
|
|
| 227 |
"pad_token_id": self.tokenizer.pad_token_id,
|
| 228 |
"eos_token_id": self.tokenizer.eos_token_id,
|
| 229 |
"use_cache": True,
|
|
|
|
|
|
|
|
|
|
| 230 |
}
|
| 231 |
if self.config.temperature and self.config.temperature > 0:
|
| 232 |
generate_kwargs.update(
|
|
@@ -239,7 +245,8 @@ class LocalManimModel:
|
|
| 239 |
else:
|
| 240 |
generate_kwargs["do_sample"] = False
|
| 241 |
|
| 242 |
-
|
|
|
|
| 243 |
output_ids = generated_ids[0][len(model_inputs.input_ids[0]) :].tolist()
|
| 244 |
|
| 245 |
try:
|
|
@@ -276,6 +283,22 @@ class LocalManimModel:
|
|
| 276 |
return "\n\n".join(chunks)
|
| 277 |
|
| 278 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 279 |
class ManimVisualAgent:
|
| 280 |
"""Generate Manim code with local inference and repair failures."""
|
| 281 |
|
|
@@ -426,9 +449,9 @@ def build_default_agent() -> ManimVisualAgent:
|
|
| 426 |
prompt_mode=os.environ.get("SCIVISUAL_PROMPT_MODE", "chat"),
|
| 427 |
device_map=os.environ.get("SCIVISUAL_DEVICE_MAP", "auto"),
|
| 428 |
load_in_4bit=os.environ.get("SCIVISUAL_LOAD_IN_4BIT", "1").lower() not in ("0", "false", "no"),
|
| 429 |
-
max_new_tokens=int(os.environ.get("SCIVISUAL_MAX_NEW_TOKENS", "
|
| 430 |
temperature=float(os.environ.get("SCIVISUAL_TEMPERATURE", "0")),
|
| 431 |
top_p=float(os.environ.get("SCIVISUAL_TOP_P", "0.9")),
|
| 432 |
-
timeout_seconds=int(os.environ.get("SCIVISUAL_RENDER_TIMEOUT", "
|
| 433 |
)
|
| 434 |
return ManimVisualAgent(config=config)
|
|
|
|
| 33 |
prompt_mode: str = "chat"
|
| 34 |
device_map: str = "auto"
|
| 35 |
load_in_4bit: bool = True
|
| 36 |
+
max_new_tokens: int = 8192
|
| 37 |
temperature: float = 0.0
|
| 38 |
top_p: float = 0.9
|
| 39 |
timeout_seconds: int = 300
|
|
|
|
| 210 |
if not self.ready:
|
| 211 |
raise RuntimeError(self.error or "Local model is not ready.")
|
| 212 |
|
| 213 |
+
import torch
|
| 214 |
+
from transformers import StoppingCriteriaList
|
| 215 |
+
|
| 216 |
if self.no_system_role_support and len(messages) >= 2 and messages[0]["role"] == "system":
|
| 217 |
messages = [{"role": "user", "content": messages[0]["content"] + "\n\n" + messages[1]["content"]}]
|
| 218 |
|
|
|
|
| 230 |
"pad_token_id": self.tokenizer.pad_token_id,
|
| 231 |
"eos_token_id": self.tokenizer.eos_token_id,
|
| 232 |
"use_cache": True,
|
| 233 |
+
"stopping_criteria": StoppingCriteriaList(
|
| 234 |
+
[_StopOnTokenSequence(self.tokenizer.encode("</CODE>", add_special_tokens=False))]
|
| 235 |
+
),
|
| 236 |
}
|
| 237 |
if self.config.temperature and self.config.temperature > 0:
|
| 238 |
generate_kwargs.update(
|
|
|
|
| 245 |
else:
|
| 246 |
generate_kwargs["do_sample"] = False
|
| 247 |
|
| 248 |
+
with torch.inference_mode():
|
| 249 |
+
generated_ids = self.model.generate(**generate_kwargs)
|
| 250 |
output_ids = generated_ids[0][len(model_inputs.input_ids[0]) :].tolist()
|
| 251 |
|
| 252 |
try:
|
|
|
|
| 283 |
return "\n\n".join(chunks)
|
| 284 |
|
| 285 |
|
| 286 |
+
class _StopOnTokenSequence:
|
| 287 |
+
"""Stop generation when a specific token suffix appears."""
|
| 288 |
+
|
| 289 |
+
def __init__(self, stop_token_ids: List[int]) -> None:
|
| 290 |
+
self.stop_token_ids = stop_token_ids
|
| 291 |
+
|
| 292 |
+
def __call__(self, input_ids, scores, **kwargs) -> bool:
|
| 293 |
+
del scores, kwargs
|
| 294 |
+
if not self.stop_token_ids:
|
| 295 |
+
return False
|
| 296 |
+
if input_ids.shape[-1] < len(self.stop_token_ids):
|
| 297 |
+
return False
|
| 298 |
+
tail = input_ids[0, -len(self.stop_token_ids) :].tolist()
|
| 299 |
+
return tail == self.stop_token_ids
|
| 300 |
+
|
| 301 |
+
|
| 302 |
class ManimVisualAgent:
|
| 303 |
"""Generate Manim code with local inference and repair failures."""
|
| 304 |
|
|
|
|
| 449 |
prompt_mode=os.environ.get("SCIVISUAL_PROMPT_MODE", "chat"),
|
| 450 |
device_map=os.environ.get("SCIVISUAL_DEVICE_MAP", "auto"),
|
| 451 |
load_in_4bit=os.environ.get("SCIVISUAL_LOAD_IN_4BIT", "1").lower() not in ("0", "false", "no"),
|
| 452 |
+
max_new_tokens=int(os.environ.get("SCIVISUAL_MAX_NEW_TOKENS", "8192")),
|
| 453 |
temperature=float(os.environ.get("SCIVISUAL_TEMPERATURE", "0")),
|
| 454 |
top_p=float(os.environ.get("SCIVISUAL_TOP_P", "0.9")),
|
| 455 |
+
timeout_seconds=int(os.environ.get("SCIVISUAL_RENDER_TIMEOUT", "600")),
|
| 456 |
)
|
| 457 |
return ManimVisualAgent(config=config)
|