diff --git a/.gitattributes b/.gitattributes
index 1abc6d15eb80582bc2bc48e8b790dd790b00d25b..a6344aac8c09253b3b630fb776ae94478aa0275b 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1 +1,35 @@
-Animated_Logo_Video_Ready.gif filter=lfs diff=lfs merge=lfs -text
+*.7z filter=lfs diff=lfs merge=lfs -text
+*.arrow filter=lfs diff=lfs merge=lfs -text
+*.bin filter=lfs diff=lfs merge=lfs -text
+*.bz2 filter=lfs diff=lfs merge=lfs -text
+*.ckpt filter=lfs diff=lfs merge=lfs -text
+*.ftz filter=lfs diff=lfs merge=lfs -text
+*.gz filter=lfs diff=lfs merge=lfs -text
+*.h5 filter=lfs diff=lfs merge=lfs -text
+*.joblib filter=lfs diff=lfs merge=lfs -text
+*.lfs.* filter=lfs diff=lfs merge=lfs -text
+*.mlmodel filter=lfs diff=lfs merge=lfs -text
+*.model filter=lfs diff=lfs merge=lfs -text
+*.msgpack filter=lfs diff=lfs merge=lfs -text
+*.npy filter=lfs diff=lfs merge=lfs -text
+*.npz filter=lfs diff=lfs merge=lfs -text
+*.onnx filter=lfs diff=lfs merge=lfs -text
+*.ot filter=lfs diff=lfs merge=lfs -text
+*.parquet filter=lfs diff=lfs merge=lfs -text
+*.pb filter=lfs diff=lfs merge=lfs -text
+*.pickle filter=lfs diff=lfs merge=lfs -text
+*.pkl filter=lfs diff=lfs merge=lfs -text
+*.pt filter=lfs diff=lfs merge=lfs -text
+*.pth filter=lfs diff=lfs merge=lfs -text
+*.rar filter=lfs diff=lfs merge=lfs -text
+*.safetensors filter=lfs diff=lfs merge=lfs -text
+saved_model/**/* filter=lfs diff=lfs merge=lfs -text
+*.tar.* filter=lfs diff=lfs merge=lfs -text
+*.tar filter=lfs diff=lfs merge=lfs -text
+*.tflite filter=lfs diff=lfs merge=lfs -text
+*.tgz filter=lfs diff=lfs merge=lfs -text
+*.wasm filter=lfs diff=lfs merge=lfs -text
+*.xz filter=lfs diff=lfs merge=lfs -text
+*.zip filter=lfs diff=lfs merge=lfs -text
+*.zst filter=lfs diff=lfs merge=lfs -text
+*tfevents* filter=lfs diff=lfs merge=lfs -text
diff --git a/.gitignore b/.gitignore
index 3f72906b798b3435d6ec00d45b2d8c4a002be398..0f25b866ecf0fddab1b5836b418c1966b98cb00f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
+.gradio/
+
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
@@ -19,18 +21,16 @@ lib64/
parts/
sdist/
var/
+wheels/
+share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
-# Virtual environments
-venv/
-env/
-ENV/
-.venv/
-
# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
@@ -48,34 +48,115 @@ htmlcov/
nosetests.xml
coverage.xml
*.cover
+*.py,cover
.hypothesis/
.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
# Jupyter Notebook
.ipynb_checkpoints
+# IPython
+profile_default/
+ipython_config.py
+
# pyenv
-.python-version
+# For a library or package, you might want to ignore these files since the code is
+# intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don't work, or not
+# install all needed dependencies.
+#Pipfile.lock
+
+# poetry
+# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
+# This is especially recommended for binary packages to ensure reproducibility, and is more
+# commonly ignored for libraries.
+# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
+#poetry.lock
+
+# pdm
+# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
+#pdm.lock
+# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
+# in version control.
+# https://pdm.fming.dev/#use-with-ide
+.pdm.toml
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
# mypy
.mypy_cache/
.dmypy.json
+dmypy.json
# Pyre type checker
.pyre/
-# Gradio cache
-log/
-logs/
-
-# System files
-.DS_Store
-Thumbs.db
+# pytype static type analyzer
+.pytype/
-# Lock files
-uv.lock
-poetry.lock
-Pipfile.lock
+# Cython debug symbols
+cython_debug/
-# VSCode
-.vscode/
\ No newline at end of file
+# PyCharm
+# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
+# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
+# and can be added to the global gitignore or merged into this file. For a more nuclear
+# option (not recommended) you can uncomment the following to ignore the entire idea folder.
+#.idea/
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..4bc0c623a659d38b912b9efd8443b18bc4922d49
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,32 @@
+repos:
+ - repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v5.0.0
+ hooks:
+ - id: check-executables-have-shebangs
+ - id: check-json
+ - id: check-merge-conflict
+ - id: check-shebang-scripts-are-executable
+ - id: check-toml
+ - id: check-yaml
+ - id: end-of-file-fixer
+ - id: mixed-line-ending
+ args: ["--fix=lf"]
+ - id: requirements-txt-fixer
+ - id: trailing-whitespace
+ - repo: https://github.com/astral-sh/ruff-pre-commit
+ rev: v0.8.6
+ hooks:
+ - id: ruff
+ args: ["--fix"]
+ - repo: https://github.com/pre-commit/mirrors-mypy
+ rev: v1.14.1
+ hooks:
+ - id: mypy
+ args: ["--ignore-missing-imports"]
+ additional_dependencies:
+ [
+ "types-python-slugify",
+ "types-requests",
+ "types-PyYAML",
+ "types-pytz",
+ ]
diff --git a/.python-version b/.python-version
new file mode 100644
index 0000000000000000000000000000000000000000..c8cfe3959183f8e9a50f83f54cd723f2dc9c252d
--- /dev/null
+++ b/.python-version
@@ -0,0 +1 @@
+3.10
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 0000000000000000000000000000000000000000..854d34838a8695341a86e1cfa239be0014098058
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,8 @@
+{
+ "recommendations": [
+ "ms-python.python",
+ "charliermarsh.ruff",
+ "streetsidesoftware.code-spell-checker",
+ "tamasfe.even-better-toml"
+ ]
+}
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000000000000000000000000000000000000..6b1ad68d2449998e1085249c2f4828e886ff47e5
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,16 @@
+{
+ "editor.formatOnSave": true,
+ "files.insertFinalNewline": false,
+ "[python]": {
+ "editor.defaultFormatter": "charliermarsh.ruff",
+ "editor.formatOnType": true,
+ "editor.codeActionsOnSave": {
+ "source.fixAll.ruff": "explicit"
+ }
+ },
+ "[jupyter]": {
+ "files.insertFinalNewline": false
+ },
+ "notebook.output.scrolling": true,
+ "notebook.formatOnSave.enabled": true
+}
diff --git a/README.md b/README.md
index ef821fedd97d9865aef70afe03b272c77cb2fd18..282f3700195e90b2e5a2446f8d96915eeb7cd3b9 100644
--- a/README.md
+++ b/README.md
@@ -4,363 +4,10 @@ emoji: 🏢
colorFrom: indigo
colorTo: indigo
sdk: gradio
-sdk_version: 5.38.0
+sdk_version: 5.23.3
app_file: app.py
pinned: false
disable_embedding: true
-hf_oauth: true
-hf_oauth_scopes:
- - manage-repos # Needed to create/upload/delete Spaces on behalf of the user
-# Note: 'openid' and 'profile' are always included by default and should not be listed here.
---
-Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
-
-# AnyCoder - AI Code Generator
-
-AnyCoder is an AI-powered code generator that helps you create applications by describing them in plain English. It supports multiple AI models and can generate HTML/CSS/JavaScript code for web applications.
-
-## Features
-
-- **Multi-Model Support**: Choose from various AI models including DeepSeek, ERNIE-4.5-VL, MiniMax, and Qwen
-- **Image-to-Code**: Upload UI design images and get corresponding HTML/CSS code (ERNIE-4.5-VL model)
-- **Image Text Extraction**: Upload images and extract text using OCR for processing
-- **Website Redesign**: Enter a website URL to extract content and redesign it with modern, responsive layouts
-- **Live Preview**: See your generated code in action with the built-in sandbox
-- **Web Search Integration**: Enable real-time web search to get the latest information and best practices
-- **Chat History**: Keep track of your conversations and generated code
-- **Quick Examples**: Pre-built examples to get you started quickly
-- **🚀 One-Click Deployment**: Deploy your generated applications directly to Hugging Face Spaces
-
-## Installation
-
-1. Clone the repository:
-```bash
-git clone Old Title
-{DIVIDER}
- New Title
-{REPLACE_END}
-{SEARCH_START}
-
-{DIVIDER} - - -{REPLACE_END} -``` -Example Deleting Code: -``` -Removing the paragraph... -{SEARCH_START} -
This paragraph will be deleted.
-{DIVIDER} -{REPLACE_END} -```""" - -# Available models -AVAILABLE_MODELS = [ - { - "name": "Moonshot Kimi-K2", - "id": "moonshotai/Kimi-K2-Instruct", - "description": "Moonshot AI Kimi-K2-Instruct model for code generation and general tasks" - }, - { - "name": "DeepSeek V3", - "id": "deepseek-ai/DeepSeek-V3-0324", - "description": "DeepSeek V3 model for code generation" - }, - { - "name": "DeepSeek R1", - "id": "deepseek-ai/DeepSeek-R1-0528", - "description": "DeepSeek R1 model for code generation" - }, - { - "name": "ERNIE-4.5-VL", - "id": "baidu/ERNIE-4.5-VL-424B-A47B-Base-PT", - "description": "ERNIE-4.5-VL model for multimodal code generation with image support" - }, - { - "name": "MiniMax M1", - "id": "MiniMaxAI/MiniMax-M1-80k", - "description": "MiniMax M1 model for code generation and general tasks" - }, - { - "name": "Qwen3-235B-A22B", - "id": "Qwen/Qwen3-235B-A22B", - "description": "Qwen3-235B-A22B model for code generation and general tasks" - }, - { - "name": "SmolLM3-3B", - "id": "HuggingFaceTB/SmolLM3-3B", - "description": "SmolLM3-3B model for code generation and general tasks" - }, - { - "name": "GLM-4.1V-9B-Thinking", - "id": "THUDM/GLM-4.1V-9B-Thinking", - "description": "GLM-4.1V-9B-Thinking model for multimodal code generation with image support" - } -] - -DEMO_LIST = [ - { - "title": "Todo App", - "description": "Create a simple todo application with add, delete, and mark as complete functionality" - }, - { - "title": "Calculator", - "description": "Build a basic calculator with addition, subtraction, multiplication, and division" - }, - { - "title": "Chat Interface", - "description": "Build a chat interface with message history and user input" - }, - { - "title": "E-commerce Product Card", - "description": "Create a product card component for an e-commerce website" - }, - { - "title": "Login Form", - "description": "Build a responsive login form with validation" - }, - { - "title": "Dashboard Layout", - "description": "Create a dashboard layout with sidebar navigation and main content area" - }, - { - "title": "Data Table", - "description": "Build a data table with sorting and filtering capabilities" - }, - { - "title": "Image Gallery", - "description": "Create an image gallery with lightbox functionality and responsive grid layout" - }, - { - "title": "UI from Image", - "description": "Upload an image of a UI design and I'll generate the HTML/CSS code for it" - }, - { - "title": "Extract Text from Image", - "description": "Upload an image containing text and I'll extract and process the text content" - }, - { - "title": "Website Redesign", - "description": "Enter a website URL to extract its content and redesign it with a modern, responsive layout" - }, - { - "title": "Modify HTML", - "description": "After generating HTML, ask me to modify it with specific changes using search/replace format" - }, - { - "title": "Search/Replace Example", - "description": "Generate HTML first, then ask: 'Change the title to My New Title' or 'Add a blue background to the body'" - } -] - -# HF Inference Client -HF_TOKEN = os.getenv('HF_TOKEN') -if not HF_TOKEN: - raise RuntimeError("HF_TOKEN environment variable is not set. Please set it to your Hugging Face API token.") - -def get_inference_client(model_id, provider="auto"): - """Return an InferenceClient with provider based on model_id and user selection.""" - return InferenceClient( - provider=provider, - api_key=HF_TOKEN, - bill_to="huggingface" - ) - -# Type definitions -History = List[Tuple[str, str]] -Messages = List[Dict[str, str]] - -# Tavily Search Client -TAVILY_API_KEY = os.getenv('TAVILY_API_KEY') -tavily_client = None -if TAVILY_API_KEY: - try: - tavily_client = TavilyClient(api_key=TAVILY_API_KEY) - except Exception as e: - print(f"Failed to initialize Tavily client: {e}") - tavily_client = None - -def history_to_messages(history: History, system: str) -> Messages: - messages = [{'role': 'system', 'content': system}] - for h in history: - # Handle multimodal content in history - user_content = h[0] - if isinstance(user_content, list): - # Extract text from multimodal content - text_content = "" - for item in user_content: - if isinstance(item, dict) and item.get("type") == "text": - text_content += item.get("text", "") - user_content = text_content if text_content else str(user_content) - - messages.append({'role': 'user', 'content': user_content}) - messages.append({'role': 'assistant', 'content': h[1]}) - return messages - -def messages_to_history(messages: Messages) -> Tuple[str, History]: - assert messages[0]['role'] == 'system' - history = [] - for q, r in zip(messages[1::2], messages[2::2]): - # Extract text content from multimodal messages for history - user_content = q['content'] - if isinstance(user_content, list): - text_content = "" - for item in user_content: - if isinstance(item, dict) and item.get("type") == "text": - text_content += item.get("text", "") - user_content = text_content if text_content else str(user_content) - - history.append([user_content, r['content']]) - return history - -def history_to_chatbot_messages(history: History) -> List[Dict[str, str]]: - """Convert history tuples to chatbot message format""" - messages = [] - for user_msg, assistant_msg in history: - # Handle multimodal content - if isinstance(user_msg, list): - text_content = "" - for item in user_msg: - if isinstance(item, dict) and item.get("type") == "text": - text_content += item.get("text", "") - user_msg = text_content if text_content else str(user_msg) - - messages.append({"role": "user", "content": user_msg}) - messages.append({"role": "assistant", "content": assistant_msg}) - return messages - -def remove_code_block(text): - # Try to match code blocks with language markers - patterns = [ - r'```(?:html|HTML)\n([\s\S]+?)\n```', # Match ```html or ```HTML - r'```\n([\s\S]+?)\n```', # Match code blocks without language markers - r'```([\s\S]+?)```' # Match code blocks without line breaks - ] - for pattern in patterns: - match = re.search(pattern, text, re.DOTALL) - if match: - extracted = match.group(1).strip() - return extracted - # If no code block is found, check if the entire text is HTML - if text.strip().startswith('') or text.strip().startswith(' str: - """Apply search/replace changes to HTML content""" - if not changes_text.strip(): - return original_html - - # Split the changes text into individual search/replace blocks - blocks = [] - current_block = "" - lines = changes_text.split('\n') - - for line in lines: - if line.strip() == SEARCH_START: - if current_block.strip(): - blocks.append(current_block.strip()) - current_block = line + '\n' - elif line.strip() == REPLACE_END: - current_block += line + '\n' - blocks.append(current_block.strip()) - current_block = "" - else: - current_block += line + '\n' - - if current_block.strip(): - blocks.append(current_block.strip()) - - modified_html = original_html - - for block in blocks: - if not block.strip(): - continue - - # Parse the search/replace block - lines = block.split('\n') - search_lines = [] - replace_lines = [] - in_search = False - in_replace = False - - for line in lines: - if line.strip() == SEARCH_START: - in_search = True - in_replace = False - elif line.strip() == DIVIDER: - in_search = False - in_replace = True - elif line.strip() == REPLACE_END: - in_replace = False - elif in_search: - search_lines.append(line) - elif in_replace: - replace_lines.append(line) - - # Apply the search/replace - if search_lines: - search_text = '\n'.join(search_lines).strip() - replace_text = '\n'.join(replace_lines).strip() - - if search_text in modified_html: - modified_html = modified_html.replace(search_text, replace_text) - else: - print(f"Warning: Search text not found in HTML: {search_text[:100]}...") - - return modified_html - -# Updated for faster Tavily search and closer prompt usage -# Uses 'advanced' search_depth and auto_parameters=True for speed and relevance - -def perform_web_search(query: str, max_results: int = 5, include_domains=None, exclude_domains=None) -> str: - """Perform web search using Tavily with default parameters""" - if not tavily_client: - return "Web search is not available. Please set the TAVILY_API_KEY environment variable." - - try: - # Use Tavily defaults with advanced search depth for better results - search_params = { - "search_depth": "advanced", - "max_results": min(max(1, max_results), 20) - } - if include_domains is not None: - search_params["include_domains"] = include_domains - if exclude_domains is not None: - search_params["exclude_domains"] = exclude_domains - - response = tavily_client.search(query, **search_params) - - search_results = [] - for result in response.get('results', []): - title = result.get('title', 'No title') - url = result.get('url', 'No URL') - content = result.get('content', 'No content') - search_results.append(f"Title: {title}\nURL: {url}\nContent: {content}\n") - - if search_results: - return "Web Search Results:\n\n" + "\n---\n".join(search_results) - else: - return "No search results found." - - except Exception as e: - return f"Search error: {str(e)}" - -def enhance_query_with_search(query: str, enable_search: bool) -> str: - """Enhance the query with web search results if search is enabled""" - if not enable_search or not tavily_client: - return query - - # Perform search to get relevant information - search_results = perform_web_search(query) - - # Combine original query with search results - enhanced_query = f"""Original Query: {query} - -{search_results} - -Please use the search results above to help create the requested application with the most up-to-date information and best practices.""" - - return enhanced_query - -def send_to_sandbox(code): - # Add a wrapper to inject necessary permissions and ensure full HTML - wrapped_code = f""" - - -
- - - - -