Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -1,180 +1,54 @@
|
|
1 |
-
import os
|
2 |
-
import json
|
3 |
import uuid
|
4 |
-
from typing import Any, Dict, Tuple
|
5 |
-
|
6 |
import gradio as gr
|
7 |
-
from huggingface_hub import create_repo
|
8 |
-
from google import genai
|
9 |
-
from google.genai import types
|
10 |
-
from google.genai.types import Tool, GoogleSearch
|
11 |
-
|
12 |
-
# -----------------------------------------------------------------------------
|
13 |
-
# Configuration
|
14 |
-
# -----------------------------------------------------------------------------
|
15 |
-
MODEL_ID = "gemini-2.5-flash-preview-04-17"
|
16 |
-
WORKSPACE_DIR = "workspace"
|
17 |
-
SYSTEM_INSTRUCTION = (
|
18 |
-
"You are a helpful coding assistant that scaffolds a complete Hugging Face Space app. "
|
19 |
-
"Based on the user's request, decide between Gradio or Streamlit (whichever fits best), "
|
20 |
-
"and respond with exactly one JSON object with keys:\n"
|
21 |
-
" • \"framework\": either \"gradio\" or \"streamlit\"\n"
|
22 |
-
" • \"files\": a map of relative file paths to file contents\n"
|
23 |
-
" • \"message\": a human‑readable summary\n"
|
24 |
-
"Do not include extra text or markdown."
|
25 |
-
)
|
26 |
-
|
27 |
-
state_store: Dict[str, Dict[str, Any]] = {}
|
28 |
-
|
29 |
-
def start_app(
|
30 |
-
gemini_key: str,
|
31 |
-
hf_token: str,
|
32 |
-
hf_username: str,
|
33 |
-
repo_name: str
|
34 |
-
) -> Dict[str, Any]:
|
35 |
-
os.makedirs(WORKSPACE_DIR, exist_ok=True)
|
36 |
-
client = genai.Client(api_key=gemini_key)
|
37 |
-
config = types.GenerateContentConfig(system_instruction=SYSTEM_INSTRUCTION)
|
38 |
-
tools = [Tool(google_search=GoogleSearch())]
|
39 |
-
chat = client.chats.create(model=MODEL_ID, config=config, tools=tools)
|
40 |
-
local_path = os.path.join(WORKSPACE_DIR, repo_name)
|
41 |
-
os.makedirs(local_path, exist_ok=True)
|
42 |
-
return {
|
43 |
-
"chat": chat,
|
44 |
-
"hf_token": hf_token,
|
45 |
-
"hf_username": hf_username,
|
46 |
-
"repo_name": repo_name,
|
47 |
-
"created": False,
|
48 |
-
"repo_id": None,
|
49 |
-
"local_path": local_path,
|
50 |
-
"embed_url": None,
|
51 |
-
"logs": [f"Initialized workspace at {WORKSPACE_DIR}/{repo_name}."]
|
52 |
-
}
|
53 |
-
|
54 |
-
def handle_message(user_msg: str, state: Dict[str, Any]) -> Tuple[str, Dict[str, Any]]:
|
55 |
-
chat = state["chat"]
|
56 |
-
logs = state.setdefault("logs", [])
|
57 |
-
logs.append(f"> **User**: {user_msg}")
|
58 |
-
resp = chat.send_message(user_msg)
|
59 |
-
logs.append("Received response from Gemini.")
|
60 |
-
|
61 |
-
try:
|
62 |
-
data = json.loads(resp.text)
|
63 |
-
framework = data["framework"]
|
64 |
-
files = data.get("files", {})
|
65 |
-
reply = data.get("message", "")
|
66 |
-
except Exception:
|
67 |
-
logs.append("⚠️ Failed to parse assistant JSON.\n" + resp.text)
|
68 |
-
return "⚠️ Parsing error. Check logs.", state
|
69 |
|
70 |
-
|
71 |
-
|
72 |
-
create_repo(
|
73 |
-
repo_id=full_repo,
|
74 |
-
token=state["hf_token"],
|
75 |
-
exist_ok=True,
|
76 |
-
repo_type="space",
|
77 |
-
space_sdk=framework,
|
78 |
-
)
|
79 |
-
state.update({
|
80 |
-
"created": True,
|
81 |
-
"repo_id": full_repo,
|
82 |
-
"embed_url": f"https://huggingface.co/spaces/{full_repo}",
|
83 |
-
})
|
84 |
-
|
85 |
-
if files:
|
86 |
-
logs.append(f"Writing {len(files)} file(s): {list(files)}")
|
87 |
-
for relpath, content in files.items():
|
88 |
-
dest = os.path.join(state["local_path"], relpath)
|
89 |
-
os.makedirs(os.path.dirname(dest), exist_ok=True)
|
90 |
-
with open(dest, "w", encoding="utf‑8") as fp:
|
91 |
-
fp.write(content)
|
92 |
-
|
93 |
-
HfApi(token=state["hf_token"]).upload_folder(
|
94 |
-
folder_path=state["local_path"],
|
95 |
-
repo_id=state["repo_id"],
|
96 |
-
repo_type="space",
|
97 |
-
)
|
98 |
-
logs.append("Snapshot upload complete.")
|
99 |
-
return reply, state
|
100 |
|
101 |
-
with gr.Blocks(title="
|
102 |
-
# 1) OAuth button + status
|
103 |
-
login_btn = gr.LoginButton(variant="huggingface", size="lg")
|
104 |
status_md = gr.Markdown("*Not logged in.*")
|
105 |
|
106 |
-
# 2) show_profile called on load & login/logout
|
107 |
def show_profile(profile: gr.OAuthProfile | None) -> str:
|
108 |
return "*Not logged in.*" if profile is None else f"Logged in as **{profile.username}**"
|
109 |
|
110 |
-
#
|
111 |
demo.load(fn=show_profile, inputs=None, outputs=[status_md])
|
112 |
login_btn.click(fn=show_profile, inputs=None, outputs=[status_md])
|
113 |
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
with gr.Column(scale=3):
|
121 |
-
chatbot = gr.Chatbot(type="messages", value=[])
|
122 |
-
logs_display = gr.Textbox(label="Operation Logs", interactive=False, lines=8)
|
123 |
-
preview_iframe = gr.HTML("<p>No deployed app yet.</p>")
|
124 |
-
user_msg = gr.Textbox(label="Your message")
|
125 |
-
send_btn = gr.Button("Send", interactive=False)
|
126 |
|
127 |
-
#
|
128 |
-
def
|
129 |
-
|
130 |
-
|
131 |
-
repo_name: str,
|
132 |
-
oauth_token: gr.OAuthToken | None
|
133 |
-
):
|
134 |
-
if oauth_token is None:
|
135 |
-
return gr.Error("Please *Sign in with Hugging Face* first."), "", "", gr.update(interactive=False)
|
136 |
-
hf_username = oauth_token.user_info["username"]
|
137 |
-
new_id = str(uuid.uuid4())
|
138 |
-
state_store[new_id] = start_app(gemini_key, oauth_token.token, hf_username, repo_name)
|
139 |
-
logs = "\n".join(state_store[new_id]["logs"])
|
140 |
-
return new_id, logs, "<p>Awaiting first instruction…</p>", gr.update(interactive=True)
|
141 |
-
|
142 |
-
start_btn.click(
|
143 |
-
on_start,
|
144 |
-
inputs=[gemini_key, repo_name, login_btn], # pass login_btn to provide OAuthToken
|
145 |
-
outputs=[session_id, logs_display, preview_iframe, send_btn],
|
146 |
-
)
|
147 |
-
|
148 |
-
# 4) Handle user messages
|
149 |
-
def on_send(
|
150 |
-
msg: str,
|
151 |
-
chat_history: list[dict],
|
152 |
-
sess_id: str
|
153 |
-
) -> Tuple[list[dict], str, str, str]:
|
154 |
-
if not sess_id or sess_id not in state_store:
|
155 |
-
return [], sess_id, "", ""
|
156 |
-
reply, new_state = handle_message(msg, state_store[sess_id])
|
157 |
-
state_store[sess_id] = new_state
|
158 |
-
|
159 |
-
history = chat_history or []
|
160 |
-
history.append({"role": "user", "content": msg})
|
161 |
-
history.append({"role": "assistant", "content": reply})
|
162 |
-
|
163 |
-
logs = "\n".join(new_state["logs"])
|
164 |
-
embed = ""
|
165 |
-
if new_state.get("embed_url"):
|
166 |
-
embed = f'<iframe src="{new_state["embed_url"]}" width="100%" height="500px"></iframe>'
|
167 |
-
return history, sess_id, logs, embed
|
168 |
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
178 |
)
|
179 |
|
180 |
if __name__ == "__main__":
|
|
|
|
|
|
|
1 |
import uuid
|
|
|
|
|
2 |
import gradio as gr
|
3 |
+
from huggingface_hub import create_repo
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4 |
|
5 |
+
# Store created spaces' info
|
6 |
+
state_store = {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
|
8 |
+
with gr.Blocks(title="HF Space Creator") as demo:
|
9 |
+
# 1) OAuth button + login status
|
10 |
+
login_btn = gr.LoginButton(variant="huggingface", size="lg")
|
11 |
status_md = gr.Markdown("*Not logged in.*")
|
12 |
|
|
|
13 |
def show_profile(profile: gr.OAuthProfile | None) -> str:
|
14 |
return "*Not logged in.*" if profile is None else f"Logged in as **{profile.username}**"
|
15 |
|
16 |
+
# Update status on page load and after click
|
17 |
demo.load(fn=show_profile, inputs=None, outputs=[status_md])
|
18 |
login_btn.click(fn=show_profile, inputs=None, outputs=[status_md])
|
19 |
|
20 |
+
# 2) Repo creation UI
|
21 |
+
repo_name = gr.Textbox(label="New Space name", placeholder="your-repo-name")
|
22 |
+
create_btn = gr.Button("Create Space", interactive=False)
|
23 |
+
session_id = gr.Textbox(value="", visible=False)
|
24 |
+
logs = gr.Textbox(label="Logs", interactive=False, lines=5)
|
25 |
+
preview_iframe = gr.HTML("<p>No Space created yet.</p>")
|
|
|
|
|
|
|
|
|
|
|
|
|
26 |
|
27 |
+
# Enable create button only after login
|
28 |
+
def enable_create(profile: gr.OAuthProfile | None):
|
29 |
+
return gr.update(interactive=(profile is not None))
|
30 |
+
login_btn.click(fn=enable_create, inputs=None, outputs=[create_btn])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
31 |
|
32 |
+
def create_space(name: str, oauth_token: gr.OAuthToken | None):
|
33 |
+
if oauth_token is None:
|
34 |
+
return "", "Please sign in first.", "<p>No Space created yet.</p>"
|
35 |
+
username = oauth_token.user_info["username"]
|
36 |
+
repo_id = f"{username}/{name}"
|
37 |
+
create_repo(
|
38 |
+
repo_id=repo_id,
|
39 |
+
token=oauth_token.token,
|
40 |
+
exist_ok=True,
|
41 |
+
repo_type="space"
|
42 |
+
)
|
43 |
+
url = f"https://huggingface.co/spaces/{repo_id}"
|
44 |
+
logs = f"Created or found Space: {url}"
|
45 |
+
iframe = f'<iframe src="{url}" width="100%" height="500px"></iframe>'
|
46 |
+
return repo_id, logs, iframe
|
47 |
+
|
48 |
+
create_btn.click(
|
49 |
+
fn=create_space,
|
50 |
+
inputs=[repo_name, login_btn],
|
51 |
+
outputs=[session_id, logs, preview_iframe]
|
52 |
)
|
53 |
|
54 |
if __name__ == "__main__":
|