Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -110,7 +110,9 @@ def get_container_logs(repo_id, profile, token):
|
|
110 |
return "β οΈ Please log in and create a Space first."
|
111 |
return _fetch_space_logs_level(repo_id, "run")
|
112 |
|
113 |
-
|
|
|
|
|
114 |
|
115 |
func_decls = [
|
116 |
types.FunctionDeclaration(
|
@@ -120,9 +122,9 @@ func_decls = [
|
|
120 |
"type": "object",
|
121 |
"properties": {
|
122 |
"repo_name": {"type": "string"},
|
123 |
-
"sdk": {"type": "string", "enum": ["gradio", "streamlit"]}
|
124 |
},
|
125 |
-
"required": ["repo_name", "sdk"]
|
126 |
}
|
127 |
),
|
128 |
types.FunctionDeclaration(
|
@@ -131,10 +133,10 @@ func_decls = [
|
|
131 |
parameters={
|
132 |
"type": "object",
|
133 |
"properties": {
|
134 |
-
"path": {"type": "string"
|
135 |
-
"content": {"type": "string"
|
136 |
},
|
137 |
-
"required": ["path", "content"]
|
138 |
}
|
139 |
),
|
140 |
types.FunctionDeclaration(
|
@@ -143,7 +145,7 @@ func_decls = [
|
|
143 |
parameters={
|
144 |
"type": "object",
|
145 |
"properties": {"repo_id": {"type": "string"}},
|
146 |
-
"required": ["repo_id"]
|
147 |
}
|
148 |
),
|
149 |
types.FunctionDeclaration(
|
@@ -152,156 +154,132 @@ func_decls = [
|
|
152 |
parameters={
|
153 |
"type": "object",
|
154 |
"properties": {"repo_id": {"type": "string"}},
|
155 |
-
"required": ["repo_id"]
|
156 |
}
|
157 |
),
|
158 |
types.FunctionDeclaration(
|
159 |
name="get_run_logs",
|
160 |
-
description="Fetch container
|
161 |
parameters={
|
162 |
"type": "object",
|
163 |
"properties": {"repo_id": {"type": "string"}},
|
164 |
-
"required": ["repo_id"]
|
165 |
}
|
166 |
),
|
167 |
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
168 |
|
169 |
-
# β CHAT HANDLER β
|
170 |
|
171 |
def process_message(
|
172 |
profile, token, user_msg,
|
173 |
gemini_key, sidebar_repo, sidebar_sdk,
|
174 |
chat_history, session
|
175 |
):
|
176 |
-
# Initialize
|
177 |
if session.get("chat") is None:
|
178 |
client = genai.Client(api_key=gemini_key)
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
183 |
),
|
184 |
-
temperature=0,
|
185 |
-
tools=func_decls, # β pass declarations directly
|
186 |
)
|
187 |
-
session
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
|
|
|
|
|
|
|
|
192 |
|
193 |
-
#
|
194 |
if session["repo_id"] is None:
|
195 |
-
m = re.search(r"call (?:it )?([A-Za-z0-9_-]+)", user_msg
|
196 |
if m:
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
)
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
session["repo_id"] = rid
|
232 |
-
result = {"log": log, "iframe": iframe}
|
233 |
-
|
234 |
-
elif fn == "write_file":
|
235 |
-
status = write_file(
|
236 |
-
args["path"], args["content"],
|
237 |
-
session["repo_id"], profile, token
|
238 |
-
)
|
239 |
-
result = {"status": status}
|
240 |
-
|
241 |
-
elif fn == "list_files":
|
242 |
-
fl = list_repo_files(
|
243 |
-
session["repo_id"],
|
244 |
-
token=token.token,
|
245 |
-
repo_type="space"
|
246 |
-
)
|
247 |
-
result = {"files": "\n".join(fl)}
|
248 |
-
|
249 |
-
elif fn == "get_build_logs":
|
250 |
-
result = {"log": get_build_logs(
|
251 |
-
session["repo_id"], profile, token
|
252 |
-
)}
|
253 |
-
|
254 |
-
elif fn == "get_run_logs":
|
255 |
-
result = {"log": get_container_logs(
|
256 |
-
session["repo_id"], profile, token
|
257 |
-
)}
|
258 |
-
|
259 |
-
# Send back the *dict* directly (no json.dumps)
|
260 |
-
session["chat"].send_message(
|
261 |
-
types.Content(
|
262 |
-
role="function",
|
263 |
-
parts=[ types.Part(
|
264 |
-
function_call=part.function_call,
|
265 |
-
function_response=result
|
266 |
-
)]
|
267 |
-
)
|
268 |
-
)
|
269 |
-
assistant_text = session["chat"].get_history()[-1].parts[0].text
|
270 |
-
|
271 |
-
else:
|
272 |
-
assistant_text = part.text
|
273 |
-
|
274 |
-
# Record assistant
|
275 |
-
session["messages"].append({
|
276 |
-
"role":"assistant","content":assistant_text
|
277 |
-
})
|
278 |
-
|
279 |
-
# Update panels
|
280 |
-
for k in ("iframe","log","files"):
|
281 |
-
if k in result:
|
282 |
-
session[k] = result[k]
|
283 |
-
|
284 |
-
return (
|
285 |
-
session["messages"],
|
286 |
-
session.get("iframe",""),
|
287 |
-
session.get("log",""),
|
288 |
-
session.get("files",""),
|
289 |
-
session
|
290 |
-
)
|
291 |
-
|
292 |
-
# β SYNC MANUAL CHANGES β
|
293 |
|
294 |
def sync_manual(profile, token, session):
|
295 |
if not (profile and token and session.get("repo_id")):
|
296 |
-
return
|
297 |
-
|
298 |
-
|
299 |
-
)
|
300 |
-
|
301 |
-
session["log"] = "π Manual changes synced."
|
302 |
-
return session["iframe"], session["log"], session["files"], session
|
303 |
|
304 |
-
# β
|
305 |
|
306 |
with gr.Blocks(css="""
|
307 |
#sidebar { background:#f2f2f2; padding:1rem; border-right:1px solid #ccc; }
|
@@ -324,12 +302,12 @@ with gr.Blocks(css="""
|
|
324 |
[profile_state, token_state], [models_md])
|
325 |
|
326 |
gemini_key = gr.Textbox(label="Gemini API Key", type="password")
|
327 |
-
sidebar_repo = gr.Textbox(label="Space name", placeholder="
|
328 |
sidebar_sdk = gr.Radio(["gradio","streamlit"],
|
329 |
value="gradio", label="SDK")
|
330 |
|
331 |
gr.Markdown("---")
|
332 |
-
confirm_btn = gr.Button("π
|
333 |
|
334 |
# Main area
|
335 |
with gr.Column(elem_id="main", scale=3):
|
@@ -338,17 +316,16 @@ with gr.Blocks(css="""
|
|
338 |
with gr.TabItem("π¬ Chat"):
|
339 |
chatbox = gr.Chatbot(type="messages")
|
340 |
user_input = gr.Textbox(show_label=False,
|
341 |
-
placeholder="
|
342 |
send_btn = gr.Button("Send")
|
343 |
with gr.TabItem("π οΈ Manual"):
|
344 |
-
gr.Markdown("#### Create
|
345 |
-
repo_m = gr.Textbox(label="Name")
|
346 |
sdk_m = gr.Radio(["gradio","streamlit"],
|
347 |
value="gradio", label="SDK")
|
348 |
create_btn = gr.Button("Create Space")
|
349 |
sess_id = gr.Textbox(visible=False)
|
350 |
-
log_c = gr.Textbox(label="Log",
|
351 |
-
interactive=False, lines=2)
|
352 |
preview = gr.HTML("<p>No Space yet.</p>")
|
353 |
|
354 |
create_btn.click(create_space,
|
@@ -359,7 +336,7 @@ with gr.Blocks(css="""
|
|
359 |
path = gr.Textbox(label="Path", value="app.py")
|
360 |
file_u = gr.File()
|
361 |
upload_btn = gr.Button("Upload File")
|
362 |
-
log_u = gr.Textbox(label="Log",
|
363 |
interactive=False, lines=2)
|
364 |
|
365 |
upload_btn.click(upload_file_to_space,
|
@@ -397,4 +374,4 @@ with gr.Blocks(css="""
|
|
397 |
[iframe_out, log_out, files_out, state])
|
398 |
|
399 |
if __name__ == "__main__":
|
400 |
-
demo.launch(
|
|
|
110 |
return "β οΈ Please log in and create a Space first."
|
111 |
return _fetch_space_logs_level(repo_id, "run")
|
112 |
|
113 |
+
|
114 |
+
# β FUNCTION DECLARATIONS & TOOL β
|
115 |
+
# Build a single Tool containing all function declarations.
|
116 |
|
117 |
func_decls = [
|
118 |
types.FunctionDeclaration(
|
|
|
122 |
"type": "object",
|
123 |
"properties": {
|
124 |
"repo_name": {"type": "string"},
|
125 |
+
"sdk": {"type": "string", "enum": ["gradio", "streamlit"]}
|
126 |
},
|
127 |
+
"required": ["repo_name", "sdk"]
|
128 |
}
|
129 |
),
|
130 |
types.FunctionDeclaration(
|
|
|
133 |
parameters={
|
134 |
"type": "object",
|
135 |
"properties": {
|
136 |
+
"path": {"type": "string"},
|
137 |
+
"content": {"type": "string"}
|
138 |
},
|
139 |
+
"required": ["path", "content"]
|
140 |
}
|
141 |
),
|
142 |
types.FunctionDeclaration(
|
|
|
145 |
parameters={
|
146 |
"type": "object",
|
147 |
"properties": {"repo_id": {"type": "string"}},
|
148 |
+
"required": ["repo_id"]
|
149 |
}
|
150 |
),
|
151 |
types.FunctionDeclaration(
|
|
|
154 |
parameters={
|
155 |
"type": "object",
|
156 |
"properties": {"repo_id": {"type": "string"}},
|
157 |
+
"required": ["repo_id"]
|
158 |
}
|
159 |
),
|
160 |
types.FunctionDeclaration(
|
161 |
name="get_run_logs",
|
162 |
+
description="Fetch container logs",
|
163 |
parameters={
|
164 |
"type": "object",
|
165 |
"properties": {"repo_id": {"type": "string"}},
|
166 |
+
"required": ["repo_id"]
|
167 |
}
|
168 |
),
|
169 |
]
|
170 |
+
tool = types.Tool(function_declarations=func_decls)
|
171 |
+
|
172 |
+
|
173 |
+
# β CHAT HANDLER β
|
174 |
+
|
175 |
+
def execute_function_by_name(name, args, profile, token, session):
|
176 |
+
"""Dispatch to the right function and return a raw dict result."""
|
177 |
+
if name == "create_space":
|
178 |
+
rid, log, iframe = create_space(
|
179 |
+
args["repo_name"], session["sdk"], profile, token
|
180 |
+
)
|
181 |
+
session["repo_id"] = rid
|
182 |
+
return {"log": log, "iframe": iframe}
|
183 |
+
if name == "write_file":
|
184 |
+
status = write_file(
|
185 |
+
args["path"], args["content"],
|
186 |
+
session["repo_id"], profile, token
|
187 |
+
)
|
188 |
+
return {"status": status}
|
189 |
+
if name == "list_files":
|
190 |
+
fl = list_repo_files(
|
191 |
+
session["repo_id"], token=token.token, repo_type="space"
|
192 |
+
)
|
193 |
+
return {"files": "\n".join(fl)}
|
194 |
+
if name == "get_build_logs":
|
195 |
+
return {"log": get_build_logs(
|
196 |
+
session["repo_id"], profile, token
|
197 |
+
)}
|
198 |
+
if name == "get_run_logs":
|
199 |
+
return {"log": get_container_logs(
|
200 |
+
session["repo_id"], profile, token
|
201 |
+
)}
|
202 |
+
return {"error": f"Unknown function {name}"}
|
203 |
|
|
|
204 |
|
205 |
def process_message(
|
206 |
profile, token, user_msg,
|
207 |
gemini_key, sidebar_repo, sidebar_sdk,
|
208 |
chat_history, session
|
209 |
):
|
210 |
+
# Initialize chat once
|
211 |
if session.get("chat") is None:
|
212 |
client = genai.Client(api_key=gemini_key)
|
213 |
+
# Pass the Tool object, not a list of dicts
|
214 |
+
model = client.chats.create(
|
215 |
+
model="gemini-2.0-flash",
|
216 |
+
config=types.GenerateContentConfig(
|
217 |
+
system_instruction=(
|
218 |
+
"You are a HF Spaces admin. You can create spaces, "
|
219 |
+
"write files, list files, and fetch logs."
|
220 |
+
),
|
221 |
+
temperature=0,
|
222 |
+
tools=[tool],
|
223 |
),
|
|
|
|
|
224 |
)
|
225 |
+
session.update({
|
226 |
+
"chat": model,
|
227 |
+
"repo_id": None,
|
228 |
+
"sdk": sidebar_sdk,
|
229 |
+
"messages": []
|
230 |
+
})
|
231 |
+
|
232 |
+
# Remember current SDK choice
|
233 |
+
session["sdk"] = sidebar_sdk
|
234 |
|
235 |
+
# Pre-chat βcall it Xβ handler
|
236 |
if session["repo_id"] is None:
|
237 |
+
m = re.search(r"call (?:it )?([A-Za-z0-9_-]+)", user_msg)
|
238 |
if m:
|
239 |
+
args = {"repo_name": m.group(1), "sdk": sidebar_sdk}
|
240 |
+
result = execute_function_by_name("create_space", args, profile, token, session)
|
241 |
+
session["messages"].append({"role": "assistant", "content": result["log"]})
|
242 |
+
return session["messages"], result.get("iframe", ""), result["log"], "", session
|
243 |
+
|
244 |
+
# Append user message
|
245 |
+
session["messages"].append({"role": "user", "content": user_msg})
|
246 |
+
response = session["chat"].send_message(user_msg)
|
247 |
+
|
248 |
+
# Handle function-calling loop
|
249 |
+
while True:
|
250 |
+
part = response.candidates[0].content.parts[0]
|
251 |
+
if not part.function_call:
|
252 |
+
break # no more tools needed
|
253 |
+
fname = part.function_call.name
|
254 |
+
fargs = part.function_call.args or {}
|
255 |
+
# Run the tool
|
256 |
+
result = execute_function_by_name(fname, fargs, profile, token, session)
|
257 |
+
# Send function result back to model
|
258 |
+
resp_part = types.Part.from_function_response(name=fname, response=result)
|
259 |
+
response = session["chat"].send_message(types.Content(role="user", parts=[resp_part]))
|
260 |
+
|
261 |
+
# Final assistant text
|
262 |
+
assistant_text = response.text
|
263 |
+
session["messages"].append({"role": "assistant", "content": assistant_text})
|
264 |
+
|
265 |
+
# Update UI panels
|
266 |
+
iframe = session.get("iframe", "")
|
267 |
+
log = session.get("log", "")
|
268 |
+
files = session.get("files", "")
|
269 |
+
return session["messages"], iframe, log, files, session
|
270 |
+
|
271 |
+
|
272 |
+
# β MANUAL SYNC β
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
273 |
|
274 |
def sync_manual(profile, token, session):
|
275 |
if not (profile and token and session.get("repo_id")):
|
276 |
+
return "", "β οΈ Cannot sync manual changes.", "", session
|
277 |
+
files = list_repo_files(session["repo_id"], token=token.token, repo_type="space")
|
278 |
+
log = "π Manual changes synced."
|
279 |
+
return session.get("iframe",""), log, "\n".join(files), session
|
280 |
+
|
|
|
|
|
281 |
|
282 |
+
# β GRADIO UI β
|
283 |
|
284 |
with gr.Blocks(css="""
|
285 |
#sidebar { background:#f2f2f2; padding:1rem; border-right:1px solid #ccc; }
|
|
|
302 |
[profile_state, token_state], [models_md])
|
303 |
|
304 |
gemini_key = gr.Textbox(label="Gemini API Key", type="password")
|
305 |
+
sidebar_repo = gr.Textbox(label="Space name", placeholder="blurrtest")
|
306 |
sidebar_sdk = gr.Radio(["gradio","streamlit"],
|
307 |
value="gradio", label="SDK")
|
308 |
|
309 |
gr.Markdown("---")
|
310 |
+
confirm_btn = gr.Button("π Sync Manual Changes")
|
311 |
|
312 |
# Main area
|
313 |
with gr.Column(elem_id="main", scale=3):
|
|
|
316 |
with gr.TabItem("π¬ Chat"):
|
317 |
chatbox = gr.Chatbot(type="messages")
|
318 |
user_input = gr.Textbox(show_label=False,
|
319 |
+
placeholder="Generate codeβ¦")
|
320 |
send_btn = gr.Button("Send")
|
321 |
with gr.TabItem("π οΈ Manual"):
|
322 |
+
gr.Markdown("#### Create / Rename Space")
|
323 |
+
repo_m = gr.Textbox(label="Name", value="")
|
324 |
sdk_m = gr.Radio(["gradio","streamlit"],
|
325 |
value="gradio", label="SDK")
|
326 |
create_btn = gr.Button("Create Space")
|
327 |
sess_id = gr.Textbox(visible=False)
|
328 |
+
log_c = gr.Textbox(label="Log", interactive=False, lines=2)
|
|
|
329 |
preview = gr.HTML("<p>No Space yet.</p>")
|
330 |
|
331 |
create_btn.click(create_space,
|
|
|
336 |
path = gr.Textbox(label="Path", value="app.py")
|
337 |
file_u = gr.File()
|
338 |
upload_btn = gr.Button("Upload File")
|
339 |
+
log_u = gr.Textbox(label="Upload Log",
|
340 |
interactive=False, lines=2)
|
341 |
|
342 |
upload_btn.click(upload_file_to_space,
|
|
|
374 |
[iframe_out, log_out, files_out, state])
|
375 |
|
376 |
if __name__ == "__main__":
|
377 |
+
demo.launch()
|