Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -1,136 +1,218 @@
|
|
1 |
-
import os
|
2 |
-
import json
|
3 |
import gradio as gr
|
4 |
-
from huggingface_hub import create_repo,
|
5 |
from huggingface_hub.utils import build_hf_headers, get_session, hf_raise_for_status
|
6 |
from google import genai
|
7 |
from google.genai import types
|
8 |
|
9 |
-
#
|
10 |
-
|
11 |
-
def
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
"
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
#
|
49 |
-
def create_space_backend(username: str, hf_token: str, repo_name: str, sdk: str) -> str:
|
50 |
-
repo_id = f"{username}/{repo_name}"
|
51 |
-
create_repo(repo_id=repo_id, token=hf_token, exist_ok=True, repo_type="space", space_sdk=sdk)
|
52 |
-
return repo_id
|
53 |
-
|
54 |
-
def fetch_logs(repo_id: str, level: str) -> list[dict]:
|
55 |
jwt_url = f"{constants.ENDPOINT}/api/spaces/{repo_id}/jwt"
|
56 |
r = get_session().get(jwt_url, headers=build_hf_headers())
|
57 |
hf_raise_for_status(r)
|
58 |
jwt = r.json()["token"]
|
59 |
-
|
60 |
-
|
61 |
-
|
|
|
62 |
hf_raise_for_status(resp)
|
63 |
for raw in resp.iter_lines():
|
64 |
-
if raw.startswith(b"data: "):
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
94 |
part = resp.candidates[0].content.parts[0]
|
|
|
|
|
95 |
if part.function_call:
|
96 |
-
|
97 |
-
args = json.loads(
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
102 |
else:
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
|
|
|
|
119 |
with gr.Row():
|
120 |
-
with gr.Column(scale=
|
121 |
-
gr.Markdown("### Setup Sandbox")
|
122 |
gemini_key = gr.Textbox(label="Gemini API Key", type="password")
|
123 |
-
repo_name
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
134 |
|
135 |
if __name__ == "__main__":
|
136 |
demo.launch()
|
|
|
1 |
+
import os, json
|
|
|
2 |
import gradio as gr
|
3 |
+
from huggingface_hub import create_repo, upload_file, list_repo_files, constants
|
4 |
from huggingface_hub.utils import build_hf_headers, get_session, hf_raise_for_status
|
5 |
from google import genai
|
6 |
from google.genai import types
|
7 |
|
8 |
+
# βββ HELPERS FOR HF SPACES βββ
|
9 |
+
|
10 |
+
def create_space(repo_name: str, sdk: str, hf_token: str):
|
11 |
+
"""Create or get a HF Space, return (repo_id, log, iframe)."""
|
12 |
+
token = hf_token or os.getenv("HF_TOKEN", "")
|
13 |
+
if not token:
|
14 |
+
return None, "β οΈ No HF token provided.", ""
|
15 |
+
os.environ["HF_TOKEN"] = token
|
16 |
+
repo_id = f"{os.getenv('HF_USERNAME', '')}/{repo_name}"
|
17 |
+
create_repo(
|
18 |
+
repo_id=repo_id,
|
19 |
+
token=token,
|
20 |
+
exist_ok=True,
|
21 |
+
repo_type="space",
|
22 |
+
space_sdk=sdk,
|
23 |
+
)
|
24 |
+
url = f"https://huggingface.co/spaces/{repo_id}"
|
25 |
+
return repo_id, f"β
Space ready: {url} (SDK={sdk})", f'<iframe src="{url}" width="100%" height="400px"></iframe>'
|
26 |
+
|
27 |
+
def upload_file_to_space(repo_id: str, local_path: str, hf_token: str):
|
28 |
+
"""Upload a local file to the Space."""
|
29 |
+
token = hf_token or os.getenv("HF_TOKEN", "")
|
30 |
+
if not (repo_id and token and os.path.exists(local_path)):
|
31 |
+
return "β οΈ Missing repo_id, token, or file not found."
|
32 |
+
os.environ["HF_TOKEN"] = token
|
33 |
+
upload_file(
|
34 |
+
path_or_fileobj=local_path,
|
35 |
+
path_in_repo=os.path.basename(local_path),
|
36 |
+
repo_id=repo_id,
|
37 |
+
token=token,
|
38 |
+
repo_type="space",
|
39 |
+
)
|
40 |
+
return f"β
Uploaded `{os.path.basename(local_path)}` to `{repo_id}`"
|
41 |
+
|
42 |
+
def fetch_logs(repo_id: str, level: str, hf_token: str):
|
43 |
+
"""Stream build or run logs."""
|
44 |
+
token = hf_token or os.getenv("HF_TOKEN", "")
|
45 |
+
if not (repo_id and token):
|
46 |
+
return "β οΈ Missing repo_id or HF token."
|
47 |
+
# 1) get JWT
|
|
|
|
|
|
|
|
|
|
|
|
|
48 |
jwt_url = f"{constants.ENDPOINT}/api/spaces/{repo_id}/jwt"
|
49 |
r = get_session().get(jwt_url, headers=build_hf_headers())
|
50 |
hf_raise_for_status(r)
|
51 |
jwt = r.json()["token"]
|
52 |
+
# 2) stream
|
53 |
+
url = f"https://api.hf.space/v1/{repo_id}/logs/{level}"
|
54 |
+
lines = []
|
55 |
+
with get_session().get(url, headers=build_hf_headers(token=jwt), stream=True) as resp:
|
56 |
hf_raise_for_status(resp)
|
57 |
for raw in resp.iter_lines():
|
58 |
+
if not raw.startswith(b"data: "): continue
|
59 |
+
ev = json.loads(raw[len(b"data: "):])
|
60 |
+
ts, txt = ev.get("timestamp",""), ev.get("data","")
|
61 |
+
lines.append(f"[{ts}] {txt}")
|
62 |
+
return "\n".join(lines)
|
63 |
+
|
64 |
+
def list_files(repo_id: str, hf_token: str):
|
65 |
+
"""List files in the Space repo."""
|
66 |
+
token = hf_token or os.getenv("HF_TOKEN", "")
|
67 |
+
if not (repo_id and token):
|
68 |
+
return "β οΈ Missing repo_id or HF token."
|
69 |
+
return "\n".join(list_repo_files(repo_id, token=token, repo_type="space"))
|
70 |
+
|
71 |
+
|
72 |
+
# βββ GEMINI FUNCTION DECLARATIONS βββ
|
73 |
+
|
74 |
+
func_decls = [
|
75 |
+
{
|
76 |
+
"name": "create_space",
|
77 |
+
"description": "Create or get a Hugging Face Space",
|
78 |
+
"parameters": {
|
79 |
+
"type":"object",
|
80 |
+
"properties":{
|
81 |
+
"repo_name":{"type":"string"},
|
82 |
+
"sdk":{"type":"string","enum":["gradio","streamlit"]}
|
83 |
+
},
|
84 |
+
"required":["repo_name","sdk"]
|
85 |
+
}
|
86 |
+
},
|
87 |
+
{
|
88 |
+
"name":"upload_file",
|
89 |
+
"description":"Upload a file to the Space",
|
90 |
+
"parameters":{
|
91 |
+
"type":"object",
|
92 |
+
"properties":{
|
93 |
+
"repo_id":{"type":"string"},
|
94 |
+
"local_path":{"type":"string"}
|
95 |
+
},
|
96 |
+
"required":["repo_id","local_path"]
|
97 |
+
}
|
98 |
+
},
|
99 |
+
{
|
100 |
+
"name":"list_files",
|
101 |
+
"description":"List files in the Space",
|
102 |
+
"parameters":{
|
103 |
+
"type":"object",
|
104 |
+
"properties":{"repo_id":{"type":"string"}},
|
105 |
+
"required":["repo_id"]
|
106 |
+
}
|
107 |
+
},
|
108 |
+
{
|
109 |
+
"name":"get_build_logs",
|
110 |
+
"description":"Fetch the build logs",
|
111 |
+
"parameters":{
|
112 |
+
"type":"object",
|
113 |
+
"properties":{"repo_id":{"type":"string"}},
|
114 |
+
"required":["repo_id"]
|
115 |
+
}
|
116 |
+
},
|
117 |
+
{
|
118 |
+
"name":"get_run_logs",
|
119 |
+
"description":"Fetch the run logs",
|
120 |
+
"parameters":{
|
121 |
+
"type":"object",
|
122 |
+
"properties":{"repo_id":{"type":"string"}},
|
123 |
+
"required":["repo_id"]
|
124 |
+
}
|
125 |
+
},
|
126 |
+
]
|
127 |
+
|
128 |
+
# βββ CHAT PROCESSING βββ
|
129 |
+
|
130 |
+
def process_message(user_msg, gemini_key, repo_name, sdk, hf_token, chat_history, session):
|
131 |
+
# init Gemini chat
|
132 |
+
if session.get("chat") is None:
|
133 |
+
client = genai.Client(api_key=gemini_key)
|
134 |
+
cfg = types.GenerateContentConfig(
|
135 |
+
system_instruction="You are a HF Spaces assistant. Use functions to manage spaces.",
|
136 |
+
temperature=0,
|
137 |
+
tools=[ types.Tool(function_declarations=func_decls) ]
|
138 |
+
)
|
139 |
+
session["chat"] = client.chats.create(model="gemini-2.0-flash", config=cfg)
|
140 |
+
session["repo_id"] = None
|
141 |
+
|
142 |
+
chat = session["chat"]
|
143 |
+
# record user
|
144 |
+
chat_history = chat_history + [(user_msg, None)]
|
145 |
+
resp = chat.send_message(user_msg)
|
146 |
part = resp.candidates[0].content.parts[0]
|
147 |
+
|
148 |
+
# if function call requested
|
149 |
if part.function_call:
|
150 |
+
name = part.function_call.name
|
151 |
+
args = json.loads(part.function_call.args)
|
152 |
+
# dispatch
|
153 |
+
if name=="create_space":
|
154 |
+
rid, log, iframe = create_space(args["repo_name"], args["sdk"], hf_token)
|
155 |
+
session["repo_id"] = rid
|
156 |
+
result = {"repo_id":rid,"log":log,"iframe":iframe}
|
157 |
+
elif name=="upload_file":
|
158 |
+
result = {"log": upload_file_to_space(args["repo_id"], args["local_path"], hf_token)}
|
159 |
+
elif name=="list_files":
|
160 |
+
result = {"files": list_files(args["repo_id"], hf_token)}
|
161 |
+
elif name=="get_build_logs":
|
162 |
+
result = {"log": fetch_logs(args["repo_id"], "build", hf_token)}
|
163 |
+
elif name=="get_run_logs":
|
164 |
+
result = {"log": fetch_logs(args["repo_id"], "run", hf_token)}
|
165 |
+
else:
|
166 |
+
result = {"log":f"β οΈ Unknown function {name}"}
|
167 |
+
|
168 |
+
# feed function result back to chat
|
169 |
+
chat.send_message(
|
170 |
+
types.Content(
|
171 |
+
role="function",
|
172 |
+
parts=[ types.Part(function_call=part.function_call, function_response=json.dumps(result)) ]
|
173 |
+
)
|
174 |
+
)
|
175 |
+
final = chat.get_history()[-1].parts[0].text
|
176 |
else:
|
177 |
+
final = part.text
|
178 |
+
|
179 |
+
# update panels
|
180 |
+
iframe = session.get("iframe", "")
|
181 |
+
log = session.get("log", "")
|
182 |
+
files = session.get("files", "")
|
183 |
+
|
184 |
+
if part.function_call:
|
185 |
+
session["iframe"] = result.get("iframe", iframe)
|
186 |
+
session["log"] = result.get("log", log)
|
187 |
+
session["files"] = result.get("files", files)
|
188 |
+
|
189 |
+
chat_history[-1] = (user_msg, final)
|
190 |
+
return chat_history, session["iframe"], session["log"], session["files"], session
|
191 |
+
|
192 |
+
# βββ GRADIO INTERFACE βββ
|
193 |
+
|
194 |
+
with gr.Blocks(title="HF Spaces β Gemini Chat") as demo:
|
195 |
with gr.Row():
|
196 |
+
with gr.Column(scale=1):
|
|
|
197 |
gemini_key = gr.Textbox(label="Gemini API Key", type="password")
|
198 |
+
repo_name = gr.Textbox(label="Space name", placeholder="my-space")
|
199 |
+
sdk_choice = gr.Radio(["gradio","streamlit"], value="gradio", label="SDK")
|
200 |
+
hf_token = gr.Textbox(label="HF Token (opt)", type="password")
|
201 |
+
with gr.Column(scale=3):
|
202 |
+
chatbox = gr.Chatbot()
|
203 |
+
user_input = gr.Textbox(show_label=False, placeholder="e.g. Create Space")
|
204 |
+
send_btn = gr.Button("Send")
|
205 |
+
|
206 |
+
iframe_out = gr.HTML(label="Space Preview")
|
207 |
+
log_out = gr.Textbox(label="Latest Log", lines=8)
|
208 |
+
files_out = gr.Textbox(label="Files in Space", lines=4)
|
209 |
+
|
210 |
+
state = gr.State({})
|
211 |
+
send_btn.click(
|
212 |
+
fn=process_message,
|
213 |
+
inputs=[user_input, gemini_key, repo_name, sdk_choice, hf_token, chatbox, state],
|
214 |
+
outputs=[chatbox, iframe_out, log_out, files_out, state]
|
215 |
+
)
|
216 |
|
217 |
if __name__ == "__main__":
|
218 |
demo.launch()
|