wuhp commited on
Commit
960524f
·
verified ·
1 Parent(s): 256db23

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +62 -67
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(f"{constants.ENDPOINT}/api/spaces/{repo_id}/jwt",
70
- headers=build_hf_headers())
 
 
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(f"{constants.ENDPOINT}/api/spaces/{repo_id}/jwt",
87
- headers=build_hf_headers())
 
 
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
- # 1) Initialize chat
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. On request, "
162
- "1) call create_space(name, sdk), 2) call write_file for app.py, "
163
- "requirements.txt, README.md in that order, then 3) optionally list or fetch logs. "
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"] = client.chats.create(model="gemini-2.0-flash", config=cfg)
173
- session["repo_id"] = None
174
- session["messages"]= []
175
 
176
- # 1.5) Auto‐create space if user says “call it NAME”
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
- # 2) Record user
190
  session["messages"].append({"role":"user","content":user_msg})
191
 
192
- # 3) Send to Gemini
193
  resp = session["chat"].send_message(user_msg)
194
  part = resp.candidates[0].content.parts[0]
195
 
196
- # 4) Handle function_call
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
- # 5) Return function result as dict (not JSON string)
225
- session["chat"].send_message(
226
- types.Content(
227
- role="function",
228
- parts=[ types.Part(
229
- function_call=part.function_call,
230
- function_response=result
231
- )]
232
- )
233
- )
234
- assistant_text = session["chat"].get_history()[-1].parts[0].text
235
 
236
- # 6) Record assistant
 
237
  session["messages"].append({"role":"assistant","content":assistant_text})
238
 
239
- # 7) Update panels
240
- if "iframe" in result: session["iframe"] = result["iframe"]
241
- if "log" in result: session["log"] = result["log"]
242
- if "files" in result: session["files"] = result["files"]
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 = gr.LoginButton(variant="huggingface", size="sm")
273
  profile_state = gr.State(None)
274
- token_state = gr.State(None)
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 = gr.Textbox(label="Gemini API Key", type="password")
287
  sidebar_repo = gr.Textbox(label="Space name", placeholder="my-space")
288
- sidebar_sdk = gr.Radio(["gradio","streamlit"], value="gradio", label="SDK")
289
  gr.Markdown("---")
290
- confirm_btn = gr.Button("🔄 Confirm Manual Changes")
291
 
 
292
  with gr.Column(elem_id="main", scale=3):
293
  tabs = gr.Tabs()
294
  with tabs:
295
  with gr.TabItem("💬 Chat"):
296
- chatbox = gr.Chatbot(type="messages")
297
  user_input = gr.Textbox(show_label=False, placeholder="Ask the LLM…")
298
- send_btn = gr.Button("Send")
299
  with gr.TabItem("🛠️ Manual"):
300
  gr.Markdown("#### Manual Controls")
301
- # (repeat manual create/upload/logs UI as before)
302
 
303
  gr.Markdown("---")
304
  iframe_out = gr.HTML(label="🖼️ Preview")
305
- log_out = gr.Textbox(label="📋 Latest Log", lines=4)
306
- files_out = gr.Textbox(label="📚 Files", lines=4)
307
 
308
  state = gr.State({})
309
- send_btn.click(process_message,
310
- inputs=[
311
- profile_state, token_state, user_input,
312
- gemini_key, sidebar_repo, sidebar_sdk,
313
- chatbox, state
314
- ],
315
- outputs=[chatbox, iframe_out, log_out, files_out, state])
316
-
317
- confirm_btn.click(sync_manual,
318
- inputs=[profile_state, token_state, state],
319
- outputs=[iframe_out, log_out, files_out, state])
 
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()