wuhp commited on
Commit
4eff17c
·
verified ·
1 Parent(s): 7e0fac0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +172 -243
app.py CHANGED
@@ -1,75 +1,79 @@
1
- import re
2
- import json
3
- import os
4
-
5
  import gradio as gr
6
- from PIL import Image, ImageFilter
 
7
  from huggingface_hub import (
8
- create_repo, list_models, upload_file, list_repo_files, constants
 
 
 
9
  )
10
  from huggingface_hub.utils import build_hf_headers, get_session, hf_raise_for_status
11
 
12
- from google import genai
13
- from google.genai import types
14
-
15
  # — USER INFO & MODEL LISTING —
16
 
17
- def show_profile(profile):
18
- return f"✅ Logged in as **{profile.username}**" if profile else "*Not logged in.*"
 
 
19
 
20
- def list_private_models(profile, token):
21
- if not (profile and token):
 
 
 
22
  return "Please log in to see your models."
23
  models = [
24
  f"{m.id} ({'private' if m.private else 'public'})"
25
- for m in list_models(author=profile.username, token=token.token)
26
  ]
27
  return "No models found." if not models else "Models:\n\n" + "\n - ".join(models)
28
 
29
  # — BUTTON‑ENABLING HELPERS —
30
 
31
- def enable_create(profile, token):
32
- return gr.update(interactive=bool(profile and token))
 
 
 
33
 
34
- def enable_repo_actions(repo_id, profile, token):
35
- return gr.update(interactive=bool(repo_id and profile and token))
 
 
 
 
36
 
37
  # — CORE ACTIONS —
38
 
39
- def create_space(repo_name, sdk, profile, token):
40
- if not (profile and token):
 
 
 
 
 
41
  return "", "⚠️ Please log in first.", "<p>No Space created yet.</p>"
42
- rid = f"{profile.username}/{repo_name.replace(' ', '-') }"
43
  create_repo(
44
- repo_id=rid,
45
- token=token.token,
46
  exist_ok=True,
47
  repo_type="space",
48
  space_sdk=sdk
49
  )
50
- url = f"https://huggingface.co/spaces/{rid}"
51
- log = f"✅ Space ready: {url} (SDK: {sdk})"
52
- iframe = f'<iframe src="{url}" width="100%" height="400px"></iframe>'
53
- return rid, log, iframe
54
-
55
- def write_file(path, content, repo_id, profile, token):
56
- if not (profile and token):
57
- return "⚠️ Please log in first."
58
- if not repo_id:
59
- return "⚠️ Please create a Space first."
60
- with open(path, "w") as f:
61
- f.write(content)
62
- upload_file(
63
- path_or_fileobj=path,
64
- path_in_repo=path,
65
- repo_id=repo_id,
66
- token=token.token,
67
- repo_type="space"
68
- )
69
- return f"✅ Wrote and uploaded `{path}`"
70
-
71
- def upload_file_to_space(file, path, repo_id, profile, token):
72
- if not (profile and token):
73
  return "⚠️ Please log in first."
74
  if not repo_id:
75
  return "⚠️ Please create a Space first."
@@ -77,223 +81,148 @@ def upload_file_to_space(file, path, repo_id, profile, token):
77
  return "⚠️ No file selected."
78
  upload_file(
79
  path_or_fileobj=file.name,
80
- path_in_repo=path,
81
  repo_id=repo_id,
82
- token=token.token,
83
  repo_type="space"
84
  )
85
- return f"✅ Uploaded `{path}` to `{repo_id}`"
86
 
87
- def _fetch_space_logs_level(repo_id, level):
88
- r = get_session().get(
89
- f"{constants.ENDPOINT}/api/spaces/{repo_id}/jwt",
90
- headers=build_hf_headers()
91
- )
92
  hf_raise_for_status(r)
93
  jwt = r.json()["token"]
94
- url = f"https://api.hf.space/v1/{repo_id}/logs/{level}"
 
95
  lines = []
96
- with get_session().get(url, headers=build_hf_headers(token=jwt), stream=True) as resp:
97
  hf_raise_for_status(resp)
98
  for raw in resp.iter_lines():
99
- if raw.startswith(b"data: "):
100
- ev = json.loads(raw[6:].decode())
101
- lines.append(f"[{ev.get('timestamp','')}] {ev.get('data','')}")
 
 
 
 
 
 
 
102
  return "\n".join(lines)
103
 
104
- def get_build_logs(repo_id, profile, token):
105
- if not (profile and token and repo_id):
 
 
 
 
106
  return "⚠️ Please log in and create a Space first."
107
  return _fetch_space_logs_level(repo_id, "build")
108
 
109
- def get_container_logs(repo_id, profile, token):
110
- if not (profile and token and repo_id):
 
 
 
 
111
  return "⚠️ Please log in and create a Space first."
112
  return _fetch_space_logs_level(repo_id, "run")
113
 
114
- # — FUNCTION DECLARATIONS & TOOL
115
-
116
- func_decls = [
117
- types.FunctionDeclaration(
118
- name="create_space",
119
- description="Create/get a HF Space",
120
- parameters={
121
- "type": "object",
122
- "properties": {
123
- "repo_name": {"type": "string"},
124
- "sdk": {"type": "string", "enum": ["gradio", "streamlit"]}
125
- },
126
- "required": ["repo_name", "sdk"]
127
- }
128
- ),
129
- types.FunctionDeclaration(
130
- name="write_file",
131
- description="Write/overwrite a file in the Space",
132
- parameters={
133
- "type": "object",
134
- "properties": {
135
- "path": {"type": "string"},
136
- "content": {"type": "string"}
137
- },
138
- "required": ["path", "content"]
139
- }
140
- ),
141
- types.FunctionDeclaration(
142
- name="list_files",
143
- description="List files in the Space",
144
- parameters={
145
- "type": "object",
146
- "properties": {"repo_id": {"type": "string"}},
147
- "required": ["repo_id"]
148
- }
149
- ),
150
- types.FunctionDeclaration(
151
- name="get_build_logs",
152
- description="Fetch build logs",
153
- parameters={
154
- "type": "object",
155
- "properties": {"repo_id": {"type": "string"}},
156
- "required": ["repo_id"]
157
- }
158
- ),
159
- types.FunctionDeclaration(
160
- name="get_run_logs",
161
- description="Fetch container logs",
162
- parameters={
163
- "type": "object",
164
- "properties": {"repo_id": {"type": "string"}},
165
- "required": ["repo_id"]
166
- }
167
- ),
168
- ]
169
- tool = types.Tool(function_declarations=func_decls)
170
-
171
- # — CHAT HANDLER —
172
-
173
- def execute_function_by_name(name, args, profile, token, session):
174
- if name == "create_space":
175
- rid, log, iframe = create_space(
176
- args["repo_name"], session["sdk"], profile, token
177
- )
178
- session["repo_id"] = rid
179
- session["iframe"] = iframe
180
- session["log"] = log
181
- return {"log": log, "iframe": iframe}
182
- if name == "write_file":
183
- status = write_file(
184
- args["path"], args["content"],
185
- session["repo_id"], profile, token
186
- )
187
- return {"status": status}
188
- if name == "list_files":
189
- fl = list_repo_files(
190
- session["repo_id"], token=token.token, repo_type="space"
191
- )
192
- return {"files": "\n".join(fl)}
193
- if name == "get_build_logs":
194
- return {"log": get_build_logs(
195
- session["repo_id"], profile, token
196
- )}
197
- if name == "get_run_logs":
198
- return {"log": get_container_logs(
199
- session["repo_id"], profile, token
200
- )}
201
- return {"error": f"Unknown function {name}"}
202
 
 
 
 
 
 
 
 
 
 
203
 
204
- def process_message(
205
- profile, token, user_msg,
206
- gemini_key, sidebar_repo, sidebar_sdk,
207
- chat_history, session
208
- ):
209
- if session.get("chat") is None:
210
- client = genai.Client(api_key=gemini_key)
211
- model = client.chats.create(
212
- model="gemini-2.5-flash-preview-04-17",
213
- config=types.GenerateContentConfig(
214
- system_instruction=(
215
- "You are a HF Spaces admin. You can create spaces, "
216
- "write files, list files, and fetch logs."
217
- ),
218
- temperature=0,
219
- tools=[tool],
220
- ),
221
- )
222
- session.update({
223
- "chat": model,
224
- "repo_id": None,
225
- "sdk": sidebar_sdk,
226
- "messages": []
227
- })
228
- session["sdk"] = sidebar_sdk
229
- if session["repo_id"] is None:
230
- m = re.search(r"(?:call it|called)\s+([\w \-]+)", user_msg, re.IGNORECASE)
231
- if m:
232
- args = {"repo_name": m.group(1), "sdk": sidebar_sdk}
233
- result = execute_function_by_name("create_space", args, profile, token, session)
234
- session["messages"].append({"role": "assistant", "content": result["log"]})
235
- return session["messages"], session["iframe"], session["log"], "", session
236
- session["messages"].append({"role": "user", "content": user_msg})
237
- response = session["chat"].send_message(user_msg)
238
- while True:
239
- part = response.candidates[0].content.parts[0]
240
- if not part.function_call:
241
- break
242
- fname = part.function_call.name
243
- fargs = part.function_call.args or {}
244
- result = execute_function_by_name(fname, fargs, profile, token, session)
245
- resp_part = types.Part.from_function_response(name=fname, response=result)
246
- response = session["chat"].send_message(resp_part)
247
- assistant_text = response.text
248
- session["messages"].append({"role": "assistant", "content": assistant_text})
249
- return (
250
- session["messages"],
251
- session.get("iframe", ""),
252
- session.get("log", ""),
253
- session.get("files", ""),
254
- session
255
  )
256
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
257
 
258
- def sync_manual(profile, token, session):
259
- if not (profile and token and session.get("repo_id")):
260
- return "", "⚠️ Cannot sync manual changes.", "", session
261
- files = list_repo_files(session["repo_id"], token=token.token, repo_type="space")
262
- log = "🔄 Manual changes synced."
263
- return session.get("iframe", ""), log, "\n".join(files), session
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
264
 
265
- with gr.Blocks(css="""
266
- #sidebar { background:#f2f2f2; padding:1rem; border-right:1px solid #ccc; }
267
- #main { padding:1rem; }
268
- """) as demo:
269
- with gr.Column(elem_id="sidebar", scale=1):
270
- gr.Markdown("### 🔑 HF Login & Config")
271
- login_btn = gr.LoginButton(variant="huggingface", size="sm")
272
- profile_state = gr.State(None)
273
- token_state = gr.State(None)
274
- login_btn.click(None, [], [profile_state, token_state])
275
- status_md = gr.Markdown("*Not logged in.*")
276
- profile_state.change(show_profile, [profile_state], [status_md])
277
- models_md = gr.Markdown()
278
- profile_state.change(list_private_models,[profile_state, token_state],[models_md])
279
- gemini_key = gr.Textbox(label="Gemini API Key", type="password")
280
- sidebar_repo = gr.Textbox(label="Space name", placeholder="blurrtest")
281
- sidebar_sdk = gr.Radio(["gradio","streamlit"],value="gradio",label="SDK")
282
- gr.Markdown("---")
283
- confirm_btn = gr.Button("🔄 Sync Manual Changes")
284
- with gr.Column(elem_id="main", scale=3):
285
- tabs = gr.Tabs()
286
- with tabs:
287
- with gr.TabItem("💬 Chat"):
288
- chatbox = gr.Chatbot(type="messages")
289
- user_input = gr.Textbox(show_label=False, placeholder="Generate code…")
290
- send_btn = gr.Button("Send")
291
- with gr.TabItem("🛠️ Manual"):
292
- gr.Markdown("#### Create / Rename Space")
293
- repo_m = gr.Textbox(label="Name", value="")
294
- sdk_m = gr.Radio(["gradio","streamlit"],value="gradio",label="SDK")
295
- create_btn = gr.Button("Create Space")
296
- sess_id = gr.Textbox(visible=False)
297
- log_c = gr.Textbox(label="Log",interactive=False,lines=2)
298
- preview = gr.HTML("<p>No Space yet.</p>")
299
- create_btn.click(create_space,[repo_m,sdk_m,profile_state,token
 
 
 
 
 
1
  import gradio as gr
2
+ import requests
3
+ import json
4
  from huggingface_hub import (
5
+ create_repo,
6
+ list_models,
7
+ upload_file,
8
+ constants,
9
  )
10
  from huggingface_hub.utils import build_hf_headers, get_session, hf_raise_for_status
11
 
 
 
 
12
  # — USER INFO & MODEL LISTING —
13
 
14
+ def show_profile(profile: gr.OAuthProfile | None) -> str:
15
+ if profile is None:
16
+ return "*Not logged in.*"
17
+ return f"✅ Logged in as **{profile.username}**"
18
 
19
+ def list_private_models(
20
+ profile: gr.OAuthProfile | None,
21
+ oauth_token: gr.OAuthToken | None
22
+ ) -> str:
23
+ if profile is None or oauth_token is None:
24
  return "Please log in to see your models."
25
  models = [
26
  f"{m.id} ({'private' if m.private else 'public'})"
27
+ for m in list_models(author=profile.username, token=oauth_token.token)
28
  ]
29
  return "No models found." if not models else "Models:\n\n" + "\n - ".join(models)
30
 
31
  # — BUTTON‑ENABLING HELPERS —
32
 
33
+ def enable_create(
34
+ profile: gr.OAuthProfile | None,
35
+ oauth_token: gr.OAuthToken | None
36
+ ):
37
+ return gr.update(interactive=profile is not None)
38
 
39
+ def enable_repo_actions(
40
+ repo_id: str,
41
+ profile: gr.OAuthProfile | None,
42
+ oauth_token: gr.OAuthToken | None
43
+ ):
44
+ return gr.update(interactive=bool(repo_id and profile and oauth_token))
45
 
46
  # — CORE ACTIONS —
47
 
48
+ def create_space(
49
+ repo_name: str,
50
+ sdk: str,
51
+ profile: gr.OAuthProfile | None,
52
+ oauth_token: gr.OAuthToken | None
53
+ ) -> tuple[str, str, str]:
54
+ if not profile or not oauth_token:
55
  return "", "⚠️ Please log in first.", "<p>No Space created yet.</p>"
56
+ repo_id = f"{profile.username}/{repo_name}"
57
  create_repo(
58
+ repo_id=repo_id,
59
+ token=oauth_token.token,
60
  exist_ok=True,
61
  repo_type="space",
62
  space_sdk=sdk
63
  )
64
+ url = f"https://huggingface.co/spaces/{repo_id}"
65
+ logmsg = f"✅ Space ready: {url} (SDK: {sdk})"
66
+ iframe = f'<iframe src="{url}" width="100%" height="500px"></iframe>'
67
+ return repo_id, logmsg, iframe
68
+
69
+ def upload_file_to_space(
70
+ file,
71
+ path_in_repo: str,
72
+ repo_id: str,
73
+ profile: gr.OAuthProfile | None,
74
+ oauth_token: gr.OAuthToken | None
75
+ ) -> str:
76
+ if not profile or not oauth_token:
 
 
 
 
 
 
 
 
 
 
77
  return "⚠️ Please log in first."
78
  if not repo_id:
79
  return "⚠️ Please create a Space first."
 
81
  return "⚠️ No file selected."
82
  upload_file(
83
  path_or_fileobj=file.name,
84
+ path_in_repo=path_in_repo,
85
  repo_id=repo_id,
86
+ token=oauth_token.token,
87
  repo_type="space"
88
  )
89
+ return f"✅ Uploaded `{path_in_repo}` to `{repo_id}`"
90
 
91
+ def _fetch_space_logs_level(repo_id: str, level: str) -> str:
92
+ # 1) Get SSE JWT
93
+ jwt_url = f"{constants.ENDPOINT}/api/spaces/{repo_id}/jwt"
94
+ r = get_session().get(jwt_url, headers=build_hf_headers())
 
95
  hf_raise_for_status(r)
96
  jwt = r.json()["token"]
97
+ # 2) Stream logs
98
+ logs_url = f"https://api.hf.space/v1/{repo_id}/logs/{level}"
99
  lines = []
100
+ with get_session().get(logs_url, headers=build_hf_headers(token=jwt), stream=True) as resp:
101
  hf_raise_for_status(resp)
102
  for raw in resp.iter_lines():
103
+ if not raw.startswith(b"data: "):
104
+ continue
105
+ payload = raw[len(b"data: "):]
106
+ try:
107
+ event = json.loads(payload.decode())
108
+ except json.JSONDecodeError:
109
+ continue
110
+ ts = event.get("timestamp", "")
111
+ txt = event.get("data", "")
112
+ lines.append(f"[{ts}] {txt}")
113
  return "\n".join(lines)
114
 
115
+ def get_build_logs(
116
+ repo_id: str,
117
+ profile: gr.OAuthProfile | None,
118
+ oauth_token: gr.OAuthToken | None
119
+ ) -> str:
120
+ if not (profile and oauth_token and repo_id):
121
  return "⚠️ Please log in and create a Space first."
122
  return _fetch_space_logs_level(repo_id, "build")
123
 
124
+ def get_container_logs(
125
+ repo_id: str,
126
+ profile: gr.OAuthProfile | None,
127
+ oauth_token: gr.OAuthToken | None
128
+ ) -> str:
129
+ if not (profile and oauth_token and repo_id):
130
  return "⚠️ Please log in and create a Space first."
131
  return _fetch_space_logs_level(repo_id, "run")
132
 
133
+ # — BUILD THE UI
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
 
135
+ with gr.Blocks(title="HF OAuth + Space Manager with Logs") as demo:
136
+ gr.Markdown(
137
+ "## Sign in with Hugging Face + Manage Your Space\n\n"
138
+ "1. Sign in\n"
139
+ "2. Create a Space (Gradio/Streamlit)\n"
140
+ "3. Upload files to it\n"
141
+ "4. Fetch build and container logs\n\n"
142
+ "---"
143
+ )
144
 
145
+ # — LOGIN & MODEL LIST —
146
+ login_btn = gr.LoginButton(variant="huggingface", size="lg")
147
+ status_md = gr.Markdown("*Not logged in.*")
148
+ models_md = gr.Markdown()
149
+ demo.load(show_profile, inputs=None, outputs=status_md)
150
+ login_btn.click(show_profile, inputs=None, outputs=status_md)
151
+ demo.load(list_private_models, inputs=None, outputs=models_md)
152
+ login_btn.click(list_private_models,
153
+ inputs=None, outputs=models_md)
154
+
155
+ # — CREATE SPACE —
156
+ repo_name = gr.Textbox(label="New Space name", placeholder="my-space")
157
+ sdk_selector = gr.Radio(
158
+ choices=["gradio","streamlit"],
159
+ value="gradio",
160
+ label="Space template (SDK)"
161
+ )
162
+ create_btn = gr.Button("Create Space", interactive=False)
163
+ session_id = gr.Textbox(visible=False)
164
+ create_logs = gr.Textbox(label="Create Logs", interactive=False, lines=3)
165
+ preview = gr.HTML("<p>No Space created yet.</p>")
166
+
167
+ demo.load(enable_create, inputs=None, outputs=[create_btn])
168
+ login_btn.click(enable_create, inputs=None, outputs=[create_btn])
169
+
170
+ create_btn.click(
171
+ fn=create_space,
172
+ inputs=[repo_name, sdk_selector],
173
+ outputs=[session_id, create_logs, preview]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
  )
175
 
176
+ # — UPLOAD FILES —
177
+ path_in_repo = gr.Textbox(label="Path in Space", value="app.py")
178
+ file_uploader = gr.File(label="Select file")
179
+ upload_btn = gr.Button("Upload File", interactive=False)
180
+ upload_logs = gr.Textbox(label="Upload Logs", interactive=False, lines=2)
181
+
182
+ demo.load(enable_repo_actions,
183
+ inputs=[session_id],
184
+ outputs=[upload_btn])
185
+ login_btn.click(enable_repo_actions,
186
+ inputs=[session_id],
187
+ outputs=[upload_btn])
188
+ session_id.change(enable_repo_actions,
189
+ inputs=[session_id],
190
+ outputs=[upload_btn])
191
+
192
+ upload_btn.click(
193
+ fn=upload_file_to_space,
194
+ inputs=[file_uploader, path_in_repo, session_id],
195
+ outputs=[upload_logs]
196
+ )
197
 
198
+ # FETCH BUILD & CONTAINER LOGS —
199
+ build_logs_btn = gr.Button("Get Build Logs", interactive=False)
200
+ container_logs_btn = gr.Button("Get Container Logs", interactive=False)
201
+ build_logs_md = gr.Textbox(label="Build Logs", interactive=False, lines=10)
202
+ container_logs_md = gr.Textbox(label="Container Logs", interactive=False, lines=10)
203
+
204
+ # enable both log buttons
205
+ for btn in (build_logs_btn, container_logs_btn):
206
+ demo.load(enable_repo_actions,
207
+ inputs=[session_id],
208
+ outputs=[btn])
209
+ login_btn.click(enable_repo_actions,
210
+ inputs=[session_id],
211
+ outputs=[btn])
212
+ session_id.change(enable_repo_actions,
213
+ inputs=[session_id],
214
+ outputs=[btn])
215
+
216
+ build_logs_btn.click(
217
+ fn=get_build_logs,
218
+ inputs=[session_id],
219
+ outputs=[build_logs_md]
220
+ )
221
+ container_logs_btn.click(
222
+ fn=get_container_logs,
223
+ inputs=[session_id],
224
+ outputs=[container_logs_md]
225
+ )
226
 
227
+ if __name__ == "__main__":
228
+ demo.launch()