wuhp commited on
Commit
549a148
Β·
verified Β·
1 Parent(s): 4c46f34

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +115 -138
app.py CHANGED
@@ -110,7 +110,9 @@ def get_container_logs(repo_id, profile, token):
110
  return "⚠️ Please log in and create a Space first."
111
  return _fetch_space_logs_level(repo_id, "run")
112
 
113
- # β€” GEMINI FUNCTION DECLARATIONS β€”
 
 
114
 
115
  func_decls = [
116
  types.FunctionDeclaration(
@@ -120,9 +122,9 @@ func_decls = [
120
  "type": "object",
121
  "properties": {
122
  "repo_name": {"type": "string"},
123
- "sdk": {"type": "string", "enum": ["gradio", "streamlit"]},
124
  },
125
- "required": ["repo_name", "sdk"],
126
  }
127
  ),
128
  types.FunctionDeclaration(
@@ -131,10 +133,10 @@ func_decls = [
131
  parameters={
132
  "type": "object",
133
  "properties": {
134
- "path": {"type": "string", "description": "File path in the space"},
135
- "content": {"type": "string", "description": "Full text content"},
136
  },
137
- "required": ["path", "content"],
138
  }
139
  ),
140
  types.FunctionDeclaration(
@@ -143,7 +145,7 @@ func_decls = [
143
  parameters={
144
  "type": "object",
145
  "properties": {"repo_id": {"type": "string"}},
146
- "required": ["repo_id"],
147
  }
148
  ),
149
  types.FunctionDeclaration(
@@ -152,156 +154,132 @@ func_decls = [
152
  parameters={
153
  "type": "object",
154
  "properties": {"repo_id": {"type": "string"}},
155
- "required": ["repo_id"],
156
  }
157
  ),
158
  types.FunctionDeclaration(
159
  name="get_run_logs",
160
- description="Fetch container (run) logs",
161
  parameters={
162
  "type": "object",
163
  "properties": {"repo_id": {"type": "string"}},
164
- "required": ["repo_id"],
165
  }
166
  ),
167
  ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
 
169
- # β€” CHAT HANDLER β€”
170
 
171
  def process_message(
172
  profile, token, user_msg,
173
  gemini_key, sidebar_repo, sidebar_sdk,
174
  chat_history, session
175
  ):
176
- # Initialize Gemini chat
177
  if session.get("chat") is None:
178
  client = genai.Client(api_key=gemini_key)
179
- cfg = types.GenerateContentConfig(
180
- system_instruction=(
181
- "You are a HF Spaces admin. You can create spaces, "
182
- "write files, list files, and fetch logs."
 
 
 
 
 
 
183
  ),
184
- temperature=0,
185
- tools=func_decls, # ← pass declarations directly
186
  )
187
- session["chat"] = client.chats.create(
188
- model="gemini-2.0-flash", config=cfg
189
- )
190
- session["repo_id"] = None
191
- session["messages"]= []
 
 
 
 
192
 
193
- # Handle β€œcall it XYZ” before other logic
194
  if session["repo_id"] is None:
195
- m = re.search(r"call (?:it )?([A-Za-z0-9_-]+)", user_msg, re.IGNORECASE)
196
  if m:
197
- name = m.group(1)
198
- rid, log, iframe = create_space(
199
- name, sidebar_sdk, profile, token
200
- )
201
- session.update({
202
- "repo_id": rid,
203
- "iframe": iframe,
204
- "log": log,
205
- "files": ""
206
- })
207
- session["messages"].append({
208
- "role":"assistant",
209
- "content":f"βœ… Created Space `{name}`. {log}"
210
- })
211
- return session["messages"], iframe, log, "", session
212
-
213
- # Record user message
214
- session["messages"].append({"role":"user","content":user_msg})
215
-
216
- # Send to Gemini
217
- resp = session["chat"].send_message(user_msg)
218
- part = resp.candidates[0].content.parts[0]
219
-
220
- result = {}
221
- if part.function_call:
222
- args = part.function_call.args
223
- if isinstance(args, str):
224
- args = json.loads(args)
225
- fn = part.function_call.name
226
-
227
- if fn == "create_space":
228
- rid, log, iframe = create_space(
229
- args["repo_name"], args["sdk"], profile, token
230
- )
231
- session["repo_id"] = rid
232
- result = {"log": log, "iframe": iframe}
233
-
234
- elif fn == "write_file":
235
- status = write_file(
236
- args["path"], args["content"],
237
- session["repo_id"], profile, token
238
- )
239
- result = {"status": status}
240
-
241
- elif fn == "list_files":
242
- fl = list_repo_files(
243
- session["repo_id"],
244
- token=token.token,
245
- repo_type="space"
246
- )
247
- result = {"files": "\n".join(fl)}
248
-
249
- elif fn == "get_build_logs":
250
- result = {"log": get_build_logs(
251
- session["repo_id"], profile, token
252
- )}
253
-
254
- elif fn == "get_run_logs":
255
- result = {"log": get_container_logs(
256
- session["repo_id"], profile, token
257
- )}
258
-
259
- # Send back the *dict* directly (no json.dumps)
260
- session["chat"].send_message(
261
- types.Content(
262
- role="function",
263
- parts=[ types.Part(
264
- function_call=part.function_call,
265
- function_response=result
266
- )]
267
- )
268
- )
269
- assistant_text = session["chat"].get_history()[-1].parts[0].text
270
-
271
- else:
272
- assistant_text = part.text
273
-
274
- # Record assistant
275
- session["messages"].append({
276
- "role":"assistant","content":assistant_text
277
- })
278
-
279
- # Update panels
280
- for k in ("iframe","log","files"):
281
- if k in result:
282
- session[k] = result[k]
283
-
284
- return (
285
- session["messages"],
286
- session.get("iframe",""),
287
- session.get("log",""),
288
- session.get("files",""),
289
- session
290
- )
291
-
292
- # β€” SYNC MANUAL CHANGES β€”
293
 
294
  def sync_manual(profile, token, session):
295
  if not (profile and token and session.get("repo_id")):
296
- return session.get("iframe",""), "⚠️ Cannot sync manual changes.", session.get("files",""), session
297
- fl = list_repo_files(
298
- session["repo_id"], token=token.token, repo_type="space"
299
- )
300
- session["files"] = "\n".join(fl)
301
- session["log"] = "πŸ”„ Manual changes synced."
302
- return session["iframe"], session["log"], session["files"], session
303
 
304
- # β€” BUILD THE UI β€”
305
 
306
  with gr.Blocks(css="""
307
  #sidebar { background:#f2f2f2; padding:1rem; border-right:1px solid #ccc; }
@@ -324,12 +302,12 @@ with gr.Blocks(css="""
324
  [profile_state, token_state], [models_md])
325
 
326
  gemini_key = gr.Textbox(label="Gemini API Key", type="password")
327
- sidebar_repo = gr.Textbox(label="Space name", placeholder="my-space")
328
  sidebar_sdk = gr.Radio(["gradio","streamlit"],
329
  value="gradio", label="SDK")
330
 
331
  gr.Markdown("---")
332
- confirm_btn = gr.Button("πŸ”„ Confirm Manual Changes")
333
 
334
  # Main area
335
  with gr.Column(elem_id="main", scale=3):
@@ -338,17 +316,16 @@ with gr.Blocks(css="""
338
  with gr.TabItem("πŸ’¬ Chat"):
339
  chatbox = gr.Chatbot(type="messages")
340
  user_input = gr.Textbox(show_label=False,
341
- placeholder="Ask the LLM…")
342
  send_btn = gr.Button("Send")
343
  with gr.TabItem("πŸ› οΈ Manual"):
344
- gr.Markdown("#### Create a Space")
345
- repo_m = gr.Textbox(label="Name")
346
  sdk_m = gr.Radio(["gradio","streamlit"],
347
  value="gradio", label="SDK")
348
  create_btn = gr.Button("Create Space")
349
  sess_id = gr.Textbox(visible=False)
350
- log_c = gr.Textbox(label="Log",
351
- interactive=False, lines=2)
352
  preview = gr.HTML("<p>No Space yet.</p>")
353
 
354
  create_btn.click(create_space,
@@ -359,7 +336,7 @@ with gr.Blocks(css="""
359
  path = gr.Textbox(label="Path", value="app.py")
360
  file_u = gr.File()
361
  upload_btn = gr.Button("Upload File")
362
- log_u = gr.Textbox(label="Log",
363
  interactive=False, lines=2)
364
 
365
  upload_btn.click(upload_file_to_space,
@@ -397,4 +374,4 @@ with gr.Blocks(css="""
397
  [iframe_out, log_out, files_out, state])
398
 
399
  if __name__ == "__main__":
400
- demo.launch(share=True)
 
110
  return "⚠️ Please log in and create a Space first."
111
  return _fetch_space_logs_level(repo_id, "run")
112
 
113
+
114
+ # β€” FUNCTION DECLARATIONS & TOOL β€”
115
+ # Build a single Tool containing all function declarations.
116
 
117
  func_decls = [
118
  types.FunctionDeclaration(
 
122
  "type": "object",
123
  "properties": {
124
  "repo_name": {"type": "string"},
125
+ "sdk": {"type": "string", "enum": ["gradio", "streamlit"]}
126
  },
127
+ "required": ["repo_name", "sdk"]
128
  }
129
  ),
130
  types.FunctionDeclaration(
 
133
  parameters={
134
  "type": "object",
135
  "properties": {
136
+ "path": {"type": "string"},
137
+ "content": {"type": "string"}
138
  },
139
+ "required": ["path", "content"]
140
  }
141
  ),
142
  types.FunctionDeclaration(
 
145
  parameters={
146
  "type": "object",
147
  "properties": {"repo_id": {"type": "string"}},
148
+ "required": ["repo_id"]
149
  }
150
  ),
151
  types.FunctionDeclaration(
 
154
  parameters={
155
  "type": "object",
156
  "properties": {"repo_id": {"type": "string"}},
157
+ "required": ["repo_id"]
158
  }
159
  ),
160
  types.FunctionDeclaration(
161
  name="get_run_logs",
162
+ description="Fetch container logs",
163
  parameters={
164
  "type": "object",
165
  "properties": {"repo_id": {"type": "string"}},
166
+ "required": ["repo_id"]
167
  }
168
  ),
169
  ]
170
+ tool = types.Tool(function_declarations=func_decls)
171
+
172
+
173
+ # β€” CHAT HANDLER β€”
174
+
175
+ def execute_function_by_name(name, args, profile, token, session):
176
+ """Dispatch to the right function and return a raw dict result."""
177
+ if name == "create_space":
178
+ rid, log, iframe = create_space(
179
+ args["repo_name"], session["sdk"], profile, token
180
+ )
181
+ session["repo_id"] = rid
182
+ return {"log": log, "iframe": iframe}
183
+ if name == "write_file":
184
+ status = write_file(
185
+ args["path"], args["content"],
186
+ session["repo_id"], profile, token
187
+ )
188
+ return {"status": status}
189
+ if name == "list_files":
190
+ fl = list_repo_files(
191
+ session["repo_id"], token=token.token, repo_type="space"
192
+ )
193
+ return {"files": "\n".join(fl)}
194
+ if name == "get_build_logs":
195
+ return {"log": get_build_logs(
196
+ session["repo_id"], profile, token
197
+ )}
198
+ if name == "get_run_logs":
199
+ return {"log": get_container_logs(
200
+ session["repo_id"], profile, token
201
+ )}
202
+ return {"error": f"Unknown function {name}"}
203
 
 
204
 
205
  def process_message(
206
  profile, token, user_msg,
207
  gemini_key, sidebar_repo, sidebar_sdk,
208
  chat_history, session
209
  ):
210
+ # Initialize chat once
211
  if session.get("chat") is None:
212
  client = genai.Client(api_key=gemini_key)
213
+ # Pass the Tool object, not a list of dicts
214
+ model = client.chats.create(
215
+ model="gemini-2.0-flash",
216
+ config=types.GenerateContentConfig(
217
+ system_instruction=(
218
+ "You are a HF Spaces admin. You can create spaces, "
219
+ "write files, list files, and fetch logs."
220
+ ),
221
+ temperature=0,
222
+ tools=[tool],
223
  ),
 
 
224
  )
225
+ session.update({
226
+ "chat": model,
227
+ "repo_id": None,
228
+ "sdk": sidebar_sdk,
229
+ "messages": []
230
+ })
231
+
232
+ # Remember current SDK choice
233
+ session["sdk"] = sidebar_sdk
234
 
235
+ # Pre-chat β€œcall it X” handler
236
  if session["repo_id"] is None:
237
+ m = re.search(r"call (?:it )?([A-Za-z0-9_-]+)", user_msg)
238
  if m:
239
+ args = {"repo_name": m.group(1), "sdk": sidebar_sdk}
240
+ result = execute_function_by_name("create_space", args, profile, token, session)
241
+ session["messages"].append({"role": "assistant", "content": result["log"]})
242
+ return session["messages"], result.get("iframe", ""), result["log"], "", session
243
+
244
+ # Append user message
245
+ session["messages"].append({"role": "user", "content": user_msg})
246
+ response = session["chat"].send_message(user_msg)
247
+
248
+ # Handle function-calling loop
249
+ while True:
250
+ part = response.candidates[0].content.parts[0]
251
+ if not part.function_call:
252
+ break # no more tools needed
253
+ fname = part.function_call.name
254
+ fargs = part.function_call.args or {}
255
+ # Run the tool
256
+ result = execute_function_by_name(fname, fargs, profile, token, session)
257
+ # Send function result back to model
258
+ resp_part = types.Part.from_function_response(name=fname, response=result)
259
+ response = session["chat"].send_message(types.Content(role="user", parts=[resp_part]))
260
+
261
+ # Final assistant text
262
+ assistant_text = response.text
263
+ session["messages"].append({"role": "assistant", "content": assistant_text})
264
+
265
+ # Update UI panels
266
+ iframe = session.get("iframe", "")
267
+ log = session.get("log", "")
268
+ files = session.get("files", "")
269
+ return session["messages"], iframe, log, files, session
270
+
271
+
272
+ # β€” MANUAL SYNC β€”
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
273
 
274
  def sync_manual(profile, token, session):
275
  if not (profile and token and session.get("repo_id")):
276
+ return "", "⚠️ Cannot sync manual changes.", "", session
277
+ files = list_repo_files(session["repo_id"], token=token.token, repo_type="space")
278
+ log = "πŸ”„ Manual changes synced."
279
+ return session.get("iframe",""), log, "\n".join(files), session
280
+
 
 
281
 
282
+ # β€” GRADIO UI β€”
283
 
284
  with gr.Blocks(css="""
285
  #sidebar { background:#f2f2f2; padding:1rem; border-right:1px solid #ccc; }
 
302
  [profile_state, token_state], [models_md])
303
 
304
  gemini_key = gr.Textbox(label="Gemini API Key", type="password")
305
+ sidebar_repo = gr.Textbox(label="Space name", placeholder="blurrtest")
306
  sidebar_sdk = gr.Radio(["gradio","streamlit"],
307
  value="gradio", label="SDK")
308
 
309
  gr.Markdown("---")
310
+ confirm_btn = gr.Button("πŸ”„ Sync Manual Changes")
311
 
312
  # Main area
313
  with gr.Column(elem_id="main", scale=3):
 
316
  with gr.TabItem("πŸ’¬ Chat"):
317
  chatbox = gr.Chatbot(type="messages")
318
  user_input = gr.Textbox(show_label=False,
319
+ placeholder="Generate code…")
320
  send_btn = gr.Button("Send")
321
  with gr.TabItem("πŸ› οΈ Manual"):
322
+ gr.Markdown("#### Create / Rename Space")
323
+ repo_m = gr.Textbox(label="Name", value="")
324
  sdk_m = gr.Radio(["gradio","streamlit"],
325
  value="gradio", label="SDK")
326
  create_btn = gr.Button("Create Space")
327
  sess_id = gr.Textbox(visible=False)
328
+ log_c = gr.Textbox(label="Log", interactive=False, lines=2)
 
329
  preview = gr.HTML("<p>No Space yet.</p>")
330
 
331
  create_btn.click(create_space,
 
336
  path = gr.Textbox(label="Path", value="app.py")
337
  file_u = gr.File()
338
  upload_btn = gr.Button("Upload File")
339
+ log_u = gr.Textbox(label="Upload Log",
340
  interactive=False, lines=2)
341
 
342
  upload_btn.click(upload_file_to_space,
 
374
  [iframe_out, log_out, files_out, state])
375
 
376
  if __name__ == "__main__":
377
+ demo.launch()