wuhp commited on
Commit
fb9266f
Β·
verified Β·
1 Parent(s): 0df9635

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +225 -117
app.py CHANGED
@@ -1,138 +1,194 @@
1
  import os, json
2
  import gradio as gr
3
- from huggingface_hub import create_repo, upload_file, list_repo_files, constants
 
 
 
 
 
 
 
4
  from huggingface_hub.utils import build_hf_headers, get_session, hf_raise_for_status
5
  from google import genai
6
  from google.genai import types
7
 
8
- # β€”β€”β€” HELPERS FOR HF SPACES β€”β€”β€”
9
 
10
- def create_space(repo_name: str, sdk: str, hf_token: str):
11
- """Create or get a HF Space, return (repo_id, log, iframe)."""
12
- token = hf_token or os.getenv("HF_TOKEN", "")
13
- if not token:
14
- return None, "⚠️ No HF token provided.", ""
15
- os.environ["HF_TOKEN"] = token
16
- repo_id = f"{os.getenv('HF_USERNAME', '')}/{repo_name}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  create_repo(
18
  repo_id=repo_id,
19
- token=token,
20
  exist_ok=True,
21
  repo_type="space",
22
- space_sdk=sdk,
23
  )
24
- url = f"https://huggingface.co/spaces/{repo_id}"
25
- return repo_id, f"βœ… Space ready: {url} (SDK={sdk})", f'<iframe src="{url}" width="100%" height="400px"></iframe>'
26
-
27
- def upload_file_to_space(repo_id: str, local_path: str, hf_token: str):
28
- """Upload a local file to the Space."""
29
- token = hf_token or os.getenv("HF_TOKEN", "")
30
- if not (repo_id and token and os.path.exists(local_path)):
31
- return "⚠️ Missing repo_id, token, or file not found."
32
- os.environ["HF_TOKEN"] = token
 
 
 
 
 
 
 
 
 
33
  upload_file(
34
- path_or_fileobj=local_path,
35
- path_in_repo=os.path.basename(local_path),
36
  repo_id=repo_id,
37
- token=token,
38
- repo_type="space",
39
  )
40
- return f"βœ… Uploaded `{os.path.basename(local_path)}` to `{repo_id}`"
41
-
42
- def fetch_logs(repo_id: str, level: str, hf_token: str):
43
- """Stream build or run logs."""
44
- token = hf_token or os.getenv("HF_TOKEN", "")
45
- if not (repo_id and token):
46
- return "⚠️ Missing repo_id or HF token."
47
- # 1) get JWT
48
  jwt_url = f"{constants.ENDPOINT}/api/spaces/{repo_id}/jwt"
49
  r = get_session().get(jwt_url, headers=build_hf_headers())
50
  hf_raise_for_status(r)
51
  jwt = r.json()["token"]
52
- # 2) stream
53
- url = f"https://api.hf.space/v1/{repo_id}/logs/{level}"
54
  lines = []
55
- with get_session().get(url, headers=build_hf_headers(token=jwt), stream=True) as resp:
56
  hf_raise_for_status(resp)
57
  for raw in resp.iter_lines():
58
- if not raw.startswith(b"data: "): continue
59
- ev = json.loads(raw[len(b"data: "):])
60
- ts, txt = ev.get("timestamp",""), ev.get("data","")
 
 
 
 
 
 
61
  lines.append(f"[{ts}] {txt}")
62
  return "\n".join(lines)
63
 
64
- def list_files(repo_id: str, hf_token: str):
65
- """List files in the Space repo."""
66
- token = hf_token or os.getenv("HF_TOKEN", "")
67
- if not (repo_id and token):
68
- return "⚠️ Missing repo_id or HF token."
69
- return "\n".join(list_repo_files(repo_id, token=token, repo_type="space"))
 
 
 
 
 
 
 
 
 
 
 
70
 
71
 
72
- # β€”β€”β€” GEMINI FUNCTION DECLARATIONS β€”β€”β€”
73
 
74
  func_decls = [
75
  {
76
- "name": "create_space",
77
- "description": "Create or get a Hugging Face Space",
78
- "parameters": {
79
- "type":"object",
80
- "properties":{
81
- "repo_name":{"type":"string"},
82
- "sdk":{"type":"string","enum":["gradio","streamlit"]}
83
- },
84
- "required":["repo_name","sdk"]
85
- }
86
- },
87
- {
88
- "name":"upload_file",
89
- "description":"Upload a file to the Space",
90
- "parameters":{
91
- "type":"object",
92
- "properties":{
93
- "repo_id":{"type":"string"},
94
- "local_path":{"type":"string"}
95
- },
96
- "required":["repo_id","local_path"]
97
- }
98
  },
99
  {
100
- "name":"list_files",
101
- "description":"List files in the Space",
102
- "parameters":{
103
- "type":"object",
104
- "properties":{"repo_id":{"type":"string"}},
105
- "required":["repo_id"]
106
- }
107
  },
108
  {
109
- "name":"get_build_logs",
110
- "description":"Fetch the build logs",
111
- "parameters":{
112
- "type":"object",
113
- "properties":{"repo_id":{"type":"string"}},
114
- "required":["repo_id"]
115
- }
116
  },
117
  {
118
- "name":"get_run_logs",
119
- "description":"Fetch the run logs",
120
- "parameters":{
121
- "type":"object",
122
- "properties":{"repo_id":{"type":"string"}},
123
- "required":["repo_id"]
124
- }
125
  },
126
  ]
127
 
128
- # β€”β€”β€” CHAT PROCESSING β€”β€”β€”
129
 
130
- def process_message(user_msg, gemini_key, repo_name, sdk, hf_token, chat_history, session):
131
- # init Gemini chat
 
 
 
 
 
 
 
 
 
132
  if session.get("chat") is None:
133
  client = genai.Client(api_key=gemini_key)
134
  cfg = types.GenerateContentConfig(
135
- system_instruction="You are a HF Spaces assistant. Use functions to manage spaces.",
136
  temperature=0,
137
  tools=[ types.Tool(function_declarations=func_decls) ]
138
  )
@@ -140,32 +196,31 @@ def process_message(user_msg, gemini_key, repo_name, sdk, hf_token, chat_history
140
  session["repo_id"] = None
141
 
142
  chat = session["chat"]
143
- # record user
144
  chat_history = chat_history + [(user_msg, None)]
145
  resp = chat.send_message(user_msg)
146
  part = resp.candidates[0].content.parts[0]
147
 
148
- # if function call requested
149
  if part.function_call:
150
  name = part.function_call.name
151
  args = json.loads(part.function_call.args)
152
  # dispatch
153
  if name=="create_space":
154
- rid, log, iframe = create_space(args["repo_name"], args["sdk"], hf_token)
155
  session["repo_id"] = rid
156
  result = {"repo_id":rid,"log":log,"iframe":iframe}
157
- elif name=="upload_file":
158
- result = {"log": upload_file_to_space(args["repo_id"], args["local_path"], hf_token)}
159
  elif name=="list_files":
160
- result = {"files": list_files(args["repo_id"], hf_token)}
 
161
  elif name=="get_build_logs":
162
- result = {"log": fetch_logs(args["repo_id"], "build", hf_token)}
163
  elif name=="get_run_logs":
164
- result = {"log": fetch_logs(args["repo_id"], "run", hf_token)}
165
  else:
166
  result = {"log":f"⚠️ Unknown function {name}"}
167
 
168
- # feed function result back to chat
169
  chat.send_message(
170
  types.Content(
171
  role="function",
@@ -176,41 +231,94 @@ def process_message(user_msg, gemini_key, repo_name, sdk, hf_token, chat_history
176
  else:
177
  final = part.text
178
 
179
- # update panels
180
  iframe = session.get("iframe", "")
181
  log = session.get("log", "")
182
  files = session.get("files", "")
183
 
184
  if part.function_call:
185
  session["iframe"] = result.get("iframe", iframe)
186
- session["log"] = result.get("log", log)
187
- session["files"] = result.get("files", files)
188
 
189
  chat_history[-1] = (user_msg, final)
190
  return chat_history, session["iframe"], session["log"], session["files"], session
191
 
192
- # β€”β€”β€” GRADIO INTERFACE β€”β€”β€”
193
 
194
- with gr.Blocks(title="HF Spaces β†’ Gemini Chat") as demo:
195
  with gr.Row():
 
196
  with gr.Column(scale=1):
197
- gemini_key = gr.Textbox(label="Gemini API Key", type="password")
198
- repo_name = gr.Textbox(label="Space name", placeholder="my-space")
199
- sdk_choice = gr.Radio(["gradio","streamlit"], value="gradio", label="SDK")
200
- hf_token = gr.Textbox(label="HF Token (opt)", type="password")
 
 
 
 
 
 
 
 
 
201
  with gr.Column(scale=3):
202
- chatbox = gr.Chatbot()
203
- user_input = gr.Textbox(show_label=False, placeholder="e.g. Create Space")
204
- send_btn = gr.Button("Send")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
 
206
- iframe_out = gr.HTML(label="Space Preview")
207
- log_out = gr.Textbox(label="Latest Log", lines=8)
208
- files_out = gr.Textbox(label="Files in Space", lines=4)
209
 
210
  state = gr.State({})
211
  send_btn.click(
212
  fn=process_message,
213
- inputs=[user_input, gemini_key, repo_name, sdk_choice, hf_token, chatbox, state],
214
  outputs=[chatbox, iframe_out, log_out, files_out, state]
215
  )
216
 
 
1
  import os, json
2
  import gradio as gr
3
+ import requests
4
+ from huggingface_hub import (
5
+ create_repo,
6
+ list_models,
7
+ upload_file,
8
+ list_repo_files,
9
+ constants,
10
+ )
11
  from huggingface_hub.utils import build_hf_headers, get_session, hf_raise_for_status
12
  from google import genai
13
  from google.genai import types
14
 
15
+ # β€” USER INFO & MODEL LISTING (UNCHANGED) β€”
16
 
17
+ def show_profile(profile: gr.OAuthProfile | None) -> str:
18
+ if profile is None:
19
+ return "*Not logged in.*"
20
+ return f"βœ… Logged in as **{profile.username}**"
21
+
22
+ def list_private_models(
23
+ profile: gr.OAuthProfile | None,
24
+ oauth_token: gr.OAuthToken | None
25
+ ) -> str:
26
+ if profile is None or oauth_token is None:
27
+ return "Please log in to see your models."
28
+ models = [
29
+ f"{m.id} ({'private' if m.private else 'public'})"
30
+ for m in list_models(author=profile.username, token=oauth_token.token)
31
+ ]
32
+ return "No models found." if not models else "Models:\n\n" + "\n - ".join(models)
33
+
34
+ def enable_create(
35
+ profile: gr.OAuthProfile | None,
36
+ oauth_token: gr.OAuthToken | None
37
+ ):
38
+ return gr.update(interactive=profile is not None)
39
+
40
+ def enable_repo_actions(
41
+ repo_id: str,
42
+ profile: gr.OAuthProfile | None,
43
+ oauth_token: gr.OAuthToken | None
44
+ ):
45
+ return gr.update(interactive=bool(repo_id and profile and oauth_token))
46
+
47
+ def create_space(
48
+ repo_name: str,
49
+ sdk: str,
50
+ profile: gr.OAuthProfile | None,
51
+ oauth_token: gr.OAuthToken | None
52
+ ) -> tuple[str, str, str]:
53
+ if not profile or not oauth_token:
54
+ return "", "⚠️ Please log in first.", "<p>No Space created yet.</p>"
55
+ repo_id = f"{profile.username}/{repo_name}"
56
  create_repo(
57
  repo_id=repo_id,
58
+ token=oauth_token.token,
59
  exist_ok=True,
60
  repo_type="space",
61
+ space_sdk=sdk
62
  )
63
+ url = f"https://huggingface.co/spaces/{repo_id}"
64
+ logmsg = f"βœ… Space ready: {url} (SDK: {sdk})"
65
+ iframe = f'<iframe src="{url}" width="100%" height="500px"></iframe>'
66
+ return repo_id, logmsg, iframe
67
+
68
+ def upload_file_to_space(
69
+ file,
70
+ path_in_repo: str,
71
+ repo_id: str,
72
+ profile: gr.OAuthProfile | None,
73
+ oauth_token: gr.OAuthToken | None
74
+ ) -> str:
75
+ if not profile or not oauth_token:
76
+ return "⚠️ Please log in first."
77
+ if not repo_id:
78
+ return "⚠️ Please create a Space first."
79
+ if not file:
80
+ return "⚠️ No file selected."
81
  upload_file(
82
+ path_or_fileobj=file.name,
83
+ path_in_repo=path_in_repo,
84
  repo_id=repo_id,
85
+ token=oauth_token.token,
86
+ repo_type="space"
87
  )
88
+ return f"βœ… Uploaded `{path_in_repo}` to `{repo_id}`"
89
+
90
+ def _fetch_space_logs_level(repo_id: str, level: str) -> str:
 
 
 
 
 
91
  jwt_url = f"{constants.ENDPOINT}/api/spaces/{repo_id}/jwt"
92
  r = get_session().get(jwt_url, headers=build_hf_headers())
93
  hf_raise_for_status(r)
94
  jwt = r.json()["token"]
95
+ logs_url = f"https://api.hf.space/v1/{repo_id}/logs/{level}"
 
96
  lines = []
97
+ with get_session().get(logs_url, headers=build_hf_headers(token=jwt), stream=True) as resp:
98
  hf_raise_for_status(resp)
99
  for raw in resp.iter_lines():
100
+ if not raw.startswith(b"data: "):
101
+ continue
102
+ payload = raw[len(b"data: "):]
103
+ try:
104
+ event = json.loads(payload.decode())
105
+ except:
106
+ continue
107
+ ts = event.get("timestamp", "")
108
+ txt = event.get("data", "")
109
  lines.append(f"[{ts}] {txt}")
110
  return "\n".join(lines)
111
 
112
+ def get_build_logs(
113
+ repo_id: str,
114
+ profile: gr.OAuthProfile | None,
115
+ oauth_token: gr.OAuthToken | None
116
+ ) -> str:
117
+ if not (profile and oauth_token and repo_id):
118
+ return "⚠️ Please log in and create a Space first."
119
+ return _fetch_space_logs_level(repo_id, "build")
120
+
121
+ def get_container_logs(
122
+ repo_id: str,
123
+ profile: gr.OAuthProfile | None,
124
+ oauth_token: gr.OAuthToken | None
125
+ ) -> str:
126
+ if not (profile and oauth_token and repo_id):
127
+ return "⚠️ Please log in and create a Space first."
128
+ return _fetch_space_logs_level(repo_id, "run")
129
 
130
 
131
+ # β€” GEMINI FUNCTION DECLARATIONS β€”
132
 
133
  func_decls = [
134
  {
135
+ "name": "create_space",
136
+ "description": "Create or get a Hugging Face Space",
137
+ "parameters": {
138
+ "type": "object",
139
+ "properties": {
140
+ "repo_name": {"type":"string"},
141
+ "sdk": {"type":"string","enum":["gradio","streamlit"]},
142
+ },
143
+ "required": ["repo_name","sdk"]
144
+ }
 
 
 
 
 
 
 
 
 
 
 
 
145
  },
146
  {
147
+ "name":"list_files",
148
+ "description":"List files in the Space",
149
+ "parameters":{
150
+ "type":"object",
151
+ "properties":{"repo_id":{"type":"string"}},
152
+ "required":["repo_id"]
153
+ }
154
  },
155
  {
156
+ "name": "get_build_logs",
157
+ "description": "Fetch the build logs",
158
+ "parameters":{
159
+ "type":"object",
160
+ "properties":{"repo_id":{"type":"string"}},
161
+ "required":["repo_id"]
162
+ }
163
  },
164
  {
165
+ "name": "get_run_logs",
166
+ "description": "Fetch the run logs",
167
+ "parameters":{
168
+ "type":"object",
169
+ "properties":{"repo_id":{"type":"string"}},
170
+ "required":["repo_id"]
171
+ }
172
  },
173
  ]
174
 
175
+ # β€” CHAT HANDLER β€”
176
 
177
+ def process_message(
178
+ user_msg: str,
179
+ profile: gr.OAuthProfile | None,
180
+ oauth_token: gr.OAuthToken | None,
181
+ gemini_key: str,
182
+ repo_name: str,
183
+ sdk: str,
184
+ chat_history,
185
+ session
186
+ ):
187
+ # init
188
  if session.get("chat") is None:
189
  client = genai.Client(api_key=gemini_key)
190
  cfg = types.GenerateContentConfig(
191
+ system_instruction="You are an admin for HF Spaces: use functions to create spaces, list files, and fetch logs.",
192
  temperature=0,
193
  tools=[ types.Tool(function_declarations=func_decls) ]
194
  )
 
196
  session["repo_id"] = None
197
 
198
  chat = session["chat"]
199
+ # user turn
200
  chat_history = chat_history + [(user_msg, None)]
201
  resp = chat.send_message(user_msg)
202
  part = resp.candidates[0].content.parts[0]
203
 
204
+ # function call?
205
  if part.function_call:
206
  name = part.function_call.name
207
  args = json.loads(part.function_call.args)
208
  # dispatch
209
  if name=="create_space":
210
+ rid, log, iframe = create_space(args["repo_name"], args["sdk"], profile, oauth_token)
211
  session["repo_id"] = rid
212
  result = {"repo_id":rid,"log":log,"iframe":iframe}
 
 
213
  elif name=="list_files":
214
+ files = list_repo_files(session["repo_id"], token=oauth_token.token, repo_type="space") if session["repo_id"] else []
215
+ result = {"files":"\n".join(files)}
216
  elif name=="get_build_logs":
217
+ result = {"log":get_build_logs(session["repo_id"], profile, oauth_token)}
218
  elif name=="get_run_logs":
219
+ result = {"log":get_container_logs(session["repo_id"], profile, oauth_token)}
220
  else:
221
  result = {"log":f"⚠️ Unknown function {name}"}
222
 
223
+ # send back
224
  chat.send_message(
225
  types.Content(
226
  role="function",
 
231
  else:
232
  final = part.text
233
 
234
+ # pick up panels
235
  iframe = session.get("iframe", "")
236
  log = session.get("log", "")
237
  files = session.get("files", "")
238
 
239
  if part.function_call:
240
  session["iframe"] = result.get("iframe", iframe)
241
+ session["log"] = result.get("log", log)
242
+ session["files"] = result.get("files", files)
243
 
244
  chat_history[-1] = (user_msg, final)
245
  return chat_history, session["iframe"], session["log"], session["files"], session
246
 
247
+ # β€” BUILD THE UI β€”
248
 
249
+ with gr.Blocks(title="HF Spaces + Gemini Chat") as demo:
250
  with gr.Row():
251
+ # β—€ Sidebar: login, models, chat config
252
  with gr.Column(scale=1):
253
+ gr.Markdown("## πŸ”‘ Sign in & Chat Config")
254
+ login_btn = gr.LoginButton(variant="huggingface", size="sm")
255
+ status_md = gr.Markdown("*Not logged in.*")
256
+ models_md = gr.Markdown()
257
+ demo.load(show_profile, inputs=None, outputs=status_md)
258
+ login_btn.click(show_profile, inputs=None, outputs=status_md)
259
+ demo.load(list_private_models, inputs=None, outputs=models_md)
260
+ login_btn.click(list_private_models, inputs=None, outputs=models_md)
261
+
262
+ gemini_key = gr.Textbox(label="Gemini API Key", type="password")
263
+ repo_name = gr.Textbox(label="Space name", placeholder="e.g. my-space")
264
+ sdk_choice = gr.Radio(["gradio","streamlit"], value="gradio", label="SDK")
265
+ # β–Ά Main: chat + panels + manual UI
266
  with gr.Column(scale=3):
267
+ gr.Markdown("## πŸ’¬ Chat to manage your Space")
268
+ chatbox = gr.Chatbot()
269
+ user_in = gr.Textbox(show_label=False, placeholder="e.g. Create Space")
270
+ send_btn = gr.Button("Send")
271
+
272
+ iframe_out = gr.HTML(label="πŸ–ΌοΈ Preview")
273
+ log_out = gr.Textbox(label="πŸ“‹ Latest Log", lines=8)
274
+ files_out = gr.Textbox(label="πŸ“š Files in Space", lines=4)
275
+
276
+ gr.Markdown("---\n## πŸ› οΈ Manual Controls (unchanged)")
277
+ # β€” Manual CREATE SPACE β€”
278
+ repo_name_m = gr.Textbox(label="New Space name", placeholder="my-space")
279
+ sdk_selector = gr.Radio(["gradio","streamlit"], value="gradio", label="SDK for manual")
280
+ create_btn = gr.Button("Create Space", interactive=False)
281
+ session_id = gr.Textbox(visible=False)
282
+ create_logs = gr.Textbox(label="Create Logs", interactive=False, lines=3)
283
+ preview_m = gr.HTML("<p>No Space created yet.</p>")
284
+
285
+ demo.load(enable_create, inputs=None, outputs=[create_btn])
286
+ login_btn.click(enable_create, inputs=None, outputs=[create_btn])
287
+ create_btn.click(
288
+ fn=create_space,
289
+ inputs=[repo_name_m, sdk_selector, status_md, login_btn.token],
290
+ outputs=[session_id, create_logs, preview_m]
291
+ )
292
+
293
+ # β€” Manual UPLOAD FILES β€”
294
+ path_in_repo = gr.Textbox(label="Path in Space", value="app.py")
295
+ file_uploader = gr.File(label="Select file")
296
+ upload_btn = gr.Button("Upload File", interactive=False)
297
+ upload_logs = gr.Textbox(label="Upload Logs", interactive=False, lines=2)
298
+
299
+ demo.load(enable_repo_actions, inputs=[session_id, status_md, login_btn.token], outputs=[upload_btn])
300
+ upload_btn.click(
301
+ fn=upload_file_to_space,
302
+ inputs=[file_uploader, path_in_repo, session_id, status_md, login_btn.token],
303
+ outputs=[upload_logs]
304
+ )
305
+
306
+ # β€” Manual FETCH LOGS β€”
307
+ build_btn = gr.Button("Get Build Logs", interactive=False)
308
+ run_btn = gr.Button("Get Container Logs", interactive=False)
309
+ build_logs_md = gr.Textbox(label="Build Logs", interactive=False, lines=10)
310
+ run_logs_md = gr.Textbox(label="Container Logs", interactive=False, lines=10)
311
+
312
+ for b in (build_btn, run_btn):
313
+ demo.load(enable_repo_actions, inputs=[session_id, status_md, login_btn.token], outputs=[b])
314
 
315
+ build_btn.click(fn=get_build_logs, inputs=[session_id, status_md, login_btn.token], outputs=[build_logs_md])
316
+ run_btn.click( fn=get_container_logs, inputs=[session_id, status_md, login_btn.token], outputs=[run_logs_md])
 
317
 
318
  state = gr.State({})
319
  send_btn.click(
320
  fn=process_message,
321
+ inputs=[user_in, status_md, login_btn.token, gemini_key, repo_name, sdk_choice, chatbox, state],
322
  outputs=[chatbox, iframe_out, log_out, files_out, state]
323
  )
324