import streamlit as st import tempfile import os import logging from pathlib import Path from PIL import Image import io import numpy as np import sys import subprocess import json from pygments import highlight from pygments.lexers import PythonLexer from pygments.formatters import HtmlFormatter import base64 import torch import re import shutil import time from datetime import datetime import streamlit.components.v1 as components import uuid import pandas as pd import plotly.express as px import zipfile import traceback # Set up enhanced logging logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[logging.StreamHandler()] ) logger = logging.getLogger(__name__) # Model configuration mapping MODEL_CONFIGS = { "DeepSeek-V3-0324": {"max_tokens": 4000, "param_name": "max_tokens", "api_version": None, "category": "DeepSeek"}, "DeepSeek-R1": {"max_tokens": 4000, "param_name": "max_tokens", "api_version": None, "category": "DeepSeek"}, "Llama-4-Scout-17B-16E-Instruct": {"max_tokens": 4000, "param_name": "max_tokens", "api_version": None, "category": "Meta"}, "Llama-4-Maverick-17B-128E-Instruct-FP8": {"max_tokens": 4000, "param_name": "max_tokens", "api_version": None, "category": "Meta"}, "gpt-4o-mini": {"max_tokens": 15000, "param_name": "max_tokens", "api_version": None, "category": "OpenAI"}, "gpt-4o": {"max_tokens": 16000, "param_name": "max_tokens", "api_version": None, "category": "OpenAI"}, "gpt-4.1": {"max_tokens": 32768, "param_name": "max_tokens", "api_version": None, "category": "OpenAI"}, "gpt-4.1-mini": {"max_tokens": 32768, "param_name": "max_tokens", "api_version": None, "category": "OpenAI"}, "gpt-4.1-nano": {"max_tokens": 32768, "param_name": "max_tokens", "api_version": None, "category": "OpenAI"}, "o3-mini": {"max_completion_tokens": 100000, "param_name": "max_completion_tokens", "api_version": "2024-12-01-preview", "category": "OpenAI"}, "o1": {"max_completion_tokens": 100000, "param_name": "max_completion_tokens", "api_version": "2024-12-01-preview", "category": "OpenAI"}, "o1-mini": {"max_completion_tokens": 66000, "param_name": "max_completion_tokens", "api_version": "2024-12-01-preview", "category": "OpenAI"}, "o1-preview": {"max_tokens": 33000, "param_name": "max_tokens", "api_version": None, "category": "OpenAI"}, "Phi-4-multimodal-instruct": {"max_tokens": 4000, "param_name": "max_tokens", "api_version": None, "category": "Microsoft"}, "Mistral-large-2407": {"max_tokens": 4000, "param_name": "max_tokens", "api_version": None, "category": "Mistral"}, "Codestral-2501": {"max_tokens": 4000, "param_name": "max_tokens", "api_version": None, "category": "Mistral"}, "default": {"max_tokens": 4000, "param_name": "max_tokens", "api_version": None, "category": "Other"} } # Try to import Streamlit Ace try: from streamlit_ace import st_ace ACE_EDITOR_AVAILABLE = True except ImportError: ACE_EDITOR_AVAILABLE = False logger.warning("streamlit-ace not available, falling back to text area") def prepare_api_params(messages, model_name): config = MODEL_CONFIGS.get(model_name, MODEL_CONFIGS["default"]) params = {"messages": messages, "model": model_name} params[config["param_name"]] = config.get(config["param_name"]) return params, config def get_secret(env_var): val = os.environ.get(env_var) if not val: logger.warning(f"Secret '{env_var}' not found") return val def check_password(): correct = get_secret("password") if not correct: st.error("Admin password not configured") return False if "password_entered" not in st.session_state: st.session_state.password_entered = False if not st.session_state.password_entered: pwd = st.text_input("Enter password to access AI features", type="password") if pwd: if pwd == correct: st.session_state.password_entered = True return True else: st.error("Incorrect password") return False return False return True def ensure_packages(): required = { 'manim': '0.17.3', 'Pillow': '9.0.0', 'numpy': '1.22.0', 'transformers': '4.30.0', 'torch': '2.0.0', 'pygments': '2.15.1', 'streamlit-ace': '0.1.1', 'pydub': '0.25.1', 'plotly': '5.14.0', 'pandas': '2.0.0', 'python-pptx': '0.6.21', 'fpdf': '1.7.2', 'matplotlib': '3.5.0', 'seaborn': '0.11.2', 'scipy': '1.7.3', 'huggingface_hub': '0.16.0' } missing = {} for pkg, ver in required.items(): try: __import__(pkg if pkg != 'Pillow' else 'PIL') except ImportError: missing[pkg] = ver if not missing: return True bar = st.progress(0) txt = st.empty() for i, (pkg, ver) in enumerate(missing.items()): bar.progress(i / len(missing)) txt.text(f"Installing {pkg}...") res = subprocess.run([sys.executable, "-m", "pip", "install", f"{pkg}>={ver}"], capture_output=True, text=True) if res.returncode != 0: st.error(f"Failed to install {pkg}") return False bar.progress(1.0) txt.empty() return True def install_custom_packages(pkgs): if not pkgs.strip(): return True, "No packages specified" parts = [p.strip() for p in pkgs.split(",") if p.strip()] if not parts: return True, "No valid packages" sidebar_txt = st.sidebar.empty() bar = st.sidebar.progress(0) results, success = [], True for i, p in enumerate(parts): bar.progress(i / len(parts)) sidebar_txt.text(f"Installing {p}...") res = subprocess.run([sys.executable, "-m", "pip", "install", p], capture_output=True, text=True) if res.returncode != 0: results.append(f"Failed {p}: {res.stderr}") success = False else: results.append(f"Installed {p}") bar.progress(1.0) sidebar_txt.empty() return success, "\n".join(results) @st.cache_resource(ttl=3600) def init_ai_models_direct(): token = get_secret("github_token_api") if not token: st.error("API token not configured") return None try: from azure.ai.inference import ChatCompletionsClient from azure.ai.inference.models import UserMessage from azure.core.credentials import AzureKeyCredential client = ChatCompletionsClient( endpoint="https://models.inference.ai.azure.com", credential=AzureKeyCredential(token) ) return {"client": client, "model_name": "gpt-4o", "last_loaded": datetime.now().isoformat()} except ImportError as e: st.error("Azure AI SDK not installed") logger.error(str(e)) return None def generate_manim_preview(code): objects = [] if "Circle" in code: objects.append("⭕") if "Square" in code: objects.append("🔲") if "MathTex" in code or "Tex" in code: objects.append("📊") if "Text" in code: objects.append("📝") if "Axes" in code: objects.append("📈") icons = "".join(objects) or "🎬" return f"""

Animation Preview

{icons}

Full rendering required for accurate preview

""" def extract_scene_class_name(code): m = re.findall(r'class\s+(\w+)\s*\([^)]*Scene', code) return m[0] if m else "MyScene" def mp4_to_gif(mp4_path, gif_path, fps=15): cmd = [ "ffmpeg", "-i", mp4_path, "-vf", f"fps={fps},scale=640:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse", "-loop", "0", gif_path ] res = subprocess.run(cmd, capture_output=True, text=True) return gif_path if res.returncode == 0 else None def generate_manim_video(code, fmt, quality, speed=1.0, audio_path=None): temp_dir = tempfile.mkdtemp(prefix="manim_") scene = extract_scene_class_name(code) scene_file = os.path.join(temp_dir, "scene.py") with open(scene_file, "w") as f: f.write(code) qflags = {"480p":"-ql","720p":"-qm","1080p":"-qh","4K":"-qk","8K":"-qp"} qf = qflags.get(quality, "-qm") cmd = ["manim", scene_file, scene, qf, f"--format={fmt}"] proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True) output, out_path, mp4_path = [], None, None log = st.empty() for line in proc.stdout: output.append(line) log.code("".join(output[-10:])) if "File ready at" in line: m = re.search(r'["\'](.+?\.(?:mp4|gif|webm|svg))["\']', line) if m: out_path = m.group(1) if out_path.endswith(".mp4"): mp4_path = out_path proc.wait() time.sleep(1) if fmt=="gif" and (not out_path or not os.path.exists(out_path)) and mp4_path: gif = os.path.join(temp_dir, "converted.gif") conv = mp4_to_gif(mp4_path, gif) if conv and os.path.exists(conv): out_path = conv data = None if out_path and os.path.exists(out_path): with open(out_path, "rb") as f: data = f.read() shutil.rmtree(temp_dir) if data: size_mb = len(data)/(1024*1024) return data, f"✅ Generated ({size_mb:.1f} MB)" else: return None, "❌ No output generated. See logs." def detect_input_calls(code): calls=[] for i,line in enumerate(code.split("\n"),1): if "input(" in line and not line.strip().startswith("#"): m=re.search(r'input\(["\'](.+?)["\']\)', line) prompt=m.group(1) if m else f"Input at line {i}" calls.append({"line":i,"prompt":prompt}) return calls def run_python_script(code, inputs=None, timeout=60): res={"stdout":"","stderr":"","exception":None,"plots":[],"dataframes":[],"execution_time":0} mod="" if inputs: mod=f""" __INPUTS={inputs} __IDX=0 def input(prompt=''): global __IDX print(prompt,end='') if __IDX