akhaliq HF Staff commited on
Commit
bdf5606
·
1 Parent(s): 531521f

update requirements for gradio and streamlit

Browse files
Files changed (1) hide show
  1. app.py +210 -2
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
- # Upload the user's code to src/streamlit_app.py (for both new and existing spaces)
 
 
 
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
- file_name = "app.py"
 
 
 
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