wuhp commited on
Commit
256db23
ยท
verified ยท
1 Parent(s): 2d4c4f3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +123 -187
app.py CHANGED
@@ -1,13 +1,7 @@
1
- import re
2
- import json
3
- import os
4
  import gradio as gr
5
  from huggingface_hub import (
6
- create_repo,
7
- list_models,
8
- upload_file,
9
- list_repo_files,
10
- constants
11
  )
12
  from huggingface_hub.utils import build_hf_headers, get_session, hf_raise_for_status
13
  from google import genai
@@ -41,13 +35,11 @@ def create_space(repo_name, sdk, profile, token):
41
  if not (profile and token):
42
  return "", "โš ๏ธ Please log in first.", "<p>No Space created yet.</p>"
43
  rid = f"{profile.username}/{repo_name}"
44
- create_repo(
45
- rid,
46
- token=token.token,
47
- exist_ok=True,
48
- repo_type="space",
49
- space_sdk=sdk
50
- )
51
  url = f"https://huggingface.co/spaces/{rid}"
52
  log = f"โœ… Space ready: {url} (SDK: {sdk})"
53
  iframe = f'<iframe src="{url}" width="100%" height="400px"></iframe>'
@@ -58,24 +50,24 @@ def write_file(path, content, repo_id, profile, token):
58
  return "โš ๏ธ Please log in first."
59
  if not repo_id:
60
  return "โš ๏ธ Please create a Space first."
 
61
  with open(path, "w") as f:
62
  f.write(content)
63
- upload_file(path, path, repo_id, token=token.token, repo_type="space")
 
 
64
  return f"โœ… Wrote and uploaded `{path}`"
65
 
66
  def list_files(repo_id, profile, token):
67
  if not (profile and token and repo_id):
68
  return "โš ๏ธ Please log in and create a Space first."
69
- files = list_repo_files(repo_id, token=token.token, repo_type="space")
70
- return "\n".join(files)
71
 
72
  def get_build_logs(repo_id, profile, token):
73
  if not (profile and token and repo_id):
74
  return "โš ๏ธ Please log in and create a Space first."
75
- r = get_session().get(
76
- f"{constants.ENDPOINT}/api/spaces/{repo_id}/jwt",
77
- headers=build_hf_headers()
78
- )
79
  hf_raise_for_status(r)
80
  jwt = r.json()["token"]
81
  url = f"https://api.hf.space/v1/{repo_id}/logs/build"
@@ -91,10 +83,8 @@ def get_build_logs(repo_id, profile, token):
91
  def get_container_logs(repo_id, profile, token):
92
  if not (profile and token and repo_id):
93
  return "โš ๏ธ Please log in and create a Space first."
94
- r = get_session().get(
95
- f"{constants.ENDPOINT}/api/spaces/{repo_id}/jwt",
96
- headers=build_hf_headers()
97
- )
98
  hf_raise_for_status(r)
99
  jwt = r.json()["token"]
100
  url = f"https://api.hf.space/v1/{repo_id}/logs/run"
@@ -111,56 +101,51 @@ def get_container_logs(repo_id, profile, token):
111
 
112
  func_decls = [
113
  {
114
- "name": "create_space",
115
- "description": "Create or get a HF Space",
116
- "parameters": {
117
- "type": "object",
118
- "properties": {
119
- "repo_name": {"type": "string"},
120
- "sdk": {"type": "string", "enum": ["gradio", "streamlit"]}
121
- },
122
- "required": ["repo_name", "sdk"]
123
- }
124
  },
125
  {
126
- "name": "write_file",
127
- "description": "Write or overwrite a file in the Space",
128
- "parameters": {
129
- "type": "object",
130
- "properties": {
131
- "path": {"type": "string"},
132
- "content": {"type": "string"}
133
- },
134
- "required": ["path", "content"]
135
- }
136
  },
137
  {
138
- "name": "list_files",
139
- "description": "List files in the Space",
140
- "parameters": {
141
- "type": "object",
142
- "properties": {"repo_id": {"type": "string"}},
143
- "required": ["repo_id"]
144
- }
145
  },
146
  {
147
- "name": "get_build_logs",
148
- "description": "Fetch build logs",
149
- "parameters": {
150
- "type": "object",
151
- "properties": {"repo_id": {"type": "string"}},
152
- "required": ["repo_id"]
153
- }
154
  },
155
  {
156
- "name": "get_run_logs",
157
- "description": "Fetch run logs",
158
- "parameters": {
159
- "type": "object",
160
- "properties": {"repo_id": {"type": "string"}},
161
- "required": ["repo_id"]
162
- }
163
- }
164
  ]
165
 
166
  # โ€” CHAT HANDLER โ€”
@@ -168,51 +153,47 @@ func_decls = [
168
  def process_message(profile, token, user_msg,
169
  gemini_key, sidebar_repo, sidebar_sdk,
170
  chat_history, session):
171
- # Initialize
172
  if session.get("chat") is None:
173
  client = genai.Client(api_key=gemini_key)
174
  cfg = types.GenerateContentConfig(
175
  system_instruction=(
176
- "You are a HF Spaces developer assistant. "
177
- "You MUST use function calls (create_space, write_file, list_files, "
178
- "get_build_logs, get_run_logs) to perform all actionsโ€”never return code inline."
 
179
  ),
180
  temperature=0,
181
- tools=[types.Tool(function_declarations=func_decls)],
182
  tool_config=types.ToolConfig(
183
  function_calling_config=types.FunctionCallingConfig(mode="ANY")
184
  )
185
  )
186
- session["chat"] = client.chats.create(
187
- model="gemini-2.0-flash", config=cfg
188
- )
189
- session["repo_id"] = None
190
- session["messages"] = []
191
 
192
- # Auto-create from โ€œcall it NAMEโ€
193
  if session["repo_id"] is None:
194
  m = re.search(r"call (?:it )?([A-Za-z0-9_-]+)", user_msg, re.IGNORECASE)
195
  if m:
196
  name = m.group(1)
197
  rid, log, iframe = create_space(name, sidebar_sdk, profile, token)
198
  session["repo_id"] = rid
199
- session["iframe"] = iframe
200
- session["log"] = log
201
- session["files"] = ""
202
- session["messages"].append({
203
- "role": "assistant",
204
- "content": log
205
- })
206
  return session["messages"], iframe, log, "", session
207
 
208
- # Record user
209
- session["messages"].append({"role": "user", "content": user_msg})
210
 
211
- # Send to Gemini
212
  resp = session["chat"].send_message(user_msg)
213
  part = resp.candidates[0].content.parts[0]
214
 
215
- # Handle function call
216
  args = part.function_call.args
217
  if isinstance(args, str):
218
  args = json.loads(args)
@@ -220,91 +201,65 @@ def process_message(profile, token, user_msg,
220
  result = {}
221
 
222
  if fn == "create_space":
223
- rid, log, iframe = create_space(
224
- args["repo_name"], args["sdk"], profile, token
225
- )
226
  session["repo_id"] = rid
227
- result = {"log": log, "iframe": iframe}
228
 
229
  elif fn == "write_file":
230
- status = write_file(
231
- args["path"], args["content"],
232
- session["repo_id"], profile, token
233
- )
234
- result = {"status": status}
235
 
236
  elif fn == "list_files":
237
- result = {"files": list_files(
238
- session["repo_id"], profile, token
239
- )}
240
 
241
  elif fn == "get_build_logs":
242
- result = {"log": get_build_logs(
243
- session["repo_id"], profile, token
244
- )}
245
 
246
  elif fn == "get_run_logs":
247
- result = {"log": get_container_logs(
248
- session["repo_id"], profile, token
249
- )}
250
 
251
  else:
252
- result = {"log": f"โš ๏ธ Unknown function {fn}"}
253
 
254
- # Respond via function_response
255
  session["chat"].send_message(
256
  types.Content(
257
  role="function",
258
- parts=[types.Part(
259
  function_call=part.function_call,
260
- function_response=json.dumps(result)
261
  )]
262
  )
263
  )
264
  assistant_text = session["chat"].get_history()[-1].parts[0].text
265
 
266
- # Record assistant
267
- session["messages"].append({
268
- "role": "assistant",
269
- "content": assistant_text
270
- })
271
 
272
- # Update panels
273
  if "iframe" in result: session["iframe"] = result["iframe"]
274
- if "log" in result: session["log"] = result["log"]
275
- if "files" in result: session["files"] = result["files"]
276
 
277
  return (
278
  session["messages"],
279
- session.get("iframe", ""),
280
- session.get("log", ""),
281
- session.get("files", ""),
282
  session
283
  )
284
 
285
- # โ€” SYNC MANUAL CHANGES โ€”
286
 
287
  def sync_manual(profile, token, session):
288
  if not (profile and token and session.get("repo_id")):
289
- return (
290
- session.get("iframe", ""),
291
- "โš ๏ธ Cannot sync manual changes.",
292
- session.get("files", ""),
293
- session
294
- )
295
- fl = list_repo_files(
296
- session["repo_id"], token=token.token, repo_type="space"
297
- )
298
  session["files"] = "\n".join(fl)
299
- session["log"] = "๐Ÿ”„ Manual changes synced."
300
- return (
301
- session["iframe"],
302
- session["log"],
303
- session["files"],
304
- session
305
- )
306
 
307
- # โ€” BUILD THE UI โ€”
308
 
309
  with gr.Blocks(css="""
310
  #sidebar { background:#f2f2f2; padding:1rem; border-right:1px solid #ccc; }
@@ -312,75 +267,56 @@ with gr.Blocks(css="""
312
  """) as demo:
313
 
314
  with gr.Row():
315
- # Sidebar
316
  with gr.Column(elem_id="sidebar", scale=1):
317
  gr.Markdown("### ๐Ÿ”‘ HF Login & Config")
318
- login_btn = gr.LoginButton(variant="huggingface", size="sm")
319
  profile_state = gr.State(None)
320
- token_state = gr.State(None)
321
 
322
  login_btn.click(None, [], [profile_state, token_state])
323
 
324
  status_md = gr.Markdown("*Not logged in.*")
325
- profile_state.change(
326
- show_profile, inputs=[profile_state], outputs=[status_md]
327
- )
328
 
329
  models_md = gr.Markdown()
330
- profile_state.change(
331
- list_private_models,
332
- inputs=[profile_state, token_state],
333
- outputs=[models_md]
334
- )
335
-
336
- gemini_key = gr.Textbox(
337
- label="Gemini API Key", type="password"
338
- )
339
- sidebar_repo = gr.Textbox(
340
- label="Space name", placeholder="my-space"
341
- )
342
- sidebar_sdk = gr.Radio(
343
- ["gradio", "streamlit"], value="gradio", label="SDK"
344
- )
345
 
 
 
 
346
  gr.Markdown("---")
347
- confirm_btn = gr.Button("๐Ÿ”„ Confirm Manual Changes")
348
 
349
- # Main area
350
  with gr.Column(elem_id="main", scale=3):
351
  tabs = gr.Tabs()
352
  with tabs:
353
  with gr.TabItem("๐Ÿ’ฌ Chat"):
354
- chatbox = gr.Chatbot(type="messages")
355
- user_input = gr.Textbox(
356
- show_label=False, placeholder="Ask the LLMโ€ฆ"
357
- )
358
- send_btn = gr.Button("Send")
359
  with gr.TabItem("๐Ÿ› ๏ธ Manual"):
360
  gr.Markdown("#### Manual Controls")
361
- # (Repeat buttons for create/upload/logs as needed)
362
 
363
  gr.Markdown("---")
364
  iframe_out = gr.HTML(label="๐Ÿ–ผ๏ธ Preview")
365
- log_out = gr.Textbox(label="๐Ÿ“‹ Latest Log", lines=4)
366
- files_out = gr.Textbox(label="๐Ÿ“š Files", lines=4)
367
 
368
  state = gr.State({})
369
- send_btn.click(
370
- process_message,
371
- inputs=[
372
- profile_state, token_state, user_input,
373
- gemini_key, sidebar_repo, sidebar_sdk,
374
- chatbox, state
375
- ],
376
- outputs=[chatbox, iframe_out, log_out, files_out, state]
377
- )
378
-
379
- confirm_btn.click(
380
- sync_manual,
381
- inputs=[profile_state, token_state, state],
382
- outputs=[iframe_out, log_out, files_out, state]
383
- )
384
 
385
  if __name__ == "__main__":
386
  demo.launch()
 
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
 
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>'
 
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}`"
60
 
61
  def list_files(repo_id, profile, token):
62
  if not (profile and token and repo_id):
63
  return "โš ๏ธ Please log in and create a Space first."
64
+ return "\n".join(list_repo_files(repo_id, token=token.token, repo_type="space"))
 
65
 
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
  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"
 
101
 
102
  func_decls = [
103
  {
104
+ "name":"create_space","description":"Create or get a HF Space",
105
+ "parameters":{
106
+ "type":"object",
107
+ "properties":{
108
+ "repo_name":{"type":"string"},
109
+ "sdk":{"type":"string","enum":["gradio","streamlit"]}
110
+ },
111
+ "required":["repo_name","sdk"]
112
+ }
 
113
  },
114
  {
115
+ "name":"write_file","description":"Write or overwrite a file in the Space",
116
+ "parameters":{
117
+ "type":"object",
118
+ "properties":{
119
+ "path":{"type":"string","description":"e.g. app.py, requirements.txt, README.md"},
120
+ "content":{"type":"string","description":"Full file contents"}
121
+ },
122
+ "required":["path","content"]
123
+ }
 
124
  },
125
  {
126
+ "name":"list_files","description":"List files in the Space",
127
+ "parameters":{
128
+ "type":"object",
129
+ "properties":{"repo_id":{"type":"string"}},
130
+ "required":["repo_id"]
131
+ }
 
132
  },
133
  {
134
+ "name":"get_build_logs","description":"Fetch build logs",
135
+ "parameters":{
136
+ "type":"object",
137
+ "properties":{"repo_id":{"type":"string"}},
138
+ "required":["repo_id"]
139
+ }
 
140
  },
141
  {
142
+ "name":"get_run_logs","description":"Fetch run logs",
143
+ "parameters":{
144
+ "type":"object",
145
+ "properties":{"repo_id":{"type":"string"}},
146
+ "required":["repo_id"]
147
+ }
148
+ },
 
149
  ]
150
 
151
  # โ€” CHAT HANDLER โ€”
 
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) ],
168
  tool_config=types.ToolConfig(
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)
 
201
  result = {}
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)
210
+ result = {"status":status}
 
 
 
211
 
212
  elif fn == "list_files":
213
+ result = {"files": list_files(session["repo_id"], profile, token)}
 
 
214
 
215
  elif fn == "get_build_logs":
216
+ result = {"log": get_build_logs(session["repo_id"], profile, token)}
 
 
217
 
218
  elif fn == "get_run_logs":
219
+ result = {"log": get_container_logs(session["repo_id"], profile, token)}
 
 
220
 
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"],
246
+ session.get("iframe",""),
247
+ session.get("log",""),
248
+ session.get("files",""),
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
  """) 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.*")
279
+ profile_state.change(show_profile, [profile_state], [status_md])
 
 
280
 
281
  models_md = gr.Markdown()
282
+ profile_state.change(list_private_models,
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()