wuhp commited on
Commit
4c46f34
·
verified ·
1 Parent(s): 7adeb1d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +190 -169
app.py CHANGED
@@ -1,9 +1,13 @@
1
- import re, json, os
 
 
 
2
  import gradio as gr
3
  from huggingface_hub import (
4
  create_repo, list_models, upload_file, list_repo_files, constants
5
  )
6
  from huggingface_hub.utils import build_hf_headers, get_session, hf_raise_for_status
 
7
  from google import genai
8
  from google.genai import types
9
 
@@ -35,13 +39,15 @@ def create_space(repo_name, sdk, profile, token):
35
  if not (profile and token):
36
  return "", "⚠️ Please log in first.", "<p>No Space created yet.</p>"
37
  rid = f"{profile.username}/{repo_name}"
38
- create_repo(rid,
39
- token=token.token,
40
- exist_ok=True,
41
- repo_type="space",
42
- space_sdk=sdk)
43
- url = f"https://huggingface.co/spaces/{rid}"
44
- log = f"✅ Space ready: {url} (SDK: {sdk})"
 
 
45
  iframe = f'<iframe src="{url}" width="100%" height="400px"></iframe>'
46
  return rid, log, iframe
47
 
@@ -52,8 +58,13 @@ def write_file(path, content, repo_id, profile, token):
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}`"
58
 
59
  def upload_file_to_space(file, path, repo_id, profile, token):
@@ -63,8 +74,13 @@ def upload_file_to_space(file, path, repo_id, profile, token):
63
  return "⚠️ Please create a Space first."
64
  if not file:
65
  return "⚠️ No file selected."
66
- upload_file(file.name, path, repo_id,
67
- token=token.token, repo_type="space")
 
 
 
 
 
68
  return f"✅ Uploaded `{path}` to `{repo_id}`"
69
 
70
  def _fetch_space_logs_level(repo_id, level):
@@ -76,9 +92,7 @@ def _fetch_space_logs_level(repo_id, level):
76
  jwt = r.json()["token"]
77
  url = f"https://api.hf.space/v1/{repo_id}/logs/{level}"
78
  lines = []
79
- with get_session().get(url,
80
- headers=build_hf_headers(token=jwt),
81
- stream=True) as resp:
82
  hf_raise_for_status(resp)
83
  for raw in resp.iter_lines():
84
  if raw.startswith(b"data: "):
@@ -99,44 +113,76 @@ def get_container_logs(repo_id, profile, token):
99
  # — GEMINI FUNCTION DECLARATIONS —
100
 
101
  func_decls = [
102
- {
103
- "name":"create_space","description":"Create/get a HF Space",
104
- "parameters":{...}
105
- },
106
- {
107
- "name":"write_file","description":"Write/overwrite a file in the Space",
108
- "parameters":{...}
109
- },
110
- {
111
- "name":"list_files","description":"List files in the Space",
112
- "parameters":{...}
113
- },
114
- {
115
- "name":"get_build_logs","description":"Fetch build logs",
116
- "parameters":{...}
117
- },
118
- {
119
- "name":"get_run_logs","description":"Fetch run logs",
120
- "parameters":{...}
121
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
  ]
123
 
124
  # — CHAT HANDLER —
125
 
126
- def process_message(profile, token, user_msg,
127
- gemini_key, sidebar_repo, sidebar_sdk,
128
- chat_history, session):
129
- # Initialize the chat
 
 
130
  if session.get("chat") is None:
131
  client = genai.Client(api_key=gemini_key)
132
  cfg = types.GenerateContentConfig(
133
  system_instruction=(
134
  "You are a HF Spaces admin. You can create spaces, "
135
- "write files (app.py, requirements.txt, README.md...), "
136
- "list files, and fetch logs."
137
  ),
138
  temperature=0,
139
- tools=[ types.Tool(function_declarations=func_decls) ]
140
  )
141
  session["chat"] = client.chats.create(
142
  model="gemini-2.0-flash", config=cfg
@@ -144,7 +190,7 @@ def process_message(profile, token, user_msg,
144
  session["repo_id"] = None
145
  session["messages"]= []
146
 
147
- # Quick create via “call it NAME
148
  if session["repo_id"] is None:
149
  m = re.search(r"call (?:it )?([A-Za-z0-9_-]+)", user_msg, re.IGNORECASE)
150
  if m:
@@ -152,18 +198,17 @@ def process_message(profile, token, user_msg,
152
  rid, log, iframe = create_space(
153
  name, sidebar_sdk, profile, token
154
  )
155
- session["repo_id"] = rid
156
- session["iframe"] = iframe
157
- session["log"] = log
158
- session["files"] = ""
 
 
159
  session["messages"].append({
160
  "role":"assistant",
161
  "content":f"✅ Created Space `{name}`. {log}"
162
  })
163
- return (
164
- session["messages"],
165
- iframe, log, "", session
166
- )
167
 
168
  # Record user message
169
  session["messages"].append({"role":"user","content":user_msg})
@@ -195,7 +240,9 @@ def process_message(profile, token, user_msg,
195
 
196
  elif fn == "list_files":
197
  fl = list_repo_files(
198
- session["repo_id"], token=token.token, repo_type="space"
 
 
199
  )
200
  result = {"files": "\n".join(fl)}
201
 
@@ -209,20 +256,18 @@ def process_message(profile, token, user_msg,
209
  session["repo_id"], profile, token
210
  )}
211
 
212
- else:
213
- result = {"log": f"⚠️ Unknown function {fn}"}
214
-
215
- # Pass the raw dict so Pydantic can validate it
216
  session["chat"].send_message(
217
  types.Content(
218
  role="function",
219
  parts=[ types.Part(
220
  function_call=part.function_call,
221
  function_response=result
222
- ) ]
223
  )
224
  )
225
  assistant_text = session["chat"].get_history()[-1].parts[0].text
 
226
  else:
227
  assistant_text = part.text
228
 
@@ -232,9 +277,9 @@ def process_message(profile, token, user_msg,
232
  })
233
 
234
  # Update panels
235
- if "iframe" in result: session["iframe"] = result["iframe"]
236
- if "log" in result: session["log"] = result["log"]
237
- if "files" in result: session["files"] = result["files"]
238
 
239
  return (
240
  session["messages"],
@@ -248,23 +293,13 @@ def process_message(profile, token, user_msg,
248
 
249
  def sync_manual(profile, token, session):
250
  if not (profile and token and session.get("repo_id")):
251
- return (
252
- session.get("iframe",""),
253
- "⚠️ Cannot sync manual changes.",
254
- session.get("files",""),
255
- session
256
- )
257
  fl = list_repo_files(
258
  session["repo_id"], token=token.token, repo_type="space"
259
  )
260
  session["files"] = "\n".join(fl)
261
  session["log"] = "🔄 Manual changes synced."
262
- return (
263
- session.get("iframe",""),
264
- session["log"],
265
- session["files"],
266
- session
267
- )
268
 
269
  # — BUILD THE UI —
270
 
@@ -273,107 +308,93 @@ with gr.Blocks(css="""
273
  #main { padding:1rem; }
274
  """) as demo:
275
 
276
- with gr.Row():
277
- # Sidebar
278
- with gr.Column(elem_id="sidebar", scale=1):
279
- gr.Markdown("### 🔑 HF Login & Config")
280
- login_btn = gr.LoginButton(variant="huggingface", size="sm")
281
- profile_state = gr.State(None)
282
- token_state = gr.State(None)
283
-
284
- login_btn.click(None, [], [profile_state, token_state])
285
-
286
- status_md = gr.Markdown("*Not logged in.*")
287
- profile_state.change(show_profile,
288
- inputs=[profile_state],
289
- outputs=[status_md])
290
-
291
- models_md = gr.Markdown()
292
- profile_state.change(list_private_models,
293
- inputs=[profile_state, token_state],
294
- outputs=[models_md])
295
-
296
- gemini_key = gr.Textbox(label="Gemini API Key", type="password")
297
- sidebar_repo = gr.Textbox(label="Space name", placeholder="my-space")
298
- sidebar_sdk = gr.Radio(["gradio","streamlit"],
299
- value="gradio", label="SDK")
300
-
301
- gr.Markdown("---")
302
- confirm_btn = gr.Button("🔄 Confirm Manual Changes")
303
-
304
- # Main area
305
- with gr.Column(elem_id="main", scale=3):
306
- tabs = gr.Tabs()
307
- with tabs:
308
- with gr.TabItem("💬 Chat"):
309
- chatbox = gr.Chatbot(type="messages")
310
- user_input = gr.Textbox(show_label=False,
311
- placeholder="Ask the LLM…")
312
- send_btn = gr.Button("Send")
313
- with gr.TabItem("🛠️ Manual"):
314
- gr.Markdown("#### Create a Space")
315
- repo_m = gr.Textbox(label="Name")
316
- sdk_m = gr.Radio(["gradio","streamlit"],
317
- value="gradio", label="SDK")
318
- create_btn = gr.Button("Create Space")
319
- sess_id = gr.Textbox(visible=False)
320
- log_c = gr.Textbox(label="Log",
321
- interactive=False, lines=2)
322
- preview = gr.HTML("<p>No Space yet.</p>")
323
-
324
- create_btn.click(create_space,
325
- inputs=[repo_m, sdk_m,
326
- profile_state, token_state],
327
- outputs=[sess_id, log_c, preview])
328
-
329
- gr.Markdown("#### Upload File")
330
- path = gr.Textbox(label="Path", value="app.py")
331
- file_u = gr.File()
332
- upload_btn = gr.Button("Upload File")
333
- log_u = gr.Textbox(label="Log",
334
- interactive=False, lines=2)
335
-
336
- upload_btn.click(upload_file_to_space,
337
- inputs=[file_u, path, sess_id,
338
- profile_state, token_state],
339
- outputs=[log_u])
340
-
341
- gr.Markdown("#### Fetch Logs")
342
- b_btn = gr.Button("Build Logs")
343
- r_btn = gr.Button("Run Logs")
344
- log_b = gr.Textbox(label="Build",
345
- interactive=False, lines=5)
346
- log_r = gr.Textbox(label="Run",
347
- interactive=False, lines=5)
348
-
349
- b_btn.click(get_build_logs,
350
- inputs=[sess_id,
351
- profile_state, token_state],
352
- outputs=[log_b])
353
- r_btn.click(get_container_logs,
354
- inputs=[sess_id,
355
- profile_state, token_state],
356
- outputs=[log_r])
357
-
358
- # Persistent panels
359
- gr.Markdown("---")
360
- iframe_out = gr.HTML(label="🖼️ Preview")
361
- log_out = gr.Textbox(label="📋 Latest Log",
362
- lines=4)
363
- files_out = gr.Textbox(label="📚 Files", lines=4)
364
 
365
  state = gr.State({})
366
  send_btn.click(process_message,
367
- inputs=[profile_state, token_state,
368
- user_input, gemini_key,
369
- sidebar_repo, sidebar_sdk,
370
- chatbox, state],
371
- outputs=[chatbox, iframe_out,
372
- log_out, files_out, state])
373
 
374
  confirm_btn.click(sync_manual,
375
- inputs=[profile_state, token_state, state],
376
- outputs=[iframe_out, log_out, files_out, state])
377
 
378
  if __name__ == "__main__":
379
- demo.launch()
 
1
+ import re
2
+ import json
3
+ import os
4
+
5
  import gradio as gr
6
  from huggingface_hub import (
7
  create_repo, list_models, upload_file, list_repo_files, constants
8
  )
9
  from huggingface_hub.utils import build_hf_headers, get_session, hf_raise_for_status
10
+
11
  from google import genai
12
  from google.genai import types
13
 
 
39
  if not (profile and token):
40
  return "", "⚠️ Please log in first.", "<p>No Space created yet.</p>"
41
  rid = f"{profile.username}/{repo_name}"
42
+ create_repo(
43
+ repo_id=rid,
44
+ token=token.token,
45
+ exist_ok=True,
46
+ repo_type="space",
47
+ space_sdk=sdk
48
+ )
49
+ url = f"https://huggingface.co/spaces/{rid}"
50
+ log = f"✅ Space ready: {url} (SDK: {sdk})"
51
  iframe = f'<iframe src="{url}" width="100%" height="400px"></iframe>'
52
  return rid, log, iframe
53
 
 
58
  return "⚠️ Please create a Space first."
59
  with open(path, "w") as f:
60
  f.write(content)
61
+ upload_file(
62
+ path_or_fileobj=path,
63
+ path_in_repo=path,
64
+ repo_id=repo_id,
65
+ token=token.token,
66
+ repo_type="space"
67
+ )
68
  return f"✅ Wrote and uploaded `{path}`"
69
 
70
  def upload_file_to_space(file, path, repo_id, profile, token):
 
74
  return "⚠️ Please create a Space first."
75
  if not file:
76
  return "⚠️ No file selected."
77
+ upload_file(
78
+ path_or_fileobj=file.name,
79
+ path_in_repo=path,
80
+ repo_id=repo_id,
81
+ token=token.token,
82
+ repo_type="space"
83
+ )
84
  return f"✅ Uploaded `{path}` to `{repo_id}`"
85
 
86
  def _fetch_space_logs_level(repo_id, level):
 
92
  jwt = r.json()["token"]
93
  url = f"https://api.hf.space/v1/{repo_id}/logs/{level}"
94
  lines = []
95
+ with get_session().get(url, headers=build_hf_headers(token=jwt), stream=True) as resp:
 
 
96
  hf_raise_for_status(resp)
97
  for raw in resp.iter_lines():
98
  if raw.startswith(b"data: "):
 
113
  # — GEMINI FUNCTION DECLARATIONS —
114
 
115
  func_decls = [
116
+ types.FunctionDeclaration(
117
+ name="create_space",
118
+ description="Create/get a HF Space",
119
+ parameters={
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(
129
+ name="write_file",
130
+ description="Write/overwrite a file in the Space",
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(
141
+ name="list_files",
142
+ description="List files in the Space",
143
+ parameters={
144
+ "type": "object",
145
+ "properties": {"repo_id": {"type": "string"}},
146
+ "required": ["repo_id"],
147
+ }
148
+ ),
149
+ types.FunctionDeclaration(
150
+ name="get_build_logs",
151
+ description="Fetch build logs",
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
 
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:
 
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})
 
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
 
 
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
 
 
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"],
 
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
 
 
308
  #main { padding:1rem; }
309
  """) as demo:
310
 
311
+ # Sidebar
312
+ with gr.Column(elem_id="sidebar", scale=1):
313
+ gr.Markdown("### 🔑 HF Login & Config")
314
+ login_btn = gr.LoginButton(variant="huggingface", size="sm")
315
+ profile_state = gr.State(None)
316
+ token_state = gr.State(None)
317
+ login_btn.click(None, [], [profile_state, token_state])
318
+
319
+ status_md = gr.Markdown("*Not logged in.*")
320
+ profile_state.change(show_profile, [profile_state], [status_md])
321
+
322
+ models_md = gr.Markdown()
323
+ profile_state.change(list_private_models,
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):
336
+ tabs = gr.Tabs()
337
+ with tabs:
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,
355
+ [repo_m, sdk_m, profile_state, token_state],
356
+ [sess_id, log_c, preview])
357
+
358
+ gr.Markdown("#### Upload File")
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,
366
+ [file_u, path, sess_id, profile_state, token_state],
367
+ [log_u])
368
+
369
+ gr.Markdown("#### Fetch Logs")
370
+ b_btn = gr.Button("Build Logs")
371
+ r_btn = gr.Button("Run Logs")
372
+ log_b = gr.Textbox(label="Build", interactive=False, lines=5)
373
+ log_r = gr.Textbox(label="Run", interactive=False, lines=5)
374
+
375
+ b_btn.click(get_build_logs,
376
+ [sess_id, profile_state, token_state],
377
+ [log_b])
378
+ r_btn.click(get_container_logs,
379
+ [sess_id, profile_state, token_state],
380
+ [log_r])
381
+
382
+ gr.Markdown("---")
383
+ iframe_out = gr.HTML(label="🖼️ Preview")
384
+ log_out = gr.Textbox(label="📋 Latest Log", lines=4)
385
+ files_out = gr.Textbox(label="📚 Files", lines=4)
 
 
 
 
 
 
 
 
 
 
 
 
 
386
 
387
  state = gr.State({})
388
  send_btn.click(process_message,
389
+ [profile_state, token_state,
390
+ user_input, gemini_key,
391
+ sidebar_repo, sidebar_sdk,
392
+ chatbox, state],
393
+ [chatbox, iframe_out, log_out, files_out, state])
 
394
 
395
  confirm_btn.click(sync_manual,
396
+ [profile_state, token_state, state],
397
+ [iframe_out, log_out, files_out, state])
398
 
399
  if __name__ == "__main__":
400
+ demo.launch(share=True)