wuhp commited on
Commit
c9b9839
Β·
verified Β·
1 Parent(s): 91af2dc

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +115 -72
app.py CHANGED
@@ -7,9 +7,9 @@ from huggingface_hub.utils import build_hf_headers, get_session, hf_raise_for_st
7
  from google import genai
8
  from google.genai import types
9
 
10
- # β€” USER INFO & HF SPACE HELPERS β€”β€”
11
 
12
- def show_profile(profile: gr.OAuthProfile | None) -> str:
13
  return f"βœ… Logged in as **{profile.username}**" if profile else "*Not logged in.*"
14
 
15
  def list_private_models(profile, oauth_token):
@@ -21,9 +21,7 @@ def list_private_models(profile, oauth_token):
21
  ]
22
  return "No models found." if not models else "Models:\n\n" + "\n - ".join(models)
23
 
24
- def enable_create(profile, oauth_token): return gr.update(interactive=bool(profile and oauth_token))
25
- def enable_repo_actions(repo_id, profile, oauth_token):
26
- return gr.update(interactive=bool(repo_id and profile and oauth_token))
27
 
28
  def create_space(repo_name, sdk, profile, oauth_token):
29
  if not (profile and oauth_token):
@@ -33,18 +31,22 @@ def create_space(repo_name, sdk, profile, oauth_token):
33
  url = f"https://huggingface.co/spaces/{rid}"
34
  return rid, f"βœ… Space ready: {url}", f'<iframe src="{url}" width="100%" height="400px"></iframe>'
35
 
36
- def upload_file_to_space(file, path_in_repo, repo_id, profile, oauth_token):
37
- if not (profile and oauth_token): return "⚠️ Please log in first."
38
- if not repo_id: return "⚠️ Create a Space first."
39
- if not file: return "⚠️ No file selected."
40
- upload_file(file.name, path_in_repo, repo_id, token=oauth_token.token, repo_type="space")
41
- return f"βœ… Uploaded `{path_in_repo}`"
 
 
 
42
 
43
- def fetch_logs(repo_id, level, profile, oauth_token):
44
  if not (profile and oauth_token and repo_id):
45
  return "⚠️ Log in & create a Space first."
46
- # SSE JWT
47
- r = get_session().get(f"{constants.ENDPOINT}/api/spaces/{repo_id}/jwt", headers=build_hf_headers())
 
48
  hf_raise_for_status(r); jwt = r.json()["token"]
49
  url = f"https://api.hf.space/v1/{repo_id}/logs/{level}"
50
  lines = []
@@ -59,10 +61,9 @@ def fetch_logs(repo_id, level, profile, oauth_token):
59
  def list_files(repo_id, profile, oauth_token):
60
  if not (profile and oauth_token and repo_id):
61
  return "⚠️ Log in & create a Space first."
62
- files = list_repo_files(repo_id, token=oauth_token.token, repo_type="space")
63
- return "\n".join(files)
64
 
65
- # β€” GEMINI FUNCTION DECLS β€”β€”
66
 
67
  func_decls = [
68
  {
@@ -102,10 +103,10 @@ func_decls = [
102
  },
103
  ]
104
 
105
- # β€” CHAT HANDLER β€”β€”
106
 
107
  def process_message(profile, oauth_token, user_msg, gemini_key, repo_name, sdk, chat_history, session):
108
- # init chat
109
  if session.get("chat") is None:
110
  client = genai.Client(api_key=gemini_key)
111
  cfg = types.GenerateContentConfig(
@@ -121,26 +122,32 @@ def process_message(profile, oauth_token, user_msg, gemini_key, repo_name, sdk,
121
  resp = chat.send_message(user_msg)
122
  part = resp.candidates[0].content.parts[0]
123
 
 
124
  if part.function_call:
125
  name = part.function_call.name
126
  args = json.loads(part.function_call.args)
127
- if name=="create_space":
128
  rid, log, iframe = create_space(args["repo_name"], args["sdk"], profile, oauth_token)
129
- session["repo_id"], result = rid, {"log":log,"iframe":iframe}
130
- elif name=="list_files":
131
- files = list_repo_files(session["repo_id"], token=oauth_token.token, repo_type="space")
132
- result = {"files":"\n".join(files)}
133
- elif name=="get_build_logs":
134
- result = {"log":fetch_logs(session["repo_id"], "build", profile, oauth_token)}
135
- elif name=="get_run_logs":
136
- result = {"log":fetch_logs(session["repo_id"], "run", profile, oauth_token)}
 
 
 
137
  else:
138
- result = {"log":f"⚠️ Unknown fn {name}"}
139
 
 
140
  chat.send_message(
141
  types.Content(
142
  role="function",
143
- parts=[ types.Part(function_call=part.function_call, function_response=json.dumps(result)) ]
 
144
  )
145
  )
146
  final = chat.get_history()[-1].parts[0].text
@@ -148,67 +155,88 @@ def process_message(profile, oauth_token, user_msg, gemini_key, repo_name, sdk,
148
  final = part.text
149
 
150
  # update panels
151
- session["iframe"] = session.get("iframe", "") if not result.get("iframe") else result["iframe"]
152
- session["log"] = session.get("log", "") if not result.get("log") else result["log"]
153
- session["files"] = session.get("files", "") if not result.get("files") else result["files"]
 
154
  chat_history[-1] = (user_msg, final)
155
- return chat_history, session["iframe"], session["log"], session["files"], session
156
 
157
  def sync_manual(profile, oauth_token, session):
158
- # after manual tweaks, refresh file list in panels & LLM memory
159
  if not (profile and oauth_token and session.get("repo_id")):
160
  return session.get("iframe",""), "⚠️ Cannot sync.", session.get("files",""), session
161
- files = list_repo_files(session["repo_id"], token=oauth_token.token, repo_type="space")
162
- session["files"] = "\n".join(files)
163
  session["log"] = "πŸ”„ Manual changes synced."
164
- return session["iframe"], session["log"], session["files"], session
165
 
166
- # β€” BUILD UI β€”β€”
167
 
168
- with gr.Blocks(title="HF Spaces + Gemini Chat", css="""
169
- #sidebar {background:#f7f7f7;padding:10px;border-right:1px solid #ddd;}
170
- #main {padding:10px;}
171
  """) as demo:
 
172
  with gr.Row():
 
173
  with gr.Column(elem_id="sidebar", scale=1):
174
  gr.Markdown("### πŸ”‘ Login & Config")
175
- login = gr.LoginButton(variant="huggingface", size="sm")
176
- status = gr.Markdown("*Not logged in.*")
177
- models = gr.Markdown()
178
- demo.load(show_profile, inputs=None, outputs=status)
179
- login.click(show_profile, inputs=None, outputs=status)
180
- demo.load(list_private_models, inputs=None, outputs=models)
181
- login.click(list_private_models, inputs=None, outputs=models)
 
 
 
 
 
 
 
 
 
 
182
 
183
  gemini_key = gr.Textbox(label="Gemini API Key", type="password")
184
  repo_name = gr.Textbox(label="Space name", placeholder="e.g. my-space")
185
  sdk_choice = gr.Radio(["gradio","streamlit"], value="gradio", label="SDK")
186
 
187
  gr.Markdown("---")
188
- sync_btn = gr.Button("βœ… Confirm Manual Changes")
189
- sync_btn.click(sync_manual, inputs=[status, login, gr.State({})],
190
- outputs=[gr.HTML(), gr.Textbox(), gr.Textbox(), gr.State({})])
 
 
191
 
 
192
  with gr.Column(elem_id="main", scale=3):
193
  tabs = gr.Tabs()
194
  with tabs:
195
  with gr.TabItem("πŸ’¬ Chat"):
196
- chatbox = gr.Chatbot(type="messages", label="Chat")
197
- user_in = gr.Textbox(show_label=False, placeholder="Ask LLM…")
198
  send = gr.Button("Send")
199
  with gr.TabItem("πŸ› οΈ Manual"):
200
  gr.Markdown("#### Create a Space")
201
  repo_m = gr.Textbox(label="Name")
202
- sdk_m = gr.Radio(["gradio","streamlit"], value="gradio", label="SDK")
203
  create = gr.Button("Create", interactive=False)
204
  sess = gr.Textbox(visible=False)
205
  log_c = gr.Textbox(label="Log", interactive=False, lines=2)
206
  preview = gr.HTML("<p>No Space yet</p>")
207
 
208
- demo.load(enable_create, inputs=None, outputs=create)
209
- login.click(enable_create, inputs=None, outputs=create)
210
- create.click(create_space,
211
- inputs=[repo_m, sdk_m, status, login],
 
 
 
 
 
212
  outputs=[sess, log_c, preview])
213
 
214
  gr.Markdown("#### Upload File")
@@ -217,23 +245,38 @@ with gr.Blocks(title="HF Spaces + Gemini Chat", css="""
217
  up_btn = gr.Button("Upload", interactive=False)
218
  log_u = gr.Textbox(label="Log", interactive=False, lines=2)
219
 
220
- demo.load(enable_repo_actions, inputs=[sess, status, login], outputs=up_btn)
 
 
 
 
 
 
221
  up_btn.click(upload_file_to_space,
222
- inputs=[file_u, path, sess, status, login],
223
- outputs=log_u)
224
 
225
  gr.Markdown("#### Fetch Logs")
226
- b_btn = gr.Button("Build Logs", interactive=False)
227
- r_btn = gr.Button("Run Logs", interactive=False)
228
- log_b = gr.Textbox(label="Build", interactive=False, lines=5)
229
- log_r = gr.Textbox(label="Run", interactive=False, lines=5)
 
 
 
 
 
 
 
230
 
231
- for b in (b_btn, r_btn):
232
- demo.load(enable_repo_actions, inputs=[sess, status, login], outputs=b)
233
- b_btn.click(fetch_logs, inputs=[sess, gr.State(), gr.State(), "build"], outputs=log_b)
234
- r_btn.click(fetch_logs, inputs=[sess, gr.State(), gr.State(), "run"], outputs=log_r)
 
 
235
 
236
- # always-visible panels
237
  gr.Markdown("---")
238
  iframe_out = gr.HTML(label="πŸ–ΌοΈ Preview")
239
  log_out = gr.Textbox(label="πŸ“‹ Latest Log", lines=4)
@@ -241,7 +284,7 @@ with gr.Blocks(title="HF Spaces + Gemini Chat", css="""
241
 
242
  state = gr.State({})
243
  send.click(process_message,
244
- inputs=[status, login, user_in, gemini_key, repo_name, sdk_choice, chatbox, state],
245
  outputs=[chatbox, iframe_out, log_out, files_out, state])
246
 
247
  if __name__ == "__main__":
 
7
  from google import genai
8
  from google.genai import types
9
 
10
+ # β€” HF LOGIN CALLBACKS β€” #
11
 
12
+ def show_profile(profile):
13
  return f"βœ… Logged in as **{profile.username}**" if profile else "*Not logged in.*"
14
 
15
  def list_private_models(profile, oauth_token):
 
21
  ]
22
  return "No models found." if not models else "Models:\n\n" + "\n - ".join(models)
23
 
24
+ # β€” HF SPACE HELPERS β€” #
 
 
25
 
26
  def create_space(repo_name, sdk, profile, oauth_token):
27
  if not (profile and oauth_token):
 
31
  url = f"https://huggingface.co/spaces/{rid}"
32
  return rid, f"βœ… Space ready: {url}", f'<iframe src="{url}" width="100%" height="400px"></iframe>'
33
 
34
+ def upload_file_to_space(file, path, repo_id, profile, oauth_token):
35
+ if not (profile and oauth_token):
36
+ return "⚠️ Please log in first."
37
+ if not repo_id:
38
+ return "⚠️ Create a Space first."
39
+ if not file:
40
+ return "⚠️ No file selected."
41
+ upload_file(file.name, path, repo_id, token=oauth_token.token, repo_type="space")
42
+ return f"βœ… Uploaded `{path}`"
43
 
44
+ def fetch_logs(repo_id, profile, oauth_token, level):
45
  if not (profile and oauth_token and repo_id):
46
  return "⚠️ Log in & create a Space first."
47
+ # get JWT
48
+ r = get_session().get(f"{constants.ENDPOINT}/api/spaces/{repo_id}/jwt",
49
+ headers=build_hf_headers())
50
  hf_raise_for_status(r); jwt = r.json()["token"]
51
  url = f"https://api.hf.space/v1/{repo_id}/logs/{level}"
52
  lines = []
 
61
  def list_files(repo_id, profile, oauth_token):
62
  if not (profile and oauth_token and repo_id):
63
  return "⚠️ Log in & create a Space first."
64
+ return "\n".join(list_repo_files(repo_id, token=oauth_token.token, repo_type="space"))
 
65
 
66
+ # β€” GEMINI FUNCTION DECLS β€” #
67
 
68
  func_decls = [
69
  {
 
103
  },
104
  ]
105
 
106
+ # β€” CHAT HANDLER β€” #
107
 
108
  def process_message(profile, oauth_token, user_msg, gemini_key, repo_name, sdk, chat_history, session):
109
+ # init on first call
110
  if session.get("chat") is None:
111
  client = genai.Client(api_key=gemini_key)
112
  cfg = types.GenerateContentConfig(
 
122
  resp = chat.send_message(user_msg)
123
  part = resp.candidates[0].content.parts[0]
124
 
125
+ result = {}
126
  if part.function_call:
127
  name = part.function_call.name
128
  args = json.loads(part.function_call.args)
129
+ if name == "create_space":
130
  rid, log, iframe = create_space(args["repo_name"], args["sdk"], profile, oauth_token)
131
+ session["repo_id"] = rid
132
+ result = {"log": log, "iframe": iframe}
133
+ elif name == "list_files":
134
+ files = list_files(session["repo_id"], profile, oauth_token)
135
+ result = {"files": files}
136
+ elif name == "get_build_logs":
137
+ log = fetch_logs(session["repo_id"], profile, oauth_token, "build")
138
+ result = {"log": log}
139
+ elif name == "get_run_logs":
140
+ log = fetch_logs(session["repo_id"], profile, oauth_token, "run")
141
+ result = {"log": log}
142
  else:
143
+ result = {"log": f"⚠️ Unknown function {name}"}
144
 
145
+ # feed back into chat
146
  chat.send_message(
147
  types.Content(
148
  role="function",
149
+ parts=[ types.Part(function_call=part.function_call,
150
+ function_response=json.dumps(result)) ]
151
  )
152
  )
153
  final = chat.get_history()[-1].parts[0].text
 
155
  final = part.text
156
 
157
  # update panels
158
+ if "iframe" in result: session["iframe"] = result["iframe"]
159
+ if "log" in result: session["log"] = result["log"]
160
+ if "files" in result: session["files"] = result["files"]
161
+
162
  chat_history[-1] = (user_msg, final)
163
+ return chat_history, session.get("iframe",""), session.get("log",""), session.get("files",""), session
164
 
165
  def sync_manual(profile, oauth_token, session):
166
+ # pull in any manual file changes
167
  if not (profile and oauth_token and session.get("repo_id")):
168
  return session.get("iframe",""), "⚠️ Cannot sync.", session.get("files",""), session
169
+ files = list_files(session["repo_id"], profile, oauth_token)
170
+ session["files"] = files
171
  session["log"] = "πŸ”„ Manual changes synced."
172
+ return session.get("iframe",""), session["log"], session["files"], session
173
 
174
+ # β€” LAYOUT β€” #
175
 
176
+ with gr.Blocks(css="""
177
+ #sidebar {background:#f7f7f7;padding:1em;border-right:1px solid #ddd;}
178
+ #main {padding:1em;}
179
  """) as demo:
180
+
181
  with gr.Row():
182
+ # Sidebar
183
  with gr.Column(elem_id="sidebar", scale=1):
184
  gr.Markdown("### πŸ”‘ Login & Config")
185
+ login_btn = gr.LoginButton(variant="huggingface", size="sm")
186
+ profile_state = gr.State(None)
187
+ token_state = gr.State(None)
188
+ # capture login outputs
189
+ login_btn.click(None, outputs=[profile_state, token_state])
190
+
191
+ status_md = gr.Markdown("*Not logged in.*")
192
+ profile_state.change(show_profile, inputs=[profile_state], outputs=[status_md])
193
+ login_btn.click(show_profile, inputs=[profile_state], outputs=[status_md])
194
+
195
+ models_md = gr.Markdown()
196
+ profile_state.change(list_private_models,
197
+ inputs=[profile_state, token_state],
198
+ outputs=[models_md])
199
+ login_btn.click(list_private_models,
200
+ inputs=[profile_state, token_state],
201
+ outputs=[models_md])
202
 
203
  gemini_key = gr.Textbox(label="Gemini API Key", type="password")
204
  repo_name = gr.Textbox(label="Space name", placeholder="e.g. my-space")
205
  sdk_choice = gr.Radio(["gradio","streamlit"], value="gradio", label="SDK")
206
 
207
  gr.Markdown("---")
208
+ sync_btn = gr.Button("πŸ”„ Confirm Manual Changes")
209
+ sync_out = gr.Markdown()
210
+ sync_btn.click(sync_manual,
211
+ inputs=[profile_state, token_state, gr.State({})],
212
+ outputs=[gr.HTML(), sync_out, gr.Textbox(), gr.State({})])
213
 
214
+ # Main area
215
  with gr.Column(elem_id="main", scale=3):
216
  tabs = gr.Tabs()
217
  with tabs:
218
  with gr.TabItem("πŸ’¬ Chat"):
219
+ chatbox = gr.Chatbot(type="messages")
220
+ user_in = gr.Textbox(show_label=False, placeholder="Ask the LLM…")
221
  send = gr.Button("Send")
222
  with gr.TabItem("πŸ› οΈ Manual"):
223
  gr.Markdown("#### Create a Space")
224
  repo_m = gr.Textbox(label="Name")
225
+ sdk_m = gr.Radio(["gradio","streamlit"], label="SDK")
226
  create = gr.Button("Create", interactive=False)
227
  sess = gr.Textbox(visible=False)
228
  log_c = gr.Textbox(label="Log", interactive=False, lines=2)
229
  preview = gr.HTML("<p>No Space yet</p>")
230
 
231
+ profile_state.change(enable_create,
232
+ inputs=[profile_state, token_state],
233
+ outputs=[create])
234
+ login_btn.click(enable_create,
235
+ inputs=[profile_state, token_state],
236
+ outputs=[create])
237
+
238
+ create.click(create_space,
239
+ inputs=[repo_m, sdk_m, profile_state, token_state],
240
  outputs=[sess, log_c, preview])
241
 
242
  gr.Markdown("#### Upload File")
 
245
  up_btn = gr.Button("Upload", interactive=False)
246
  log_u = gr.Textbox(label="Log", interactive=False, lines=2)
247
 
248
+ profile_state.change(enable_repo_actions,
249
+ inputs=[sess, profile_state, token_state],
250
+ outputs=[up_btn])
251
+ login_btn.click(enable_repo_actions,
252
+ inputs=[sess, profile_state, token_state],
253
+ outputs=[up_btn])
254
+
255
  up_btn.click(upload_file_to_space,
256
+ inputs=[file_u, path, sess, profile_state, token_state],
257
+ outputs=[log_u])
258
 
259
  gr.Markdown("#### Fetch Logs")
260
+ b_btn = gr.Button("Build Logs", interactive=False)
261
+ r_btn = gr.Button("Run Logs", interactive=False)
262
+ log_b = gr.Textbox(label="Build", interactive=False, lines=5)
263
+ log_r = gr.Textbox(label="Run", interactive=False, lines=5)
264
+
265
+ profile_state.change(enable_repo_actions,
266
+ inputs=[sess, profile_state, token_state],
267
+ outputs=[b_btn, r_btn])
268
+ login_btn.click(enable_repo_actions,
269
+ inputs=[sess, profile_state, token_state],
270
+ outputs=[b_btn, r_btn])
271
 
272
+ b_btn.click(fetch_logs,
273
+ inputs=[sess, profile_state, token_state, gr.State("build")],
274
+ outputs=[log_b])
275
+ r_btn.click(fetch_logs,
276
+ inputs=[sess, profile_state, token_state, gr.State("run")],
277
+ outputs=[log_r])
278
 
279
+ # always‑visible panels
280
  gr.Markdown("---")
281
  iframe_out = gr.HTML(label="πŸ–ΌοΈ Preview")
282
  log_out = gr.Textbox(label="πŸ“‹ Latest Log", lines=4)
 
284
 
285
  state = gr.State({})
286
  send.click(process_message,
287
+ inputs=[profile_state, token_state, user_in, gemini_key, repo_name, sdk_choice, chatbox, state],
288
  outputs=[chatbox, iframe_out, log_out, files_out, state])
289
 
290
  if __name__ == "__main__":