wuhp commited on
Commit
599725a
Β·
verified Β·
1 Parent(s): b2c5c54

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +223 -131
app.py CHANGED
@@ -1,19 +1,21 @@
1
- import 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
 
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):
16
- if not (profile and oauth_token):
17
  return "Please log in to see your models."
18
  models = [
19
  f"{m.id} ({'private' if m.private else 'public'})"
@@ -21,60 +23,94 @@ 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
- # β€” MANUAL HELPERS β€” #
25
-
26
- def enable_create(profile, oauth_token):
27
- return gr.update(interactive=bool(profile and oauth_token))
28
 
29
- def enable_repo_actions(repo_id, profile, oauth_token):
 
 
30
  return gr.update(interactive=bool(repo_id and profile and oauth_token))
31
 
32
- def create_space(repo_name, sdk, profile, oauth_token):
33
- if not (profile and oauth_token):
34
- return "", "⚠️ Please log in first.", "<p>No Space.</p>"
35
- rid = f"{profile.username}/{repo_name}"
36
- create_repo(rid, token=oauth_token.token, exist_ok=True, repo_type="space", space_sdk=sdk)
37
- url = f"https://huggingface.co/spaces/{rid}"
38
- return rid, f"βœ… Space ready: {url}", f'<iframe src="{url}" width="100%" height="400px"></iframe>'
39
-
40
- def upload_file_to_space(file, path, repo_id, profile, oauth_token):
41
- if not (profile and oauth_token):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  return "⚠️ Please log in first."
43
  if not repo_id:
44
- return "⚠️ Create a Space first."
45
  if not file:
46
  return "⚠️ No file selected."
47
- upload_file(file.name, path, repo_id, token=oauth_token.token, repo_type="space")
48
- return f"βœ… Uploaded `{path}`"
49
-
50
- def fetch_logs(repo_id, profile, oauth_token, level):
51
- if not (profile and oauth_token and repo_id):
52
- return "⚠️ Log in & create a Space first."
53
- # get JWT
54
- r = get_session().get(f"{constants.ENDPOINT}/api/spaces/{repo_id}/jwt", headers=build_hf_headers())
 
 
 
 
55
  hf_raise_for_status(r)
56
  jwt = r.json()["token"]
57
- url = f"https://api.hf.space/v1/{repo_id}/logs/{level}"
58
  lines = []
59
- with get_session().get(url, headers=build_hf_headers(token=jwt), stream=True) as resp:
60
  hf_raise_for_status(resp)
61
  for raw in resp.iter_lines():
62
  if raw.startswith(b"data: "):
63
- ev = json.loads(raw[6:])
64
- lines.append(f"[{ev.get('timestamp','')}] {ev.get('data','')}")
 
65
  return "\n".join(lines)
66
 
67
- def list_files(repo_id, profile, oauth_token):
 
 
 
68
  if not (profile and oauth_token and repo_id):
69
- return "⚠️ Log in & create a Space first."
70
- return "\n".join(list_repo_files(repo_id, token=oauth_token.token, repo_type="space"))
 
 
 
 
 
 
 
 
71
 
72
- # β€” GEMINI FUNCTION DECLARATIONS β€” #
73
 
 
74
  func_decls = [
75
  {
76
- "name":"create_space","description":"Create/get a HF Space",
77
- "parameters":{
 
78
  "type":"object",
79
  "properties":{
80
  "repo_name":{"type":"string"},
@@ -84,7 +120,8 @@ func_decls = [
84
  }
85
  },
86
  {
87
- "name":"list_files","description":"List files in Space",
 
88
  "parameters":{
89
  "type":"object",
90
  "properties":{"repo_id":{"type":"string"}},
@@ -92,7 +129,8 @@ func_decls = [
92
  }
93
  },
94
  {
95
- "name":"get_build_logs","description":"Fetch build logs",
 
96
  "parameters":{
97
  "type":"object",
98
  "properties":{"repo_id":{"type":"string"}},
@@ -100,7 +138,8 @@ func_decls = [
100
  }
101
  },
102
  {
103
- "name":"get_run_logs","description":"Fetch run logs",
 
104
  "parameters":{
105
  "type":"object",
106
  "properties":{"repo_id":{"type":"string"}},
@@ -109,18 +148,22 @@ func_decls = [
109
  },
110
  ]
111
 
112
- # β€” CHAT HANDLER β€” #
113
-
114
- def process_message(profile, oauth_token, user_msg, gemini_key, repo_name, sdk, chat_history, session):
115
- # initialize on first call
 
116
  if session.get("chat") is None:
117
  client = genai.Client(api_key=gemini_key)
118
  cfg = types.GenerateContentConfig(
119
- system_instruction="You are an admin for HF Spaces: create spaces, list files, and fetch logs.",
 
 
 
120
  temperature=0,
121
  tools=[ types.Tool(function_declarations=func_decls) ]
122
  )
123
- session["chat"] = client.chats.create(model="gemini-2.0-flash", config=cfg)
124
  session["repo_id"] = None
125
 
126
  chat = session["chat"]
@@ -133,27 +176,35 @@ def process_message(profile, oauth_token, user_msg, gemini_key, repo_name, sdk,
133
  name = part.function_call.name
134
  args = json.loads(part.function_call.args)
135
  if name == "create_space":
136
- rid, log, iframe = create_space(args["repo_name"], args["sdk"], profile, oauth_token)
 
 
137
  session["repo_id"] = rid
138
  result = {"log": log, "iframe": iframe}
139
  elif name == "list_files":
140
- files = list_files(session["repo_id"], profile, oauth_token)
141
- result = {"files": files}
 
 
142
  elif name == "get_build_logs":
143
- log = fetch_logs(session["repo_id"], profile, oauth_token, "build")
144
- result = {"log": log}
 
145
  elif name == "get_run_logs":
146
- log = fetch_logs(session["repo_id"], profile, oauth_token, "run")
147
- result = {"log": log}
 
148
  else:
149
  result = {"log": f"⚠️ Unknown function {name}"}
150
 
151
- # feed back
152
  chat.send_message(
153
  types.Content(
154
  role="function",
155
- parts=[ types.Part(function_call=part.function_call,
156
- function_response=json.dumps(result)) ]
 
 
157
  )
158
  )
159
  final = chat.get_history()[-1].parts[0].text
@@ -166,108 +217,149 @@ def process_message(profile, oauth_token, user_msg, gemini_key, repo_name, sdk,
166
  if "files" in result: session["files"] = result["files"]
167
 
168
  chat_history[-1] = (user_msg, final)
169
- return chat_history, session.get("iframe",""), session.get("log",""), session.get("files",""), session
170
-
171
- # β€” SYNC MANUAL CHANGES β€” #
 
 
172
 
 
173
  def sync_manual(profile, oauth_token, session):
174
  if not (profile and oauth_token and session.get("repo_id")):
175
- return session.get("iframe",""), "⚠️ Cannot sync.", session.get("files",""), session
176
- files = list_files(session["repo_id"], profile, oauth_token)
177
- session["files"] = files
 
 
 
 
 
178
  session["log"] = "πŸ”„ Manual changes synced."
179
- return session.get("iframe",""), session["log"], session["files"], session
 
 
 
180
 
181
- # β€” BUILD THE UI β€” #
182
 
 
183
  with gr.Blocks(css="""
184
- #sidebar {background:#f7f7f7;padding:1rem;border-right:1px solid #ddd;}
185
- #main {padding:1rem;}
186
  """) as demo:
 
187
  with gr.Row():
188
- # Sidebar
189
  with gr.Column(elem_id="sidebar", scale=1):
190
- gr.Markdown("### πŸ”‘ Login & Config")
191
- hf_login = gr.LoginButton(variant="huggingface", size="sm")
192
- status = gr.Markdown("*Not logged in.*")
193
- models = gr.Markdown()
194
-
195
- hf_login.click(show_profile, inputs=[hf_login], outputs=[status])
196
- hf_login.click(list_private_models, inputs=[hf_login], outputs=[models])
197
-
198
- gemini_key = gr.Textbox(label="Gemini API Key", type="password")
199
- repo_name = gr.Textbox(label="Space name", placeholder="my-space")
200
- sdk_choice = gr.Radio(["gradio","streamlit"], value="gradio", label="SDK")
 
 
 
 
 
 
201
 
202
  gr.Markdown("---")
203
- sync_btn = gr.Button("πŸ”„ Confirm Manual Changes")
204
 
205
- # Main area
206
  with gr.Column(elem_id="main", scale=3):
207
  tabs = gr.Tabs()
208
-
209
  with tabs:
210
  with gr.TabItem("πŸ’¬ Chat"):
211
- chatbox = gr.Chatbot(type="messages", label="Chat")
212
- user_in = gr.Textbox(show_label=False, placeholder="Ask the LLM…")
213
- send = gr.Button("Send")
 
214
  with gr.TabItem("πŸ› οΈ Manual"):
215
  gr.Markdown("#### Create a Space")
216
  repo_m = gr.Textbox(label="Name")
217
- sdk_m = gr.Radio(["gradio","streamlit"], value="gradio", label="SDK")
218
- create_btn = gr.Button("Create Space", interactive=False)
219
- sess = gr.Textbox(visible=False)
220
- log_c = gr.Textbox(label="Log", interactive=False, lines=2)
221
- preview = gr.HTML("<p>No Space yet</p>")
222
-
223
- hf_login.change(enable_create, inputs=[hf_login], outputs=[create_btn])
 
 
 
 
 
224
  create_btn.click(create_space,
225
- inputs=[repo_m, sdk_m, hf_login, hf_login],
226
- outputs=[sess, log_c, preview])
 
 
227
 
228
  gr.Markdown("#### Upload File")
229
- path = gr.Textbox(label="Path in Repo", value="app.py")
230
- file_u = gr.File()
231
- up_btn = gr.Button("Upload File", interactive=False)
232
- log_u = gr.Textbox(label="Log", interactive=False, lines=2)
233
-
234
- hf_login.change(enable_repo_actions,
235
- inputs=[sess, hf_login, hf_login],
236
- outputs=[up_btn])
 
 
 
237
  up_btn.click(upload_file_to_space,
238
- inputs=[file_u, path, sess, hf_login, hf_login],
 
239
  outputs=[log_u])
240
 
241
  gr.Markdown("#### Fetch Logs")
242
- b_btn = gr.Button("Build Logs", interactive=False)
243
- r_btn = gr.Button("Run Logs", interactive=False)
244
- log_b = gr.Textbox(label="Build", interactive=False, lines=5)
245
- log_r = gr.Textbox(label="Run", interactive=False, lines=5)
246
-
247
- hf_login.change(enable_repo_actions,
248
- inputs=[sess, hf_login, hf_login],
249
- outputs=[b_btn, r_btn])
250
- b_btn.click(fetch_logs,
251
- inputs=[sess, hf_login, hf_login, gr.State("build")],
252
- outputs=[log_b])
253
- r_btn.click(fetch_logs,
254
- inputs=[sess, hf_login, hf_login, gr.State("run")],
255
- outputs=[log_r])
256
-
257
- # Persistent panels
 
 
 
 
 
 
258
  gr.Markdown("---")
259
  iframe_out = gr.HTML(label="πŸ–ΌοΈ Preview")
260
- log_out = gr.Textbox(label="πŸ“‹ Latest Log", lines=4)
 
261
  files_out = gr.Textbox(label="πŸ“š Files", lines=4)
262
 
263
  state = gr.State({})
264
- send.click(process_message,
265
- inputs=[hf_login, hf_login, user_in, gemini_key, repo_name, sdk_choice, chatbox, state],
266
- outputs=[chatbox, iframe_out, log_out, files_out, state])
267
-
268
- sync_btn.click(sync_manual,
269
- inputs=[hf_login, hf_login, state],
270
- outputs=[iframe_out, log_out, files_out, state])
 
 
 
 
 
271
 
272
  if __name__ == "__main__":
273
  demo.launch()
 
1
+ import os, json, requests
2
  import gradio as gr
3
  from huggingface_hub import (
4
+ create_repo, list_models, upload_file, 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
 
10
+ # β€” USER INFO & MODEL LISTING β€” (unchanged)
11
+ def show_profile(profile: gr.OAuthProfile | None) -> str:
12
+ if profile is None:
13
+ return "*Not logged in.*"
14
+ return f"βœ… Logged in as **{profile.username}**"
15
 
16
+ def list_private_models(profile: gr.OAuthProfile | None,
17
+ oauth_token: gr.OAuthToken | None) -> str:
18
+ if profile is None or oauth_token is None:
 
 
19
  return "Please log in to see your models."
20
  models = [
21
  f"{m.id} ({'private' if m.private else 'public'})"
 
23
  ]
24
  return "No models found." if not models else "Models:\n\n" + "\n - ".join(models)
25
 
26
+ # β€” BUTTON‑ENABLING HELPERS β€” (unchanged)
27
+ def enable_create(profile: gr.OAuthProfile | None,
28
+ oauth_token: gr.OAuthToken | None):
29
+ return gr.update(interactive=profile is not None)
30
 
31
+ def enable_repo_actions(repo_id: str,
32
+ profile: gr.OAuthProfile | None,
33
+ oauth_token: gr.OAuthToken | None):
34
  return gr.update(interactive=bool(repo_id and profile and oauth_token))
35
 
36
+ # β€” CORE ACTIONS β€” (unchanged)
37
+ def create_space(repo_name: str, sdk: str,
38
+ profile: gr.OAuthProfile | None,
39
+ oauth_token: gr.OAuthToken | None
40
+ ) -> tuple[str, str, str]:
41
+ if not profile or not oauth_token:
42
+ return "", "⚠️ Please log in first.", "<p>No Space created yet.</p>"
43
+ repo_id = f"{profile.username}/{repo_name}"
44
+ create_repo(
45
+ repo_id=repo_id,
46
+ token=oauth_token.token,
47
+ exist_ok=True,
48
+ repo_type="space",
49
+ space_sdk=sdk
50
+ )
51
+ url = f"https://huggingface.co/spaces/{repo_id}"
52
+ logmsg = f"βœ… Space ready: {url} (SDK: {sdk})"
53
+ iframe = f'<iframe src="{url}" width="100%" height="400px"></iframe>'
54
+ return repo_id, logmsg, iframe
55
+
56
+ def upload_file_to_space(file, path_in_repo: str, repo_id: str,
57
+ profile: gr.OAuthProfile | None,
58
+ oauth_token: gr.OAuthToken | None
59
+ ) -> str:
60
+ if not profile or not oauth_token:
61
  return "⚠️ Please log in first."
62
  if not repo_id:
63
+ return "⚠️ Please create a Space first."
64
  if not file:
65
  return "⚠️ No file selected."
66
+ upload_file(
67
+ path_or_fileobj=file.name,
68
+ path_in_repo=path_in_repo,
69
+ repo_id=repo_id,
70
+ token=oauth_token.token,
71
+ repo_type="space"
72
+ )
73
+ return f"βœ… Uploaded `{path_in_repo}` to `{repo_id}`"
74
+
75
+ def _fetch_space_logs_level(repo_id: str, level: str) -> str:
76
+ jwt_url = f"{constants.ENDPOINT}/api/spaces/{repo_id}/jwt"
77
+ r = get_session().get(jwt_url, headers=build_hf_headers())
78
  hf_raise_for_status(r)
79
  jwt = r.json()["token"]
80
+ logs_url = f"https://api.hf.space/v1/{repo_id}/logs/{level}"
81
  lines = []
82
+ with get_session().get(logs_url, headers=build_hf_headers(token=jwt), stream=True) as resp:
83
  hf_raise_for_status(resp)
84
  for raw in resp.iter_lines():
85
  if raw.startswith(b"data: "):
86
+ ev = json.loads(raw[len(b"data: "):].decode())
87
+ ts, txt = ev.get("timestamp", ""), ev.get("data", "")
88
+ lines.append(f"[{ts}] {txt}")
89
  return "\n".join(lines)
90
 
91
+ def get_build_logs(repo_id: str,
92
+ profile: gr.OAuthProfile | None,
93
+ oauth_token: gr.OAuthToken | None
94
+ ) -> str:
95
  if not (profile and oauth_token and repo_id):
96
+ return "⚠️ Please log in and create a Space first."
97
+ return _fetch_space_logs_level(repo_id, "build")
98
+
99
+ def get_container_logs(repo_id: str,
100
+ profile: gr.OAuthProfile | None,
101
+ oauth_token: gr.OAuthToken | None
102
+ ) -> str:
103
+ if not (profile and oauth_token and repo_id):
104
+ return "⚠️ Please log in and create a Space first."
105
+ return _fetch_space_logs_level(repo_id, "run")
106
 
 
107
 
108
+ # β€” GEMINI FUNCTION DECLARATIONS β€”
109
  func_decls = [
110
  {
111
+ "name": "create_space",
112
+ "description": "Create or get a Hugging Face Space",
113
+ "parameters": {
114
  "type":"object",
115
  "properties":{
116
  "repo_name":{"type":"string"},
 
120
  }
121
  },
122
  {
123
+ "name":"list_files",
124
+ "description":"List files in the Space",
125
  "parameters":{
126
  "type":"object",
127
  "properties":{"repo_id":{"type":"string"}},
 
129
  }
130
  },
131
  {
132
+ "name":"get_build_logs",
133
+ "description":"Fetch the build logs",
134
  "parameters":{
135
  "type":"object",
136
  "properties":{"repo_id":{"type":"string"}},
 
138
  }
139
  },
140
  {
141
+ "name":"get_run_logs",
142
+ "description":"Fetch the run logs",
143
  "parameters":{
144
  "type":"object",
145
  "properties":{"repo_id":{"type":"string"}},
 
148
  },
149
  ]
150
 
151
+ # β€” CHAT HANDLER β€”
152
+ def process_message(profile, oauth_token, user_msg,
153
+ gemini_key, repo_name, sdk,
154
+ chat_history, session):
155
+ # initialize chat on first call
156
  if session.get("chat") is None:
157
  client = genai.Client(api_key=gemini_key)
158
  cfg = types.GenerateContentConfig(
159
+ system_instruction=(
160
+ "You are a HF Spaces admin. "
161
+ "Use functions to create spaces, list files, or fetch logs."
162
+ ),
163
  temperature=0,
164
  tools=[ types.Tool(function_declarations=func_decls) ]
165
  )
166
+ session["chat"] = client.chats.create(model="gemini-2.0-flash", config=cfg)
167
  session["repo_id"] = None
168
 
169
  chat = session["chat"]
 
176
  name = part.function_call.name
177
  args = json.loads(part.function_call.args)
178
  if name == "create_space":
179
+ rid, log, iframe = create_space(
180
+ args["repo_name"], args["sdk"], profile, oauth_token
181
+ )
182
  session["repo_id"] = rid
183
  result = {"log": log, "iframe": iframe}
184
  elif name == "list_files":
185
+ files = list_repo_files(
186
+ session["repo_id"], token=oauth_token.token, repo_type="space"
187
+ )
188
+ result = {"files": "\n".join(files)}
189
  elif name == "get_build_logs":
190
+ result = {
191
+ "log": get_build_logs(session["repo_id"], profile, oauth_token)
192
+ }
193
  elif name == "get_run_logs":
194
+ result = {
195
+ "log": get_container_logs(session["repo_id"], profile, oauth_token)
196
+ }
197
  else:
198
  result = {"log": f"⚠️ Unknown function {name}"}
199
 
200
+ # feed function result back into the chat
201
  chat.send_message(
202
  types.Content(
203
  role="function",
204
+ parts=[ types.Part(
205
+ function_call=part.function_call,
206
+ function_response=json.dumps(result)
207
+ )]
208
  )
209
  )
210
  final = chat.get_history()[-1].parts[0].text
 
217
  if "files" in result: session["files"] = result["files"]
218
 
219
  chat_history[-1] = (user_msg, final)
220
+ return (chat_history,
221
+ session.get("iframe",""),
222
+ session.get("log",""),
223
+ session.get("files",""),
224
+ session)
225
 
226
+ # β€” SYNC MANUAL CHANGES β€”
227
  def sync_manual(profile, oauth_token, session):
228
  if not (profile and oauth_token and session.get("repo_id")):
229
+ return (session.get("iframe",""),
230
+ "⚠️ Cannot sync manual changes.",
231
+ session.get("files",""),
232
+ session)
233
+ files = list_repo_files(
234
+ session["repo_id"], token=oauth_token.token, repo_type="space"
235
+ )
236
+ session["files"] = "\n".join(files)
237
  session["log"] = "πŸ”„ Manual changes synced."
238
+ return (session.get("iframe",""),
239
+ session["log"],
240
+ session["files"],
241
+ session)
242
 
 
243
 
244
+ # β€” BUILD THE UI β€”
245
  with gr.Blocks(css="""
246
+ #sidebar { background:#fafafa; padding:1em; border-right:1px solid #ddd; }
247
+ #main { padding:1em; }
248
  """) as demo:
249
+
250
  with gr.Row():
251
+ # β—€ Sidebar
252
  with gr.Column(elem_id="sidebar", scale=1):
253
+ gr.Markdown("### πŸ”‘ HF Login & Config")
254
+ hf_login = gr.LoginButton(variant="huggingface", size="sm")
255
+ status_md = gr.Markdown("*Not logged in.*")
256
+ models_md = gr.Markdown()
257
+
258
+ # login callbacks
259
+ hf_login.click(show_profile,
260
+ inputs=[hf_login], outputs=[status_md])
261
+ hf_login.click(list_private_models,
262
+ inputs=[hf_login, hf_login], outputs=[models_md])
263
+
264
+ gemini_key = gr.Textbox(label="Gemini API Key",
265
+ type="password")
266
+ sidebar_repo = gr.Textbox(label="Space name",
267
+ placeholder="e.g. my-space")
268
+ sidebar_sdk = gr.Radio(["gradio","streamlit"],
269
+ value="gradio", label="SDK")
270
 
271
  gr.Markdown("---")
272
+ confirm_btn = gr.Button("πŸ”„ Confirm Manual Changes")
273
 
274
+ # β–Ά Main area: Chat & Manual Controls
275
  with gr.Column(elem_id="main", scale=3):
276
  tabs = gr.Tabs()
 
277
  with tabs:
278
  with gr.TabItem("πŸ’¬ Chat"):
279
+ chatbox = gr.Chatbot(type="messages")
280
+ user_input= gr.Textbox(show_label=False,
281
+ placeholder="Ask the LLM…")
282
+ send_btn = gr.Button("Send")
283
  with gr.TabItem("πŸ› οΈ Manual"):
284
  gr.Markdown("#### Create a Space")
285
  repo_m = gr.Textbox(label="Name")
286
+ sdk_m = gr.Radio(["gradio","streamlit"],
287
+ value="gradio", label="SDK")
288
+ create_btn = gr.Button("Create Space",
289
+ interactive=False)
290
+ sess_id = gr.Textbox(visible=False)
291
+ log_c = gr.Textbox(label="Log",
292
+ interactive=False, lines=2)
293
+ iframe_m = gr.HTML("<p>No Space yet.</p>")
294
+
295
+ hf_login.click(enable_create,
296
+ inputs=[hf_login, hf_login],
297
+ outputs=[create_btn])
298
  create_btn.click(create_space,
299
+ inputs=[repo_m, sdk_m,
300
+ hf_login, hf_login],
301
+ outputs=[sess_id, log_c,
302
+ iframe_m])
303
 
304
  gr.Markdown("#### Upload File")
305
+ path = gr.Textbox(label="Path in Repo",
306
+ value="app.py")
307
+ file_u = gr.File()
308
+ up_btn = gr.Button("Upload File",
309
+ interactive=False)
310
+ log_u = gr.Textbox(label="Log",
311
+ interactive=False, lines=2)
312
+
313
+ hf_login.click(enable_repo_actions,
314
+ inputs=[sess_id, hf_login, hf_login],
315
+ outputs=[up_btn])
316
  up_btn.click(upload_file_to_space,
317
+ inputs=[file_u, path, sess_id,
318
+ hf_login, hf_login],
319
  outputs=[log_u])
320
 
321
  gr.Markdown("#### Fetch Logs")
322
+ build_btn = gr.Button("Build Logs",
323
+ interactive=False)
324
+ run_btn = gr.Button("Run Logs",
325
+ interactive=False)
326
+ log_b = gr.Textbox(label="Build",
327
+ interactive=False, lines=5)
328
+ log_r = gr.Textbox(label="Run",
329
+ interactive=False, lines=5)
330
+
331
+ hf_login.click(enable_repo_actions,
332
+ inputs=[sess_id, hf_login, hf_login],
333
+ outputs=[build_btn, run_btn])
334
+ build_btn.click(get_build_logs,
335
+ inputs=[sess_id,
336
+ hf_login, hf_login],
337
+ outputs=[log_b])
338
+ run_btn.click(get_container_logs,
339
+ inputs=[sess_id,
340
+ hf_login, hf_login],
341
+ outputs=[log_r])
342
+
343
+ # persistent bottom panels
344
  gr.Markdown("---")
345
  iframe_out = gr.HTML(label="πŸ–ΌοΈ Preview")
346
+ log_out = gr.Textbox(label="πŸ“‹ Latest Log",
347
+ lines=4)
348
  files_out = gr.Textbox(label="πŸ“š Files", lines=4)
349
 
350
  state = gr.State({})
351
+ send_btn.click(process_message,
352
+ inputs=[hf_login, hf_login,
353
+ user_input, gemini_key,
354
+ sidebar_repo, sidebar_sdk,
355
+ chatbox, state],
356
+ outputs=[chatbox, iframe_out,
357
+ log_out, files_out, state])
358
+
359
+ confirm_btn.click(sync_manual,
360
+ inputs=[hf_login, hf_login, state],
361
+ outputs=[iframe_out, log_out,
362
+ files_out, state])
363
 
364
  if __name__ == "__main__":
365
  demo.launch()