Spaces:
Running
Running
update
Browse files
app.py
CHANGED
|
@@ -513,10 +513,10 @@ def create_multimodal_message(text, image=None):
|
|
| 513 |
|
| 514 |
return {"role": "user", "content": content}
|
| 515 |
|
| 516 |
-
def apply_search_replace_changes(
|
| 517 |
-
"""Apply search/replace changes to HTML
|
| 518 |
if not changes_text.strip():
|
| 519 |
-
return
|
| 520 |
|
| 521 |
# Split the changes text into individual search/replace blocks
|
| 522 |
blocks = []
|
|
@@ -538,7 +538,7 @@ def apply_search_replace_changes(original_html: str, changes_text: str) -> str:
|
|
| 538 |
if current_block.strip():
|
| 539 |
blocks.append(current_block.strip())
|
| 540 |
|
| 541 |
-
|
| 542 |
|
| 543 |
for block in blocks:
|
| 544 |
if not block.strip():
|
|
@@ -570,12 +570,12 @@ def apply_search_replace_changes(original_html: str, changes_text: str) -> str:
|
|
| 570 |
search_text = '\n'.join(search_lines).strip()
|
| 571 |
replace_text = '\n'.join(replace_lines).strip()
|
| 572 |
|
| 573 |
-
if search_text in
|
| 574 |
-
|
| 575 |
else:
|
| 576 |
-
print(f"Warning: Search text not found in
|
| 577 |
|
| 578 |
-
return
|
| 579 |
|
| 580 |
# Updated for faster Tavily search and closer prompt usage
|
| 581 |
# Uses 'advanced' search_depth and auto_parameters=True for speed and relevance
|
|
@@ -1094,17 +1094,23 @@ def generation_code(query: Optional[str], image: Optional[gr.Image], file: Optio
|
|
| 1094 |
_history = []
|
| 1095 |
_history = [h for h in _history if isinstance(h, list) and len(h) == 2]
|
| 1096 |
|
| 1097 |
-
# Check if there's existing
|
| 1098 |
-
|
| 1099 |
last_assistant_msg = ""
|
| 1100 |
if _history and len(_history[-1]) > 1:
|
| 1101 |
last_assistant_msg = _history[-1][1]
|
| 1102 |
-
|
| 1103 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1104 |
|
| 1105 |
# Choose system prompt based on context
|
| 1106 |
-
if
|
| 1107 |
-
# Use follow-up prompt for modifying existing
|
| 1108 |
system_prompt = FollowUpSystemPrompt
|
| 1109 |
else:
|
| 1110 |
# Use language-specific prompt
|
|
@@ -1260,22 +1266,24 @@ This will help me create a better design for you."""
|
|
| 1260 |
}
|
| 1261 |
else:
|
| 1262 |
clean_code = remove_code_block(content)
|
| 1263 |
-
if
|
| 1264 |
-
#
|
| 1265 |
if clean_code.strip().startswith("<!DOCTYPE html>") or clean_code.strip().startswith("<html"):
|
|
|
|
| 1266 |
yield {
|
| 1267 |
code_output: gr.update(value=clean_code, language=get_gradio_language(language)),
|
| 1268 |
history_output: history_to_chatbot_messages(_history),
|
| 1269 |
sandbox: send_to_sandbox(clean_code) if language == "html" else "<div style='padding:1em;color:#888;text-align:center;'>Preview is only available for HTML. Please download your code using the download button above.</div>",
|
| 1270 |
}
|
| 1271 |
else:
|
| 1272 |
-
|
| 1273 |
-
|
| 1274 |
-
|
|
|
|
| 1275 |
yield {
|
| 1276 |
-
code_output: gr.update(value=
|
| 1277 |
history_output: history_to_chatbot_messages(_history),
|
| 1278 |
-
sandbox: send_to_sandbox(
|
| 1279 |
}
|
| 1280 |
else:
|
| 1281 |
yield {
|
|
@@ -1307,21 +1315,23 @@ This will help me create a better design for you."""
|
|
| 1307 |
sandbox: "<div style='padding:1em;color:#888;text-align:center;'>Error parsing transformers.js output. Please try again.</div>",
|
| 1308 |
history_output: history_to_chatbot_messages(_history),
|
| 1309 |
}
|
| 1310 |
-
elif
|
| 1311 |
-
#
|
| 1312 |
final_code = remove_code_block(content)
|
| 1313 |
if final_code.strip().startswith("<!DOCTYPE html>") or final_code.strip().startswith("<html"):
|
| 1314 |
-
|
|
|
|
| 1315 |
else:
|
| 1316 |
-
|
| 1317 |
-
|
| 1318 |
-
|
| 1319 |
-
|
| 1320 |
-
|
|
|
|
| 1321 |
yield {
|
| 1322 |
-
code_output:
|
| 1323 |
history: _history,
|
| 1324 |
-
sandbox: send_to_sandbox(
|
| 1325 |
history_output: history_to_chatbot_messages(_history),
|
| 1326 |
}
|
| 1327 |
else:
|
|
@@ -1393,6 +1403,114 @@ def deploy_to_spaces_static(code):
|
|
| 1393 |
full_url = f"{base_url}?{params}&{files_params}"
|
| 1394 |
webbrowser.open_new_tab(full_url)
|
| 1395 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1396 |
# Main application
|
| 1397 |
with gr.Blocks(
|
| 1398 |
theme=gr.themes.Base(
|
|
@@ -1417,6 +1535,20 @@ with gr.Blocks(
|
|
| 1417 |
|
| 1418 |
with gr.Sidebar():
|
| 1419 |
login_button = gr.LoginButton()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1420 |
input = gr.Textbox(
|
| 1421 |
label="What would you like to build?",
|
| 1422 |
placeholder="Describe your application...",
|
|
@@ -1529,6 +1661,44 @@ with gr.Blocks(
|
|
| 1529 |
with gr.Tab("History"):
|
| 1530 |
history_output = gr.Chatbot(show_label=False, height=400, type="messages")
|
| 1531 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1532 |
# Event handlers
|
| 1533 |
def update_code_language(language):
|
| 1534 |
return gr.update(language=get_gradio_language(language))
|
|
@@ -1563,6 +1733,13 @@ with gr.Blocks(
|
|
| 1563 |
def hide_deploy_components(*args):
|
| 1564 |
return [gr.Textbox(visible=False), gr.Dropdown(visible=False), gr.Button(visible=False)]
|
| 1565 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1566 |
btn.click(
|
| 1567 |
generation_code,
|
| 1568 |
inputs=[input, image_input, file_input, website_url_input, setting, history, current_model, search_toggle, language_dropdown, provider_state],
|
|
@@ -1577,6 +1754,11 @@ with gr.Blocks(
|
|
| 1577 |
language_dropdown.change(preview_logic, inputs=[code_output, language_dropdown], outputs=sandbox)
|
| 1578 |
clear_btn.click(clear_history, outputs=[history, history_output, file_input, website_url_input])
|
| 1579 |
clear_btn.click(hide_deploy_components, None, [space_name_input, sdk_dropdown, deploy_btn])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1580 |
|
| 1581 |
# Deploy to Spaces logic
|
| 1582 |
|
|
@@ -1592,8 +1774,34 @@ with gr.Blocks(
|
|
| 1592 |
return gr.update(value="No code to deploy.", visible=True)
|
| 1593 |
if profile is None or token is None:
|
| 1594 |
return gr.update(value="Please log in with your Hugging Face account to deploy to your own Space. Otherwise, use the default deploy (opens in new tab).", visible=True)
|
| 1595 |
-
|
| 1596 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1597 |
# Map SDK name to HF SDK slug
|
| 1598 |
sdk_map = {
|
| 1599 |
"Gradio (Python)": "gradio",
|
|
@@ -1602,9 +1810,11 @@ with gr.Blocks(
|
|
| 1602 |
"Transformers.js": "static" # Transformers.js uses static SDK
|
| 1603 |
}
|
| 1604 |
sdk = sdk_map.get(sdk_name, "gradio")
|
|
|
|
|
|
|
| 1605 |
api = HfApi(token=token.token)
|
| 1606 |
-
# Only create the repo for non-Transformers.js and non-Streamlit SDKs
|
| 1607 |
-
if sdk != "docker" and sdk_name != "Transformers.js":
|
| 1608 |
try:
|
| 1609 |
api.create_repo(
|
| 1610 |
repo_id=repo_id, # e.g. username/space_name
|
|
@@ -1615,7 +1825,7 @@ with gr.Blocks(
|
|
| 1615 |
except Exception as e:
|
| 1616 |
return gr.update(value=f"Error creating Space: {e}", visible=True)
|
| 1617 |
# Streamlit/docker logic
|
| 1618 |
-
if sdk == "docker":
|
| 1619 |
try:
|
| 1620 |
# Use duplicate_space to create a Streamlit template space
|
| 1621 |
from huggingface_hub import duplicate_space
|
|
@@ -1642,9 +1852,14 @@ with gr.Blocks(
|
|
| 1642 |
repo_type="space"
|
| 1643 |
)
|
| 1644 |
space_url = f"https://huggingface.co/spaces/{repo_id}"
|
| 1645 |
-
|
|
|
|
| 1646 |
except Exception as e:
|
| 1647 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1648 |
finally:
|
| 1649 |
import os
|
| 1650 |
os.unlink(temp_path)
|
|
@@ -1652,7 +1867,7 @@ with gr.Blocks(
|
|
| 1652 |
except Exception as e:
|
| 1653 |
return gr.update(value=f"Error duplicating Streamlit space: {e}", visible=True)
|
| 1654 |
# Transformers.js logic
|
| 1655 |
-
elif sdk_name == "Transformers.js":
|
| 1656 |
try:
|
| 1657 |
# Use duplicate_space to create a transformers.js template space
|
| 1658 |
from huggingface_hub import duplicate_space
|
|
@@ -1689,7 +1904,11 @@ with gr.Blocks(
|
|
| 1689 |
repo_type="space"
|
| 1690 |
)
|
| 1691 |
except Exception as e:
|
| 1692 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1693 |
finally:
|
| 1694 |
import os
|
| 1695 |
os.unlink(temp_path)
|
|
@@ -1707,7 +1926,11 @@ with gr.Blocks(
|
|
| 1707 |
repo_type="space"
|
| 1708 |
)
|
| 1709 |
except Exception as e:
|
| 1710 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1711 |
finally:
|
| 1712 |
import os
|
| 1713 |
os.unlink(temp_path)
|
|
@@ -1725,9 +1948,14 @@ with gr.Blocks(
|
|
| 1725 |
repo_type="space"
|
| 1726 |
)
|
| 1727 |
space_url = f"https://huggingface.co/spaces/{repo_id}"
|
| 1728 |
-
|
|
|
|
| 1729 |
except Exception as e:
|
| 1730 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1731 |
finally:
|
| 1732 |
import os
|
| 1733 |
os.unlink(temp_path)
|
|
@@ -1753,12 +1981,16 @@ with gr.Blocks(
|
|
| 1753 |
repo_type="space"
|
| 1754 |
)
|
| 1755 |
space_url = f"https://huggingface.co/spaces/{repo_id}"
|
| 1756 |
-
|
|
|
|
| 1757 |
except Exception as e:
|
| 1758 |
-
|
|
|
|
|
|
|
|
|
|
| 1759 |
time.sleep(2) # Wait before retrying
|
| 1760 |
else:
|
| 1761 |
-
return gr.update(value=f"Error uploading file after {max_attempts} attempts: {e}.
|
| 1762 |
finally:
|
| 1763 |
import os
|
| 1764 |
os.unlink(temp_path)
|
|
@@ -1776,9 +2008,14 @@ with gr.Blocks(
|
|
| 1776 |
repo_type="space"
|
| 1777 |
)
|
| 1778 |
space_url = f"https://huggingface.co/spaces/{repo_id}"
|
| 1779 |
-
|
|
|
|
| 1780 |
except Exception as e:
|
| 1781 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1782 |
finally:
|
| 1783 |
import os
|
| 1784 |
os.unlink(temp_path)
|
|
|
|
| 513 |
|
| 514 |
return {"role": "user", "content": content}
|
| 515 |
|
| 516 |
+
def apply_search_replace_changes(original_content: str, changes_text: str) -> str:
|
| 517 |
+
"""Apply search/replace changes to content (HTML, Python, etc.)"""
|
| 518 |
if not changes_text.strip():
|
| 519 |
+
return original_content
|
| 520 |
|
| 521 |
# Split the changes text into individual search/replace blocks
|
| 522 |
blocks = []
|
|
|
|
| 538 |
if current_block.strip():
|
| 539 |
blocks.append(current_block.strip())
|
| 540 |
|
| 541 |
+
modified_content = original_content
|
| 542 |
|
| 543 |
for block in blocks:
|
| 544 |
if not block.strip():
|
|
|
|
| 570 |
search_text = '\n'.join(search_lines).strip()
|
| 571 |
replace_text = '\n'.join(replace_lines).strip()
|
| 572 |
|
| 573 |
+
if search_text in modified_content:
|
| 574 |
+
modified_content = modified_content.replace(search_text, replace_text)
|
| 575 |
else:
|
| 576 |
+
print(f"Warning: Search text not found in content: {search_text[:100]}...")
|
| 577 |
|
| 578 |
+
return modified_content
|
| 579 |
|
| 580 |
# Updated for faster Tavily search and closer prompt usage
|
| 581 |
# Uses 'advanced' search_depth and auto_parameters=True for speed and relevance
|
|
|
|
| 1094 |
_history = []
|
| 1095 |
_history = [h for h in _history if isinstance(h, list) and len(h) == 2]
|
| 1096 |
|
| 1097 |
+
# Check if there's existing content in history to determine if this is a modification request
|
| 1098 |
+
has_existing_content = False
|
| 1099 |
last_assistant_msg = ""
|
| 1100 |
if _history and len(_history[-1]) > 1:
|
| 1101 |
last_assistant_msg = _history[-1][1]
|
| 1102 |
+
# Check for various content types that indicate an existing project
|
| 1103 |
+
if ('<!DOCTYPE html>' in last_assistant_msg or
|
| 1104 |
+
'<html' in last_assistant_msg or
|
| 1105 |
+
'import gradio' in last_assistant_msg or
|
| 1106 |
+
'import streamlit' in last_assistant_msg or
|
| 1107 |
+
'def ' in last_assistant_msg and 'app' in last_assistant_msg or
|
| 1108 |
+
'IMPORTED PROJECT FROM HUGGING FACE SPACE' in last_assistant_msg):
|
| 1109 |
+
has_existing_content = True
|
| 1110 |
|
| 1111 |
# Choose system prompt based on context
|
| 1112 |
+
if has_existing_content:
|
| 1113 |
+
# Use follow-up prompt for modifying existing content
|
| 1114 |
system_prompt = FollowUpSystemPrompt
|
| 1115 |
else:
|
| 1116 |
# Use language-specific prompt
|
|
|
|
| 1266 |
}
|
| 1267 |
else:
|
| 1268 |
clean_code = remove_code_block(content)
|
| 1269 |
+
if has_existing_content:
|
| 1270 |
+
# Handle modification of existing content
|
| 1271 |
if clean_code.strip().startswith("<!DOCTYPE html>") or clean_code.strip().startswith("<html"):
|
| 1272 |
+
# Model returned a complete HTML file
|
| 1273 |
yield {
|
| 1274 |
code_output: gr.update(value=clean_code, language=get_gradio_language(language)),
|
| 1275 |
history_output: history_to_chatbot_messages(_history),
|
| 1276 |
sandbox: send_to_sandbox(clean_code) if language == "html" else "<div style='padding:1em;color:#888;text-align:center;'>Preview is only available for HTML. Please download your code using the download button above.</div>",
|
| 1277 |
}
|
| 1278 |
else:
|
| 1279 |
+
# Model returned search/replace changes - apply them
|
| 1280 |
+
last_content = _history[-1][1] if _history and len(_history[-1]) > 1 else ""
|
| 1281 |
+
modified_content = apply_search_replace_changes(last_content, clean_code)
|
| 1282 |
+
clean_content = remove_code_block(modified_content)
|
| 1283 |
yield {
|
| 1284 |
+
code_output: gr.update(value=clean_content, language=get_gradio_language(language)),
|
| 1285 |
history_output: history_to_chatbot_messages(_history),
|
| 1286 |
+
sandbox: send_to_sandbox(clean_content) if language == "html" else "<div style='padding:1em;color:#888;text-align:center;'>Preview is only available for HTML. Please download your code using the download button above.</div>",
|
| 1287 |
}
|
| 1288 |
else:
|
| 1289 |
yield {
|
|
|
|
| 1315 |
sandbox: "<div style='padding:1em;color:#888;text-align:center;'>Error parsing transformers.js output. Please try again.</div>",
|
| 1316 |
history_output: history_to_chatbot_messages(_history),
|
| 1317 |
}
|
| 1318 |
+
elif has_existing_content:
|
| 1319 |
+
# Handle modification of existing content
|
| 1320 |
final_code = remove_code_block(content)
|
| 1321 |
if final_code.strip().startswith("<!DOCTYPE html>") or final_code.strip().startswith("<html"):
|
| 1322 |
+
# Model returned a complete HTML file
|
| 1323 |
+
clean_content = final_code
|
| 1324 |
else:
|
| 1325 |
+
# Model returned search/replace changes - apply them
|
| 1326 |
+
last_content = _history[-1][1] if _history and len(_history[-1]) > 1 else ""
|
| 1327 |
+
modified_content = apply_search_replace_changes(last_content, final_code)
|
| 1328 |
+
clean_content = remove_code_block(modified_content)
|
| 1329 |
+
# Update history with the cleaned content
|
| 1330 |
+
_history.append([query, clean_content])
|
| 1331 |
yield {
|
| 1332 |
+
code_output: clean_content,
|
| 1333 |
history: _history,
|
| 1334 |
+
sandbox: send_to_sandbox(clean_content) if language == "html" else "<div style='padding:1em;color:#888;text-align:center;'>Preview is only available for HTML. Please download your code using the download button above.</div>",
|
| 1335 |
history_output: history_to_chatbot_messages(_history),
|
| 1336 |
}
|
| 1337 |
else:
|
|
|
|
| 1403 |
full_url = f"{base_url}?{params}&{files_params}"
|
| 1404 |
webbrowser.open_new_tab(full_url)
|
| 1405 |
|
| 1406 |
+
def check_hf_space_url(url: str) -> Tuple[bool, Optional[str], Optional[str]]:
|
| 1407 |
+
"""Check if URL is a valid Hugging Face Spaces URL and extract username/project"""
|
| 1408 |
+
import re
|
| 1409 |
+
|
| 1410 |
+
# Pattern to match HF Spaces URLs
|
| 1411 |
+
url_pattern = re.compile(
|
| 1412 |
+
r'^(https?://)?(huggingface\.co|hf\.co)/spaces/([\w-]+)/([\w-]+)$',
|
| 1413 |
+
re.IGNORECASE
|
| 1414 |
+
)
|
| 1415 |
+
|
| 1416 |
+
match = url_pattern.match(url.strip())
|
| 1417 |
+
if match:
|
| 1418 |
+
username = match.group(3)
|
| 1419 |
+
project_name = match.group(4)
|
| 1420 |
+
return True, username, project_name
|
| 1421 |
+
return False, None, None
|
| 1422 |
+
|
| 1423 |
+
def fetch_hf_space_content(username: str, project_name: str) -> str:
|
| 1424 |
+
"""Fetch content from a Hugging Face Space"""
|
| 1425 |
+
try:
|
| 1426 |
+
import requests
|
| 1427 |
+
from huggingface_hub import HfApi
|
| 1428 |
+
|
| 1429 |
+
# Try to get space info first
|
| 1430 |
+
api = HfApi()
|
| 1431 |
+
space_info = api.space_info(f"{username}/{project_name}")
|
| 1432 |
+
|
| 1433 |
+
# Try to fetch the main file based on SDK
|
| 1434 |
+
sdk = space_info.sdk
|
| 1435 |
+
main_file = None
|
| 1436 |
+
|
| 1437 |
+
if sdk == "static":
|
| 1438 |
+
main_file = "index.html"
|
| 1439 |
+
elif sdk == "gradio":
|
| 1440 |
+
main_file = "app.py"
|
| 1441 |
+
elif sdk == "streamlit":
|
| 1442 |
+
main_file = "streamlit_app.py"
|
| 1443 |
+
else:
|
| 1444 |
+
# Try common files
|
| 1445 |
+
for file in ["app.py", "index.html", "streamlit_app.py", "main.py"]:
|
| 1446 |
+
try:
|
| 1447 |
+
content = api.hf_hub_download(
|
| 1448 |
+
repo_id=f"{username}/{project_name}",
|
| 1449 |
+
filename=file,
|
| 1450 |
+
repo_type="space"
|
| 1451 |
+
)
|
| 1452 |
+
main_file = file
|
| 1453 |
+
break
|
| 1454 |
+
except:
|
| 1455 |
+
continue
|
| 1456 |
+
|
| 1457 |
+
if main_file:
|
| 1458 |
+
content = api.hf_hub_download(
|
| 1459 |
+
repo_id=f"{username}/{project_name}",
|
| 1460 |
+
filename=main_file,
|
| 1461 |
+
repo_type="space"
|
| 1462 |
+
)
|
| 1463 |
+
|
| 1464 |
+
# Read the file content
|
| 1465 |
+
with open(content, 'r', encoding='utf-8') as f:
|
| 1466 |
+
file_content = f.read()
|
| 1467 |
+
|
| 1468 |
+
return f"""IMPORTED PROJECT FROM HUGGING FACE SPACE
|
| 1469 |
+
==============================================
|
| 1470 |
+
|
| 1471 |
+
Space: {username}/{project_name}
|
| 1472 |
+
SDK: {sdk}
|
| 1473 |
+
Main File: {main_file}
|
| 1474 |
+
|
| 1475 |
+
{file_content}"""
|
| 1476 |
+
else:
|
| 1477 |
+
return f"Error: Could not find main file in space {username}/{project_name}"
|
| 1478 |
+
|
| 1479 |
+
except Exception as e:
|
| 1480 |
+
return f"Error fetching space content: {str(e)}"
|
| 1481 |
+
|
| 1482 |
+
def load_project_from_url(url: str) -> Tuple[str, str]:
|
| 1483 |
+
"""Load project from Hugging Face Space URL"""
|
| 1484 |
+
# Validate URL
|
| 1485 |
+
is_valid, username, project_name = check_hf_space_url(url)
|
| 1486 |
+
|
| 1487 |
+
if not is_valid:
|
| 1488 |
+
return "Error: Please enter a valid Hugging Face Spaces URL.\n\nExpected format: https://huggingface.co/spaces/username/project", ""
|
| 1489 |
+
|
| 1490 |
+
# Fetch content
|
| 1491 |
+
content = fetch_hf_space_content(username, project_name)
|
| 1492 |
+
|
| 1493 |
+
if content.startswith("Error:"):
|
| 1494 |
+
return content, ""
|
| 1495 |
+
|
| 1496 |
+
# Extract the actual code content by removing metadata
|
| 1497 |
+
lines = content.split('\n')
|
| 1498 |
+
code_start = 0
|
| 1499 |
+
for i, line in enumerate(lines):
|
| 1500 |
+
# Skip metadata lines and find the start of actual code
|
| 1501 |
+
if (line.strip() and
|
| 1502 |
+
not line.startswith('=') and
|
| 1503 |
+
not line.startswith('IMPORTED PROJECT') and
|
| 1504 |
+
not line.startswith('Space:') and
|
| 1505 |
+
not line.startswith('SDK:') and
|
| 1506 |
+
not line.startswith('Main File:')):
|
| 1507 |
+
code_start = i
|
| 1508 |
+
break
|
| 1509 |
+
|
| 1510 |
+
code_content = '\n'.join(lines[code_start:])
|
| 1511 |
+
|
| 1512 |
+
return f"β
Successfully imported project from {username}/{project_name}", code_content
|
| 1513 |
+
|
| 1514 |
# Main application
|
| 1515 |
with gr.Blocks(
|
| 1516 |
theme=gr.themes.Base(
|
|
|
|
| 1535 |
|
| 1536 |
with gr.Sidebar():
|
| 1537 |
login_button = gr.LoginButton()
|
| 1538 |
+
|
| 1539 |
+
# Add Load Project section
|
| 1540 |
+
with gr.Group():
|
| 1541 |
+
gr.Markdown("**π₯ Load Existing Project**")
|
| 1542 |
+
load_project_url = gr.Textbox(
|
| 1543 |
+
label="Hugging Face Space URL",
|
| 1544 |
+
placeholder="https://huggingface.co/spaces/username/project",
|
| 1545 |
+
lines=1
|
| 1546 |
+
)
|
| 1547 |
+
load_project_btn = gr.Button("Import Project", variant="secondary", size="sm")
|
| 1548 |
+
load_project_status = gr.Markdown(visible=False)
|
| 1549 |
+
|
| 1550 |
+
gr.Markdown("---")
|
| 1551 |
+
|
| 1552 |
input = gr.Textbox(
|
| 1553 |
label="What would you like to build?",
|
| 1554 |
placeholder="Describe your application...",
|
|
|
|
| 1661 |
with gr.Tab("History"):
|
| 1662 |
history_output = gr.Chatbot(show_label=False, height=400, type="messages")
|
| 1663 |
|
| 1664 |
+
# Load project function
|
| 1665 |
+
def handle_load_project(url):
|
| 1666 |
+
if not url.strip():
|
| 1667 |
+
return gr.update(value="Please enter a URL.", visible=True)
|
| 1668 |
+
|
| 1669 |
+
status, code = load_project_from_url(url)
|
| 1670 |
+
|
| 1671 |
+
if code:
|
| 1672 |
+
# Extract space info for deployment
|
| 1673 |
+
is_valid, username, project_name = check_hf_space_url(url)
|
| 1674 |
+
space_info = f"{username}/{project_name}" if is_valid else ""
|
| 1675 |
+
|
| 1676 |
+
# Success - update the code output and show success message
|
| 1677 |
+
# Also update history to include the loaded project
|
| 1678 |
+
loaded_history = [[f"Loaded project from {url}", code]]
|
| 1679 |
+
return [
|
| 1680 |
+
gr.update(value=status, visible=True),
|
| 1681 |
+
gr.update(value=code, language="html"),
|
| 1682 |
+
gr.update(value=send_to_sandbox(code) if code.strip().startswith('<!DOCTYPE html>') or code.strip().startswith('<html') else "<div style='padding:1em;color:#888;text-align:center;'>Preview not available for this file type.</div>"),
|
| 1683 |
+
gr.update(value=""),
|
| 1684 |
+
loaded_history,
|
| 1685 |
+
history_to_chatbot_messages(loaded_history),
|
| 1686 |
+
gr.update(value=space_info, visible=True), # Update space name with loaded project
|
| 1687 |
+
gr.update(value="Update Existing Space", visible=True) # Change button text
|
| 1688 |
+
]
|
| 1689 |
+
else:
|
| 1690 |
+
# Error - just show error message
|
| 1691 |
+
return [
|
| 1692 |
+
gr.update(value=status, visible=True),
|
| 1693 |
+
gr.update(),
|
| 1694 |
+
gr.update(),
|
| 1695 |
+
gr.update(),
|
| 1696 |
+
[],
|
| 1697 |
+
[],
|
| 1698 |
+
gr.update(value="", visible=False),
|
| 1699 |
+
gr.update(value="π Deploy App", visible=False)
|
| 1700 |
+
]
|
| 1701 |
+
|
| 1702 |
# Event handlers
|
| 1703 |
def update_code_language(language):
|
| 1704 |
return gr.update(language=get_gradio_language(language))
|
|
|
|
| 1733 |
def hide_deploy_components(*args):
|
| 1734 |
return [gr.Textbox(visible=False), gr.Dropdown(visible=False), gr.Button(visible=False)]
|
| 1735 |
|
| 1736 |
+
# Load project button event
|
| 1737 |
+
load_project_btn.click(
|
| 1738 |
+
handle_load_project,
|
| 1739 |
+
inputs=[load_project_url],
|
| 1740 |
+
outputs=[load_project_status, code_output, sandbox, load_project_url, history, history_output, space_name_input, deploy_btn]
|
| 1741 |
+
)
|
| 1742 |
+
|
| 1743 |
btn.click(
|
| 1744 |
generation_code,
|
| 1745 |
inputs=[input, image_input, file_input, website_url_input, setting, history, current_model, search_toggle, language_dropdown, provider_state],
|
|
|
|
| 1754 |
language_dropdown.change(preview_logic, inputs=[code_output, language_dropdown], outputs=sandbox)
|
| 1755 |
clear_btn.click(clear_history, outputs=[history, history_output, file_input, website_url_input])
|
| 1756 |
clear_btn.click(hide_deploy_components, None, [space_name_input, sdk_dropdown, deploy_btn])
|
| 1757 |
+
# Reset space name and button text when clearing
|
| 1758 |
+
clear_btn.click(
|
| 1759 |
+
lambda: [gr.update(value=""), gr.update(value="π Deploy App")],
|
| 1760 |
+
outputs=[space_name_input, deploy_btn]
|
| 1761 |
+
)
|
| 1762 |
|
| 1763 |
# Deploy to Spaces logic
|
| 1764 |
|
|
|
|
| 1774 |
return gr.update(value="No code to deploy.", visible=True)
|
| 1775 |
if profile is None or token is None:
|
| 1776 |
return gr.update(value="Please log in with your Hugging Face account to deploy to your own Space. Otherwise, use the default deploy (opens in new tab).", visible=True)
|
| 1777 |
+
|
| 1778 |
+
# Check if token has write permissions
|
| 1779 |
+
if not token.token or token.token == "hf_":
|
| 1780 |
+
return gr.update(value="Error: Invalid token. Please log in again with your Hugging Face account to get a valid write token.", visible=True)
|
| 1781 |
+
|
| 1782 |
+
# Check if this is an update to an existing space (contains /)
|
| 1783 |
+
is_update = "/" in space_name.strip()
|
| 1784 |
+
if is_update:
|
| 1785 |
+
# This is an existing space, use the provided space_name as repo_id
|
| 1786 |
+
repo_id = space_name.strip()
|
| 1787 |
+
# Extract username from repo_id for permission check
|
| 1788 |
+
space_username = repo_id.split('/')[0]
|
| 1789 |
+
if space_username != profile.username:
|
| 1790 |
+
return gr.update(value=f"Error: You can only update your own spaces. This space belongs to {space_username}.", visible=True)
|
| 1791 |
+
|
| 1792 |
+
# Verify the user has write access to this space
|
| 1793 |
+
try:
|
| 1794 |
+
api = HfApi(token=token.token)
|
| 1795 |
+
# Try to get space info to verify access
|
| 1796 |
+
space_info = api.space_info(repo_id)
|
| 1797 |
+
if not space_info:
|
| 1798 |
+
return gr.update(value=f"Error: Could not access space {repo_id}. Please check your permissions.", visible=True)
|
| 1799 |
+
except Exception as e:
|
| 1800 |
+
return gr.update(value=f"Error: No write access to space {repo_id}. Please ensure you have the correct permissions. Error: {str(e)}", visible=True)
|
| 1801 |
+
else:
|
| 1802 |
+
# This is a new space, create repo_id with current user
|
| 1803 |
+
username = profile.username
|
| 1804 |
+
repo_id = f"{username}/{space_name.strip()}"
|
| 1805 |
# Map SDK name to HF SDK slug
|
| 1806 |
sdk_map = {
|
| 1807 |
"Gradio (Python)": "gradio",
|
|
|
|
| 1810 |
"Transformers.js": "static" # Transformers.js uses static SDK
|
| 1811 |
}
|
| 1812 |
sdk = sdk_map.get(sdk_name, "gradio")
|
| 1813 |
+
|
| 1814 |
+
# Create API client with user's token for proper authentication
|
| 1815 |
api = HfApi(token=token.token)
|
| 1816 |
+
# Only create the repo for new spaces (not updates) and non-Transformers.js and non-Streamlit SDKs
|
| 1817 |
+
if not is_update and sdk != "docker" and sdk_name != "Transformers.js":
|
| 1818 |
try:
|
| 1819 |
api.create_repo(
|
| 1820 |
repo_id=repo_id, # e.g. username/space_name
|
|
|
|
| 1825 |
except Exception as e:
|
| 1826 |
return gr.update(value=f"Error creating Space: {e}", visible=True)
|
| 1827 |
# Streamlit/docker logic
|
| 1828 |
+
if sdk == "docker" and not is_update:
|
| 1829 |
try:
|
| 1830 |
# Use duplicate_space to create a Streamlit template space
|
| 1831 |
from huggingface_hub import duplicate_space
|
|
|
|
| 1852 |
repo_type="space"
|
| 1853 |
)
|
| 1854 |
space_url = f"https://huggingface.co/spaces/{repo_id}"
|
| 1855 |
+
action_text = "Updated" if is_update else "Deployed"
|
| 1856 |
+
return gr.update(value=f"β
{action_text}! [Open your Space here]({space_url})", visible=True)
|
| 1857 |
except Exception as e:
|
| 1858 |
+
error_msg = str(e)
|
| 1859 |
+
if "403 Forbidden" in error_msg and "write token" in error_msg:
|
| 1860 |
+
return gr.update(value=f"Error: Permission denied. Please ensure you have write access to {repo_id} and your token has the correct permissions.", visible=True)
|
| 1861 |
+
else:
|
| 1862 |
+
return gr.update(value=f"Error uploading Streamlit app: {e}", visible=True)
|
| 1863 |
finally:
|
| 1864 |
import os
|
| 1865 |
os.unlink(temp_path)
|
|
|
|
| 1867 |
except Exception as e:
|
| 1868 |
return gr.update(value=f"Error duplicating Streamlit space: {e}", visible=True)
|
| 1869 |
# Transformers.js logic
|
| 1870 |
+
elif sdk_name == "Transformers.js" and not is_update:
|
| 1871 |
try:
|
| 1872 |
# Use duplicate_space to create a transformers.js template space
|
| 1873 |
from huggingface_hub import duplicate_space
|
|
|
|
| 1904 |
repo_type="space"
|
| 1905 |
)
|
| 1906 |
except Exception as e:
|
| 1907 |
+
error_msg = str(e)
|
| 1908 |
+
if "403 Forbidden" in error_msg and "write token" in error_msg:
|
| 1909 |
+
return gr.update(value=f"Error: Permission denied. Please ensure you have write access to {repo_id} and your token has the correct permissions.", visible=True)
|
| 1910 |
+
else:
|
| 1911 |
+
return gr.update(value=f"Error uploading index.html: {e}", visible=True)
|
| 1912 |
finally:
|
| 1913 |
import os
|
| 1914 |
os.unlink(temp_path)
|
|
|
|
| 1926 |
repo_type="space"
|
| 1927 |
)
|
| 1928 |
except Exception as e:
|
| 1929 |
+
error_msg = str(e)
|
| 1930 |
+
if "403 Forbidden" in error_msg and "write token" in error_msg:
|
| 1931 |
+
return gr.update(value=f"Error: Permission denied. Please ensure you have write access to {repo_id} and your token has the correct permissions.", visible=True)
|
| 1932 |
+
else:
|
| 1933 |
+
return gr.update(value=f"Error uploading index.js: {e}", visible=True)
|
| 1934 |
finally:
|
| 1935 |
import os
|
| 1936 |
os.unlink(temp_path)
|
|
|
|
| 1948 |
repo_type="space"
|
| 1949 |
)
|
| 1950 |
space_url = f"https://huggingface.co/spaces/{repo_id}"
|
| 1951 |
+
action_text = "Updated" if is_update else "Deployed"
|
| 1952 |
+
return gr.update(value=f"β
{action_text}! [Open your Transformers.js Space here]({space_url})", visible=True)
|
| 1953 |
except Exception as e:
|
| 1954 |
+
error_msg = str(e)
|
| 1955 |
+
if "403 Forbidden" in error_msg and "write token" in error_msg:
|
| 1956 |
+
return gr.update(value=f"Error: Permission denied. Please ensure you have write access to {repo_id} and your token has the correct permissions.", visible=True)
|
| 1957 |
+
else:
|
| 1958 |
+
return gr.update(value=f"Error uploading style.css: {e}", visible=True)
|
| 1959 |
finally:
|
| 1960 |
import os
|
| 1961 |
os.unlink(temp_path)
|
|
|
|
| 1981 |
repo_type="space"
|
| 1982 |
)
|
| 1983 |
space_url = f"https://huggingface.co/spaces/{repo_id}"
|
| 1984 |
+
action_text = "Updated" if is_update else "Deployed"
|
| 1985 |
+
return gr.update(value=f"β
{action_text}! [Open your Space here]({space_url})", visible=True)
|
| 1986 |
except Exception as e:
|
| 1987 |
+
error_msg = str(e)
|
| 1988 |
+
if "403 Forbidden" in error_msg and "write token" in error_msg:
|
| 1989 |
+
return gr.update(value=f"Error: Permission denied. Please ensure you have write access to {repo_id} and your token has the correct permissions.", visible=True)
|
| 1990 |
+
elif attempt < max_attempts - 1:
|
| 1991 |
time.sleep(2) # Wait before retrying
|
| 1992 |
else:
|
| 1993 |
+
return gr.update(value=f"Error uploading file after {max_attempts} attempts: {e}. Please check your permissions and try again.", visible=True)
|
| 1994 |
finally:
|
| 1995 |
import os
|
| 1996 |
os.unlink(temp_path)
|
|
|
|
| 2008 |
repo_type="space"
|
| 2009 |
)
|
| 2010 |
space_url = f"https://huggingface.co/spaces/{repo_id}"
|
| 2011 |
+
action_text = "Updated" if is_update else "Deployed"
|
| 2012 |
+
return gr.update(value=f"β
{action_text}! [Open your Space here]({space_url})", visible=True)
|
| 2013 |
except Exception as e:
|
| 2014 |
+
error_msg = str(e)
|
| 2015 |
+
if "403 Forbidden" in error_msg and "write token" in error_msg:
|
| 2016 |
+
return gr.update(value=f"Error: Permission denied. Please ensure you have write access to {repo_id} and your token has the correct permissions.", visible=True)
|
| 2017 |
+
else:
|
| 2018 |
+
return gr.update(value=f"Error uploading file: {e}", visible=True)
|
| 2019 |
finally:
|
| 2020 |
import os
|
| 2021 |
os.unlink(temp_path)
|