Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -50,10 +50,8 @@ def write_file(path, content, repo_id, profile, token):
|
|
50 |
return "⚠️ Please log in first."
|
51 |
if not repo_id:
|
52 |
return "⚠️ Please create a Space first."
|
53 |
-
# Write locally
|
54 |
with open(path, "w") as f:
|
55 |
f.write(content)
|
56 |
-
# Upload
|
57 |
upload_file(path, path, repo_id,
|
58 |
token=token.token, repo_type="space")
|
59 |
return f"✅ Wrote and uploaded `{path}`"
|
@@ -66,8 +64,10 @@ def list_files(repo_id, profile, token):
|
|
66 |
def get_build_logs(repo_id, profile, token):
|
67 |
if not (profile and token and repo_id):
|
68 |
return "⚠️ Please log in and create a Space first."
|
69 |
-
r = get_session().get(
|
70 |
-
|
|
|
|
|
71 |
hf_raise_for_status(r)
|
72 |
jwt = r.json()["token"]
|
73 |
url = f"https://api.hf.space/v1/{repo_id}/logs/build"
|
@@ -83,8 +83,10 @@ def get_build_logs(repo_id, profile, token):
|
|
83 |
def get_container_logs(repo_id, profile, token):
|
84 |
if not (profile and token and repo_id):
|
85 |
return "⚠️ Please log in and create a Space first."
|
86 |
-
r = get_session().get(
|
87 |
-
|
|
|
|
|
88 |
hf_raise_for_status(r)
|
89 |
jwt = r.json()["token"]
|
90 |
url = f"https://api.hf.space/v1/{repo_id}/logs/run"
|
@@ -153,15 +155,14 @@ func_decls = [
|
|
153 |
def process_message(profile, token, user_msg,
|
154 |
gemini_key, sidebar_repo, sidebar_sdk,
|
155 |
chat_history, session):
|
156 |
-
#
|
157 |
if session.get("chat") is None:
|
158 |
client = genai.Client(api_key=gemini_key)
|
159 |
cfg = types.GenerateContentConfig(
|
160 |
system_instruction=(
|
161 |
-
"You are a HF Spaces developer assistant.
|
162 |
-
"
|
163 |
-
"
|
164 |
-
"You MUST use function calls for every action, and never return code inline."
|
165 |
),
|
166 |
temperature=0,
|
167 |
tools=[ types.Tool(function_declarations=func_decls) ],
|
@@ -169,31 +170,28 @@ def process_message(profile, token, user_msg,
|
|
169 |
function_calling_config=types.FunctionCallingConfig(mode="ANY")
|
170 |
)
|
171 |
)
|
172 |
-
session["chat"]
|
173 |
-
session["repo_id"]
|
174 |
-
session["messages"]= []
|
175 |
|
176 |
-
#
|
177 |
if session["repo_id"] is None:
|
178 |
m = re.search(r"call (?:it )?([A-Za-z0-9_-]+)", user_msg, re.IGNORECASE)
|
179 |
if m:
|
180 |
name = m.group(1)
|
181 |
rid, log, iframe = create_space(name, sidebar_sdk, profile, token)
|
182 |
-
session["repo_id"] = rid
|
183 |
-
session["iframe"] = iframe
|
184 |
-
session["log"] = log
|
185 |
-
session["files"] = ""
|
186 |
session["messages"].append({"role":"assistant","content":log})
|
187 |
return session["messages"], iframe, log, "", session
|
188 |
|
189 |
-
#
|
190 |
session["messages"].append({"role":"user","content":user_msg})
|
191 |
|
192 |
-
#
|
193 |
resp = session["chat"].send_message(user_msg)
|
194 |
part = resp.candidates[0].content.parts[0]
|
195 |
|
196 |
-
#
|
197 |
args = part.function_call.args
|
198 |
if isinstance(args, str):
|
199 |
args = json.loads(args)
|
@@ -202,8 +200,7 @@ def process_message(profile, token, user_msg,
|
|
202 |
|
203 |
if fn == "create_space":
|
204 |
rid, log, iframe = create_space(args["repo_name"], args["sdk"], profile, token)
|
205 |
-
session["repo_id"] = rid
|
206 |
-
result = {"log":log, "iframe":iframe}
|
207 |
|
208 |
elif fn == "write_file":
|
209 |
status = write_file(args["path"], args["content"], session["repo_id"], profile, token)
|
@@ -221,25 +218,22 @@ def process_message(profile, token, user_msg,
|
|
221 |
else:
|
222 |
result = {"log":f"⚠️ Unknown function {fn}"}
|
223 |
|
224 |
-
#
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
)]
|
232 |
-
)
|
233 |
-
)
|
234 |
-
assistant_text = session["chat"].get_history()[-1].parts[0].text
|
235 |
|
236 |
-
#
|
|
|
237 |
session["messages"].append({"role":"assistant","content":assistant_text})
|
238 |
|
239 |
-
#
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
|
244 |
return (
|
245 |
session["messages"],
|
@@ -249,17 +243,16 @@ def process_message(profile, token, user_msg,
|
|
249 |
session
|
250 |
)
|
251 |
|
252 |
-
# — SYNC MANUAL —
|
253 |
|
254 |
def sync_manual(profile, token, session):
|
255 |
if not (profile and token and session.get("repo_id")):
|
256 |
-
return session.get("iframe",""), "⚠️ Cannot sync.", session.get("files",""), session
|
257 |
fl = list_repo_files(session["repo_id"], token=token.token, repo_type="space")
|
258 |
-
session["files"] = "\n".join(fl)
|
259 |
-
session["log"] = "🔄 Manual changes synced."
|
260 |
return session["iframe"], session["log"], session["files"], session
|
261 |
|
262 |
-
# — UI —
|
263 |
|
264 |
with gr.Blocks(css="""
|
265 |
#sidebar { background:#f2f2f2; padding:1rem; border-right:1px solid #ccc; }
|
@@ -267,12 +260,12 @@ with gr.Blocks(css="""
|
|
267 |
""") as demo:
|
268 |
|
269 |
with gr.Row():
|
|
|
270 |
with gr.Column(elem_id="sidebar", scale=1):
|
271 |
gr.Markdown("### 🔑 HF Login & Config")
|
272 |
-
login_btn
|
273 |
profile_state = gr.State(None)
|
274 |
-
token_state
|
275 |
-
|
276 |
login_btn.click(None, [], [profile_state, token_state])
|
277 |
|
278 |
status_md = gr.Markdown("*Not logged in.*")
|
@@ -283,40 +276,42 @@ with gr.Blocks(css="""
|
|
283 |
[profile_state, token_state],
|
284 |
[models_md])
|
285 |
|
286 |
-
gemini_key
|
287 |
sidebar_repo = gr.Textbox(label="Space name", placeholder="my-space")
|
288 |
-
sidebar_sdk
|
289 |
gr.Markdown("---")
|
290 |
-
confirm_btn
|
291 |
|
|
|
292 |
with gr.Column(elem_id="main", scale=3):
|
293 |
tabs = gr.Tabs()
|
294 |
with tabs:
|
295 |
with gr.TabItem("💬 Chat"):
|
296 |
-
chatbox
|
297 |
user_input = gr.Textbox(show_label=False, placeholder="Ask the LLM…")
|
298 |
-
send_btn
|
299 |
with gr.TabItem("🛠️ Manual"):
|
300 |
gr.Markdown("#### Manual Controls")
|
301 |
-
#
|
302 |
|
303 |
gr.Markdown("---")
|
304 |
iframe_out = gr.HTML(label="🖼️ Preview")
|
305 |
-
log_out
|
306 |
-
files_out
|
307 |
|
308 |
state = gr.State({})
|
309 |
-
send_btn.click(
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
|
|
320 |
|
321 |
if __name__ == "__main__":
|
322 |
demo.launch()
|
|
|
50 |
return "⚠️ Please log in first."
|
51 |
if not repo_id:
|
52 |
return "⚠️ Please create a Space first."
|
|
|
53 |
with open(path, "w") as f:
|
54 |
f.write(content)
|
|
|
55 |
upload_file(path, path, repo_id,
|
56 |
token=token.token, repo_type="space")
|
57 |
return f"✅ Wrote and uploaded `{path}`"
|
|
|
64 |
def get_build_logs(repo_id, profile, token):
|
65 |
if not (profile and token and repo_id):
|
66 |
return "⚠️ Please log in and create a Space first."
|
67 |
+
r = get_session().get(
|
68 |
+
f"{constants.ENDPOINT}/api/spaces/{repo_id}/jwt",
|
69 |
+
headers=build_hf_headers()
|
70 |
+
)
|
71 |
hf_raise_for_status(r)
|
72 |
jwt = r.json()["token"]
|
73 |
url = f"https://api.hf.space/v1/{repo_id}/logs/build"
|
|
|
83 |
def get_container_logs(repo_id, profile, token):
|
84 |
if not (profile and token and repo_id):
|
85 |
return "⚠️ Please log in and create a Space first."
|
86 |
+
r = get_session().get(
|
87 |
+
f"{constants.ENDPOINT}/api/spaces/{repo_id}/jwt",
|
88 |
+
headers=build_hf_headers()
|
89 |
+
)
|
90 |
hf_raise_for_status(r)
|
91 |
jwt = r.json()["token"]
|
92 |
url = f"https://api.hf.space/v1/{repo_id}/logs/run"
|
|
|
155 |
def process_message(profile, token, user_msg,
|
156 |
gemini_key, sidebar_repo, sidebar_sdk,
|
157 |
chat_history, session):
|
158 |
+
# Initialize the chat once
|
159 |
if session.get("chat") is None:
|
160 |
client = genai.Client(api_key=gemini_key)
|
161 |
cfg = types.GenerateContentConfig(
|
162 |
system_instruction=(
|
163 |
+
"You are a HF Spaces developer assistant. When asked to build a new Space, "
|
164 |
+
"first call create_space(name,sdk), then call write_file for app.py, requirements.txt "
|
165 |
+
"and README.md in that order. You must use function calls—never emit raw code."
|
|
|
166 |
),
|
167 |
temperature=0,
|
168 |
tools=[ types.Tool(function_declarations=func_decls) ],
|
|
|
170 |
function_calling_config=types.FunctionCallingConfig(mode="ANY")
|
171 |
)
|
172 |
)
|
173 |
+
session["chat"] = client.chats.create(model="gemini-2.0-flash", config=cfg)
|
174 |
+
session["repo_id"] = None
|
175 |
+
session["messages"] = []
|
176 |
|
177 |
+
# Auto‐create on “call it NAME”
|
178 |
if session["repo_id"] is None:
|
179 |
m = re.search(r"call (?:it )?([A-Za-z0-9_-]+)", user_msg, re.IGNORECASE)
|
180 |
if m:
|
181 |
name = m.group(1)
|
182 |
rid, log, iframe = create_space(name, sidebar_sdk, profile, token)
|
183 |
+
session["repo_id"], session["iframe"], session["log"], session["files"] = rid, iframe, log, ""
|
|
|
|
|
|
|
184 |
session["messages"].append({"role":"assistant","content":log})
|
185 |
return session["messages"], iframe, log, "", session
|
186 |
|
187 |
+
# Record the user message
|
188 |
session["messages"].append({"role":"user","content":user_msg})
|
189 |
|
190 |
+
# Send to Gemini
|
191 |
resp = session["chat"].send_message(user_msg)
|
192 |
part = resp.candidates[0].content.parts[0]
|
193 |
|
194 |
+
# Prepare to call back
|
195 |
args = part.function_call.args
|
196 |
if isinstance(args, str):
|
197 |
args = json.loads(args)
|
|
|
200 |
|
201 |
if fn == "create_space":
|
202 |
rid, log, iframe = create_space(args["repo_name"], args["sdk"], profile, token)
|
203 |
+
session["repo_id"], result = rid, {"log":log, "iframe":iframe}
|
|
|
204 |
|
205 |
elif fn == "write_file":
|
206 |
status = write_file(args["path"], args["content"], session["repo_id"], profile, token)
|
|
|
218 |
else:
|
219 |
result = {"log":f"⚠️ Unknown function {fn}"}
|
220 |
|
221 |
+
# **Key fix:** use Part.from_function_response
|
222 |
+
func_call = part.function_call
|
223 |
+
resp_parts = [
|
224 |
+
types.Part(function_call=func_call),
|
225 |
+
types.Part.from_function_response(name=func_call.name, response=result)
|
226 |
+
]
|
227 |
+
session["chat"].send_message(types.Content(role="function", parts=resp_parts))
|
|
|
|
|
|
|
|
|
228 |
|
229 |
+
# Fetch the assistant’s final text
|
230 |
+
assistant_text = session["chat"].get_history()[-1].parts[0].text
|
231 |
session["messages"].append({"role":"assistant","content":assistant_text})
|
232 |
|
233 |
+
# Update panels
|
234 |
+
for k in ("iframe","log","files"):
|
235 |
+
if k in result:
|
236 |
+
session[k] = result[k]
|
237 |
|
238 |
return (
|
239 |
session["messages"],
|
|
|
243 |
session
|
244 |
)
|
245 |
|
246 |
+
# — SYNC MANUAL CHANGES —
|
247 |
|
248 |
def sync_manual(profile, token, session):
|
249 |
if not (profile and token and session.get("repo_id")):
|
250 |
+
return session.get("iframe",""), "⚠️ Cannot sync manual changes.", session.get("files",""), session
|
251 |
fl = list_repo_files(session["repo_id"], token=token.token, repo_type="space")
|
252 |
+
session["files"], session["log"] = "\n".join(fl), "🔄 Manual changes synced."
|
|
|
253 |
return session["iframe"], session["log"], session["files"], session
|
254 |
|
255 |
+
# — BUILD THE UI —
|
256 |
|
257 |
with gr.Blocks(css="""
|
258 |
#sidebar { background:#f2f2f2; padding:1rem; border-right:1px solid #ccc; }
|
|
|
260 |
""") as demo:
|
261 |
|
262 |
with gr.Row():
|
263 |
+
# Sidebar
|
264 |
with gr.Column(elem_id="sidebar", scale=1):
|
265 |
gr.Markdown("### 🔑 HF Login & Config")
|
266 |
+
login_btn = gr.LoginButton(variant="huggingface", size="sm")
|
267 |
profile_state = gr.State(None)
|
268 |
+
token_state = gr.State(None)
|
|
|
269 |
login_btn.click(None, [], [profile_state, token_state])
|
270 |
|
271 |
status_md = gr.Markdown("*Not logged in.*")
|
|
|
276 |
[profile_state, token_state],
|
277 |
[models_md])
|
278 |
|
279 |
+
gemini_key = gr.Textbox(label="Gemini API Key", type="password")
|
280 |
sidebar_repo = gr.Textbox(label="Space name", placeholder="my-space")
|
281 |
+
sidebar_sdk = gr.Radio(["gradio","streamlit"], value="gradio", label="SDK")
|
282 |
gr.Markdown("---")
|
283 |
+
confirm_btn = gr.Button("🔄 Confirm Manual Changes")
|
284 |
|
285 |
+
# Main area
|
286 |
with gr.Column(elem_id="main", scale=3):
|
287 |
tabs = gr.Tabs()
|
288 |
with tabs:
|
289 |
with gr.TabItem("💬 Chat"):
|
290 |
+
chatbox = gr.Chatbot(type="messages")
|
291 |
user_input = gr.Textbox(show_label=False, placeholder="Ask the LLM…")
|
292 |
+
send_btn = gr.Button("Send")
|
293 |
with gr.TabItem("🛠️ Manual"):
|
294 |
gr.Markdown("#### Manual Controls")
|
295 |
+
# …repeat your manual-create/upload/logs UI here…
|
296 |
|
297 |
gr.Markdown("---")
|
298 |
iframe_out = gr.HTML(label="🖼️ Preview")
|
299 |
+
log_out = gr.Textbox(label="📋 Latest Log", lines=4)
|
300 |
+
files_out = gr.Textbox(label="📚 Files", lines=4)
|
301 |
|
302 |
state = gr.State({})
|
303 |
+
send_btn.click(
|
304 |
+
process_message,
|
305 |
+
inputs=[profile_state, token_state, user_input,
|
306 |
+
gemini_key, sidebar_repo, sidebar_sdk,
|
307 |
+
chatbox, state],
|
308 |
+
outputs=[chatbox, iframe_out, log_out, files_out, state]
|
309 |
+
)
|
310 |
+
confirm_btn.click(
|
311 |
+
sync_manual,
|
312 |
+
inputs=[profile_state, token_state, state],
|
313 |
+
outputs=[iframe_out, log_out, files_out, state]
|
314 |
+
)
|
315 |
|
316 |
if __name__ == "__main__":
|
317 |
demo.launch()
|