wuhp commited on
Commit
f2231db
·
verified ·
1 Parent(s): 5716b08

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +142 -207
app.py CHANGED
@@ -1,228 +1,163 @@
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."
80
- if not file:
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()
 
1
+ import os
 
2
  import json
3
+ from huggingface_hub import create_repo, list_models, upload_file, constants
 
 
 
 
 
4
  from huggingface_hub.utils import build_hf_headers, get_session, hf_raise_for_status
5
+ import gradio as gr
6
+ from google import genai
7
+ from google.genai import types
8
+
9
+ # --- Initialize Gemini client ---
10
+ client = genai.Client(api_key=os.getenv("GEMINI_API_KEY"))
11
+ # System prompt for Gemini
12
+ system_instruction = (
13
+ "You are a helpful assistant that writes, debugs, and pushes code to Hugging Face Spaces. "
14
+ "Treat Hugging Face as a sandbox: create spaces, upload code, and debug via function calls. "
15
+ "Respond in JSON with {success, data, message}."
16
+ )
17
 
18
+ # --- Function declarations for logs (behind the scenes) ---
19
+ get_build_logs_decl = {
20
+ "name": "get_build_logs",
21
+ "description": "Fetches build logs for a Space",
22
+ "parameters": {
23
+ "type": "object",
24
+ "properties": {
25
+ "repo_id": {"type": "string"}
26
+ },
27
+ "required": ["repo_id"]
28
+ }
29
+ }
30
+ get_container_logs_decl = {
31
+ "name": "get_container_logs",
32
+ "description": "Fetches container logs for a Space",
33
+ "parameters": {
34
+ "type": "object",
35
+ "properties": {
36
+ "repo_id": {"type": "string"}
37
+ },
38
+ "required": ["repo_id"]
39
+ }
40
+ }
41
+ tools = [
42
+ types.Tool(function_declarations=[get_build_logs_decl, get_container_logs_decl])
43
+ ]
44
+
45
+ # --- Core Hugging Face functions (unchanged) ---
46
+ def _fetch_space_logs_level(repo_id: str, level: str):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  jwt_url = f"{constants.ENDPOINT}/api/spaces/{repo_id}/jwt"
48
  r = get_session().get(jwt_url, headers=build_hf_headers())
49
  hf_raise_for_status(r)
50
  jwt = r.json()["token"]
 
51
  logs_url = f"https://api.hf.space/v1/{repo_id}/logs/{level}"
52
+ records = []
53
  with get_session().get(logs_url, headers=build_hf_headers(token=jwt), stream=True) as resp:
54
  hf_raise_for_status(resp)
55
  for raw in resp.iter_lines():
56
  if not raw.startswith(b"data: "):
57
  continue
 
58
  try:
59
+ event = json.loads(raw[len(b"data: "):].decode())
60
+ records.append({
61
+ "timestamp": event.get("timestamp"),
62
+ "message": event.get("data")
63
+ })
64
+ except:
65
+ pass
66
+ return records
67
+
68
+ # Space management (hidden behind scenes)
69
+ def create_space_backend(username, token, repo_name, sdk):
70
+ repo_id = f"{username}/{repo_name}"
71
+ create_repo(
72
+ repo_id=repo_id,
73
+ token=token,
74
+ exist_ok=True,
75
+ repo_type="space",
76
+ space_sdk=sdk
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  )
78
+ return repo_id
79
 
80
+ def upload_file_backend(repo_id, token, file_path, path_in_repo):
81
+ upload_file(
82
+ path_or_fileobj=file_path,
83
+ path_in_repo=path_in_repo,
84
+ repo_id=repo_id,
85
+ token=token,
86
+ repo_type="space"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  )
88
+ return True
89
+
90
+ # --- Chat and interaction ---
91
+ chat = None
92
+
93
+ def init_chat(repo_id, api_key):
94
+ global chat
95
+ os.environ["GEMINI_API_KEY"] = api_key
96
+ chat = client.chats.create(
97
+ model="gemini-2.5-flash-preview-04-17",
98
+ config=types.GenerateContentConfig(
99
+ system_instruction=system_instruction,
100
+ tools=tools,
101
+ temperature=0
102
+ )
103
  )
104
+ return {"success": True, "data": None, "message": f"Sandbox ready: {repo_id}"}
105
+
106
+ def chatbot_respond(message, history, repo_id, api_key):
107
+ global chat
108
+ if chat is None:
109
+ init_chat(repo_id, api_key)
110
+ response = chat.send_message(message)
111
+ part = response.candidates[0].content.parts[0]
112
+ # Handle function calls
113
+ if part.function_call:
114
+ fn = part.function_call
115
+ args = json.loads(fn.args)
116
+ if fn.name == "get_build_logs":
117
+ result = _fetch_space_logs_level(repo_id, "build")
118
+ else:
119
+ result = _fetch_space_logs_level(repo_id, "run")
120
+ response2 = chat.send_message("", function_response={fn.name: result})
121
+ reply = response2.candidates[0].content.parts[0].text
122
+ else:
123
+ reply = part.text
124
+ history.append((message, reply))
125
+ return history
126
+
127
+ # --- UI: Chatbot with Sidebar for Space Creation ---
128
+ with gr.Blocks(title="HF Code Sandbox Chat") as demo:
129
+ # Top login button
130
+ with gr.Row():
131
+ login_btn = gr.LoginButton(variant="huggingface", label="Sign in with HF")
132
+ status = gr.Markdown("*Not signed in.*")
133
+ login_btn.click(
134
+ lambda profile: f"✅ Logged in as {profile.username}" if profile else "*Not signed in.*",
135
+ inputs=None,
136
+ outputs=status
137
+ )
138
+
139
+ with gr.Row():
140
+ # Sidebar
141
+ with gr.Column(scale=2):
142
+ gr.Markdown("### 🏗️ Create New Space Sandbox")
143
+ api_key = gr.Textbox(label="Gemini API Key", placeholder="sk-...", type="password")
144
+ repo_name = gr.Textbox(label="Space Name", placeholder="my-sandbox")
145
+ sdk_selector = gr.Radio(label="SDK", choices=["gradio", "streamlit"], value="gradio")
146
+ create_btn = gr.Button("Initialize Sandbox")
147
+ create_status = gr.JSON(label="Init Status")
148
+ create_btn.click(init_chat, inputs=[repo_name, api_key], outputs=create_status)
149
+ # hidden store for repo_id
150
+ repo_store = gr.Variable("")
151
+
152
+ # Chat area
153
+ with gr.Column(scale=8):
154
+ chatbot = gr.Chatbot()
155
+ user_input = gr.Textbox(show_label=False, placeholder="Ask the sandbox to write or debug code...")
156
+ user_input.submit(
157
+ chatbot_respond,
158
+ inputs=[user_input, chatbot, repo_store, api_key],
159
+ outputs=chatbot
160
+ )
161
 
162
  if __name__ == "__main__":
163
  demo.launch()