Spaces:
Running
Running
update requirements for gradio and streamlit
Browse files
app.py
CHANGED
|
@@ -2030,10 +2030,161 @@ This will help me create a better design for you."""
|
|
| 2030 |
|
| 2031 |
# Deploy to Spaces logic
|
| 2032 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2033 |
def wrap_html_in_gradio_app(html_code):
|
| 2034 |
# Escape triple quotes for safe embedding
|
| 2035 |
safe_html = html_code.replace('"""', r'\"\"\"')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2036 |
return (
|
|
|
|
| 2037 |
'import gradio as gr\n\n'
|
| 2038 |
'def show_html():\n'
|
| 2039 |
f' return """{safe_html}"""\n\n'
|
|
@@ -2559,8 +2710,36 @@ with gr.Blocks(
|
|
| 2559 |
exist_ok=True
|
| 2560 |
)
|
| 2561 |
|
| 2562 |
-
#
|
|
|
|
|
|
|
|
|
|
| 2563 |
import tempfile
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2564 |
with tempfile.NamedTemporaryFile("w", suffix=".py", delete=False) as f:
|
| 2565 |
f.write(code)
|
| 2566 |
temp_path = f.name
|
|
@@ -2817,8 +2996,37 @@ with gr.Blocks(
|
|
| 2817 |
import os
|
| 2818 |
os.unlink(temp_path)
|
| 2819 |
else:
|
| 2820 |
-
|
|
|
|
|
|
|
|
|
|
| 2821 |
import tempfile
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2822 |
with tempfile.NamedTemporaryFile("w", suffix=f".{file_name.split('.')[-1]}", delete=False) as f:
|
| 2823 |
f.write(code)
|
| 2824 |
temp_path = f.name
|
|
|
|
| 2030 |
|
| 2031 |
# Deploy to Spaces logic
|
| 2032 |
|
| 2033 |
+
def extract_import_statements(code):
|
| 2034 |
+
"""Extract import statements from generated code."""
|
| 2035 |
+
import ast
|
| 2036 |
+
import re
|
| 2037 |
+
|
| 2038 |
+
import_statements = []
|
| 2039 |
+
|
| 2040 |
+
# Built-in Python modules to exclude
|
| 2041 |
+
builtin_modules = {
|
| 2042 |
+
'os', 'sys', 'json', 'time', 'datetime', 'random', 'math', 're', 'collections',
|
| 2043 |
+
'itertools', 'functools', 'pathlib', 'urllib', 'http', 'email', 'html', 'xml',
|
| 2044 |
+
'csv', 'tempfile', 'shutil', 'subprocess', 'threading', 'multiprocessing',
|
| 2045 |
+
'asyncio', 'logging', 'typing', 'base64', 'hashlib', 'secrets', 'uuid',
|
| 2046 |
+
'copy', 'pickle', 'io', 'contextlib', 'warnings', 'sqlite3', 'gzip', 'zipfile',
|
| 2047 |
+
'tarfile', 'socket', 'ssl', 'platform', 'getpass', 'pwd', 'grp', 'stat',
|
| 2048 |
+
'glob', 'fnmatch', 'linecache', 'traceback', 'inspect', 'keyword', 'token',
|
| 2049 |
+
'tokenize', 'ast', 'code', 'codeop', 'dis', 'py_compile', 'compileall',
|
| 2050 |
+
'importlib', 'pkgutil', 'modulefinder', 'runpy', 'site', 'sysconfig'
|
| 2051 |
+
}
|
| 2052 |
+
|
| 2053 |
+
try:
|
| 2054 |
+
# Try to parse as Python AST
|
| 2055 |
+
tree = ast.parse(code)
|
| 2056 |
+
|
| 2057 |
+
for node in ast.walk(tree):
|
| 2058 |
+
if isinstance(node, ast.Import):
|
| 2059 |
+
for alias in node.names:
|
| 2060 |
+
module_name = alias.name.split('.')[0]
|
| 2061 |
+
if module_name not in builtin_modules and not module_name.startswith('_'):
|
| 2062 |
+
import_statements.append(f"import {alias.name}")
|
| 2063 |
+
|
| 2064 |
+
elif isinstance(node, ast.ImportFrom):
|
| 2065 |
+
if node.module:
|
| 2066 |
+
module_name = node.module.split('.')[0]
|
| 2067 |
+
if module_name not in builtin_modules and not module_name.startswith('_'):
|
| 2068 |
+
names = [alias.name for alias in node.names]
|
| 2069 |
+
import_statements.append(f"from {node.module} import {', '.join(names)}")
|
| 2070 |
+
|
| 2071 |
+
except SyntaxError:
|
| 2072 |
+
# Fallback: use regex to find import statements
|
| 2073 |
+
for line in code.split('\n'):
|
| 2074 |
+
line = line.strip()
|
| 2075 |
+
if line.startswith('import ') or line.startswith('from '):
|
| 2076 |
+
# Check if it's not a builtin module
|
| 2077 |
+
if line.startswith('import '):
|
| 2078 |
+
module_name = line.split()[1].split('.')[0]
|
| 2079 |
+
elif line.startswith('from '):
|
| 2080 |
+
module_name = line.split()[1].split('.')[0]
|
| 2081 |
+
|
| 2082 |
+
if module_name not in builtin_modules and not module_name.startswith('_'):
|
| 2083 |
+
import_statements.append(line)
|
| 2084 |
+
|
| 2085 |
+
return list(set(import_statements)) # Remove duplicates
|
| 2086 |
+
|
| 2087 |
+
def generate_requirements_txt_with_llm(import_statements):
|
| 2088 |
+
"""Generate requirements.txt content using LLM based on import statements."""
|
| 2089 |
+
if not import_statements:
|
| 2090 |
+
return "# No additional dependencies required\n"
|
| 2091 |
+
|
| 2092 |
+
# Use a lightweight model for this task
|
| 2093 |
+
try:
|
| 2094 |
+
client = get_inference_client("Qwen/Qwen3-Coder-480B-A35B", "auto")
|
| 2095 |
+
|
| 2096 |
+
imports_text = '\n'.join(import_statements)
|
| 2097 |
+
|
| 2098 |
+
prompt = f"""Based on the following Python import statements, generate a requirements.txt file with the necessary PyPI packages:
|
| 2099 |
+
|
| 2100 |
+
{imports_text}
|
| 2101 |
+
|
| 2102 |
+
Instructions:
|
| 2103 |
+
- Only include external packages that need to be installed via pip
|
| 2104 |
+
- Do not include Python built-in modules
|
| 2105 |
+
- Use the correct PyPI package names (e.g., cv2 -> opencv-python, PIL -> Pillow, sklearn -> scikit-learn)
|
| 2106 |
+
- Do not specify versions unless absolutely necessary for compatibility
|
| 2107 |
+
- One package per line
|
| 2108 |
+
- If no external packages are needed, return "# No additional dependencies required"
|
| 2109 |
+
|
| 2110 |
+
Requirements.txt:"""
|
| 2111 |
+
|
| 2112 |
+
messages = [
|
| 2113 |
+
{"role": "system", "content": "You are a Python packaging expert. Generate accurate requirements.txt files based on import statements."},
|
| 2114 |
+
{"role": "user", "content": prompt}
|
| 2115 |
+
]
|
| 2116 |
+
|
| 2117 |
+
response = client.chat.completions.create(
|
| 2118 |
+
model="Qwen/Qwen3-Coder-480B-A35B",
|
| 2119 |
+
messages=messages,
|
| 2120 |
+
max_tokens=1024,
|
| 2121 |
+
temperature=0.1
|
| 2122 |
+
)
|
| 2123 |
+
|
| 2124 |
+
requirements_content = response.choices[0].message.content.strip()
|
| 2125 |
+
|
| 2126 |
+
# Clean up the response in case it includes extra formatting
|
| 2127 |
+
if '```' in requirements_content:
|
| 2128 |
+
# Extract content between code blocks
|
| 2129 |
+
lines = requirements_content.split('\n')
|
| 2130 |
+
in_code_block = False
|
| 2131 |
+
clean_lines = []
|
| 2132 |
+
for line in lines:
|
| 2133 |
+
if line.strip().startswith('```'):
|
| 2134 |
+
in_code_block = not in_code_block
|
| 2135 |
+
continue
|
| 2136 |
+
if in_code_block:
|
| 2137 |
+
clean_lines.append(line)
|
| 2138 |
+
requirements_content = '\n'.join(clean_lines).strip()
|
| 2139 |
+
|
| 2140 |
+
# Ensure it ends with a newline
|
| 2141 |
+
if requirements_content and not requirements_content.endswith('\n'):
|
| 2142 |
+
requirements_content += '\n'
|
| 2143 |
+
|
| 2144 |
+
return requirements_content if requirements_content else "# No additional dependencies required\n"
|
| 2145 |
+
|
| 2146 |
+
except Exception as e:
|
| 2147 |
+
# Fallback: simple extraction with basic mapping
|
| 2148 |
+
dependencies = set()
|
| 2149 |
+
special_cases = {
|
| 2150 |
+
'cv2': 'opencv-python',
|
| 2151 |
+
'PIL': 'Pillow',
|
| 2152 |
+
'sklearn': 'scikit-learn',
|
| 2153 |
+
'skimage': 'scikit-image',
|
| 2154 |
+
'bs4': 'beautifulsoup4'
|
| 2155 |
+
}
|
| 2156 |
+
|
| 2157 |
+
for stmt in import_statements:
|
| 2158 |
+
if stmt.startswith('import '):
|
| 2159 |
+
module_name = stmt.split()[1].split('.')[0]
|
| 2160 |
+
package_name = special_cases.get(module_name, module_name)
|
| 2161 |
+
dependencies.add(package_name)
|
| 2162 |
+
elif stmt.startswith('from '):
|
| 2163 |
+
module_name = stmt.split()[1].split('.')[0]
|
| 2164 |
+
package_name = special_cases.get(module_name, module_name)
|
| 2165 |
+
dependencies.add(package_name)
|
| 2166 |
+
|
| 2167 |
+
if dependencies:
|
| 2168 |
+
return '\n'.join(sorted(dependencies)) + '\n'
|
| 2169 |
+
else:
|
| 2170 |
+
return "# No additional dependencies required\n"
|
| 2171 |
+
|
| 2172 |
def wrap_html_in_gradio_app(html_code):
|
| 2173 |
# Escape triple quotes for safe embedding
|
| 2174 |
safe_html = html_code.replace('"""', r'\"\"\"')
|
| 2175 |
+
|
| 2176 |
+
# Extract import statements and generate requirements.txt with LLM
|
| 2177 |
+
import_statements = extract_import_statements(html_code)
|
| 2178 |
+
requirements_comment = ""
|
| 2179 |
+
if import_statements:
|
| 2180 |
+
requirements_content = generate_requirements_txt_with_llm(import_statements)
|
| 2181 |
+
requirements_comment = (
|
| 2182 |
+
"# Generated requirements.txt content (create this file manually if needed):\n"
|
| 2183 |
+
+ '\n'.join(f"# {line}" for line in requirements_content.strip().split('\n')) + '\n\n'
|
| 2184 |
+
)
|
| 2185 |
+
|
| 2186 |
return (
|
| 2187 |
+
f'{requirements_comment}'
|
| 2188 |
'import gradio as gr\n\n'
|
| 2189 |
'def show_html():\n'
|
| 2190 |
f' return """{safe_html}"""\n\n'
|
|
|
|
| 2710 |
exist_ok=True
|
| 2711 |
)
|
| 2712 |
|
| 2713 |
+
# Generate and upload requirements.txt for Streamlit apps
|
| 2714 |
+
import_statements = extract_import_statements(code)
|
| 2715 |
+
requirements_content = generate_requirements_txt_with_llm(import_statements)
|
| 2716 |
+
|
| 2717 |
import tempfile
|
| 2718 |
+
|
| 2719 |
+
# Upload requirements.txt first
|
| 2720 |
+
try:
|
| 2721 |
+
with tempfile.NamedTemporaryFile("w", suffix=".txt", delete=False) as f:
|
| 2722 |
+
f.write(requirements_content)
|
| 2723 |
+
requirements_temp_path = f.name
|
| 2724 |
+
|
| 2725 |
+
api.upload_file(
|
| 2726 |
+
path_or_fileobj=requirements_temp_path,
|
| 2727 |
+
path_in_repo="requirements.txt",
|
| 2728 |
+
repo_id=repo_id,
|
| 2729 |
+
repo_type="space"
|
| 2730 |
+
)
|
| 2731 |
+
except Exception as e:
|
| 2732 |
+
error_msg = str(e)
|
| 2733 |
+
if "403 Forbidden" in error_msg and "write token" in error_msg:
|
| 2734 |
+
return gr.update(value=f"Error uploading requirements.txt: Permission denied. Please ensure you have write access to {repo_id} and your token has the correct permissions.", visible=True)
|
| 2735 |
+
else:
|
| 2736 |
+
return gr.update(value=f"Error uploading requirements.txt: {e}", visible=True)
|
| 2737 |
+
finally:
|
| 2738 |
+
import os
|
| 2739 |
+
if 'requirements_temp_path' in locals():
|
| 2740 |
+
os.unlink(requirements_temp_path)
|
| 2741 |
+
|
| 2742 |
+
# Upload the user's code to src/streamlit_app.py (for both new and existing spaces)
|
| 2743 |
with tempfile.NamedTemporaryFile("w", suffix=".py", delete=False) as f:
|
| 2744 |
f.write(code)
|
| 2745 |
temp_path = f.name
|
|
|
|
| 2996 |
import os
|
| 2997 |
os.unlink(temp_path)
|
| 2998 |
else:
|
| 2999 |
+
# Generate and upload requirements.txt for Gradio apps
|
| 3000 |
+
import_statements = extract_import_statements(code)
|
| 3001 |
+
requirements_content = generate_requirements_txt_with_llm(import_statements)
|
| 3002 |
+
|
| 3003 |
import tempfile
|
| 3004 |
+
|
| 3005 |
+
# Upload requirements.txt first
|
| 3006 |
+
try:
|
| 3007 |
+
with tempfile.NamedTemporaryFile("w", suffix=".txt", delete=False) as f:
|
| 3008 |
+
f.write(requirements_content)
|
| 3009 |
+
requirements_temp_path = f.name
|
| 3010 |
+
|
| 3011 |
+
api.upload_file(
|
| 3012 |
+
path_or_fileobj=requirements_temp_path,
|
| 3013 |
+
path_in_repo="requirements.txt",
|
| 3014 |
+
repo_id=repo_id,
|
| 3015 |
+
repo_type="space"
|
| 3016 |
+
)
|
| 3017 |
+
except Exception as e:
|
| 3018 |
+
error_msg = str(e)
|
| 3019 |
+
if "403 Forbidden" in error_msg and "write token" in error_msg:
|
| 3020 |
+
return gr.update(value=f"Error uploading requirements.txt: Permission denied. Please ensure you have write access to {repo_id} and your token has the correct permissions.", visible=True)
|
| 3021 |
+
else:
|
| 3022 |
+
return gr.update(value=f"Error uploading requirements.txt: {e}", visible=True)
|
| 3023 |
+
finally:
|
| 3024 |
+
import os
|
| 3025 |
+
if 'requirements_temp_path' in locals():
|
| 3026 |
+
os.unlink(requirements_temp_path)
|
| 3027 |
+
|
| 3028 |
+
# Now upload the main app.py file
|
| 3029 |
+
file_name = "app.py"
|
| 3030 |
with tempfile.NamedTemporaryFile("w", suffix=f".{file_name.split('.')[-1]}", delete=False) as f:
|
| 3031 |
f.write(code)
|
| 3032 |
temp_path = f.name
|