Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -82,7 +82,7 @@ def check_iframe(url: str, timeout: int = 5) -> bool:
|
|
82 |
except:
|
83 |
return False
|
84 |
|
85 |
-
# — CORE LOOP —
|
86 |
|
87 |
def handle_user_message(
|
88 |
history,
|
@@ -97,21 +97,33 @@ def handle_user_message(
|
|
97 |
if not profile or not oauth_token:
|
98 |
return history + [{"role":"assistant","content":"⚠️ Please log in first."}], "", "", "<p>No Space yet.</p>"
|
99 |
|
100 |
-
client
|
101 |
-
code_fn
|
102 |
-
repo_id
|
103 |
iframe_url = f"https://huggingface.co/spaces/{repo_id}"
|
104 |
|
105 |
-
|
|
|
106 |
"role":"system",
|
107 |
"content":(
|
108 |
-
|
109 |
-
f"{
|
110 |
-
|
111 |
-
"
|
112 |
)
|
113 |
}
|
114 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
115 |
|
116 |
build_logs = run_logs = ""
|
117 |
backoff = 1
|
@@ -125,28 +137,28 @@ def handle_user_message(
|
|
125 |
max_output_tokens=max_output_tokens,
|
126 |
)
|
127 |
|
128 |
-
#
|
129 |
-
|
130 |
model="gemini-2.5-flash-preview-04-17",
|
131 |
-
contents=[m["content"] for m in
|
132 |
config=cfg
|
133 |
)
|
134 |
-
code = extract_code(
|
135 |
-
|
|
|
136 |
|
137 |
-
#
|
138 |
try:
|
139 |
compile(code, code_fn, "exec")
|
140 |
except SyntaxError as e:
|
141 |
-
|
142 |
"role":"user",
|
143 |
-
"content": f"SyntaxError caught: {e}. Please
|
144 |
})
|
145 |
-
time.sleep(backoff)
|
146 |
-
backoff = min(backoff*2, 30)
|
147 |
continue
|
148 |
|
149 |
-
# write
|
150 |
sdk_version = get_sdk_version(sdk_choice)
|
151 |
files = {
|
152 |
code_fn: code,
|
@@ -158,8 +170,6 @@ sdk_version: {sdk_version}
|
|
158 |
app_file: {code_fn}
|
159 |
pinned: false
|
160 |
---
|
161 |
-
|
162 |
-
See https://huggingface.co/docs/hub/spaces-config-reference
|
163 |
""",
|
164 |
"requirements.txt": "pandas\n" + ("streamlit\n" if sdk_choice=="streamlit" else "gradio\n")
|
165 |
}
|
@@ -167,46 +177,59 @@ See https://huggingface.co/docs/hub/spaces-config-reference
|
|
167 |
with open(fn, "w") as f:
|
168 |
f.write(content)
|
169 |
|
170 |
-
# push to HF
|
171 |
create_repo(repo_id=repo_id, token=oauth_token.token,
|
172 |
exist_ok=True, repo_type="space", space_sdk=sdk_choice)
|
173 |
for fn in files:
|
174 |
upload_file(
|
175 |
-
path_or_fileobj=fn,
|
176 |
-
|
177 |
-
repo_id=repo_id,
|
178 |
-
token=oauth_token.token,
|
179 |
repo_type="space"
|
180 |
)
|
181 |
|
182 |
# fetch logs
|
183 |
build_logs = fetch_logs(repo_id, "build")
|
184 |
run_logs = fetch_logs(repo_id, "run")
|
185 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
186 |
|
|
|
187 |
if "ERROR" not in build_logs.upper() and \
|
188 |
"ERROR" not in run_logs.upper() and \
|
189 |
check_iframe(iframe_url):
|
190 |
break
|
191 |
|
192 |
-
|
|
|
193 |
"role":"user",
|
194 |
-
"content":
|
195 |
-
f"Attempt {attempt} had {errors} errors.\n"
|
196 |
-
f"Build logs:\n{build_logs}\n\n"
|
197 |
-
f"Run logs:\n{run_logs}\n\n"
|
198 |
-
f"Please output the full corrected `{code_fn}` code block only, "
|
199 |
-
f"and ensure the iframe at {iframe_url} returns HTTP 200."
|
200 |
-
)
|
201 |
})
|
202 |
-
time.sleep(backoff)
|
203 |
-
backoff = min(backoff*2, 30)
|
204 |
|
205 |
-
|
|
|
|
|
|
|
|
|
206 |
iframe_html = (
|
207 |
f'<iframe src="{iframe_url}" width="100%" height="500px"></iframe>'
|
208 |
-
+ ("" if check_iframe(iframe_url)
|
209 |
-
"<p style='color:red;'>⚠️ iframe not responding.</p>")
|
210 |
)
|
211 |
return messages, build_logs, run_logs, iframe_html
|
212 |
|
@@ -249,9 +272,9 @@ with gr.Blocks(title="HF Space Auto‑Builder") as demo:
|
|
249 |
|
250 |
refresh_btn = gr.Button("Refresh Logs")
|
251 |
refresh_btn.click(
|
252 |
-
fn=lambda profile,token: (
|
253 |
-
fetch_logs(f"{profile.username}/{profile.username}-auto-space","build"),
|
254 |
-
fetch_logs(f"{profile.username}/{profile.username}-auto-space","run")
|
255 |
),
|
256 |
outputs=[build_box, run_box]
|
257 |
)
|
|
|
82 |
except:
|
83 |
return False
|
84 |
|
85 |
+
# — CORE LOOP WITH TWO AGENTS —
|
86 |
|
87 |
def handle_user_message(
|
88 |
history,
|
|
|
97 |
if not profile or not oauth_token:
|
98 |
return history + [{"role":"assistant","content":"⚠️ Please log in first."}], "", "", "<p>No Space yet.</p>"
|
99 |
|
100 |
+
client = genai.Client(api_key=gemini_api_key)
|
101 |
+
code_fn = "app.py" if sdk_choice=="gradio" else "streamlit_app.py"
|
102 |
+
repo_id = f"{profile.username}/{profile.username}-auto-space"
|
103 |
iframe_url = f"https://huggingface.co/spaces/{repo_id}"
|
104 |
|
105 |
+
# — SYSTEM PROMPTS —
|
106 |
+
system_code = {
|
107 |
"role":"system",
|
108 |
"content":(
|
109 |
+
"You are **Code‑Gen Agent**, a proactive AI developer. Your sole responsibility is to author "
|
110 |
+
f"and correct the entire `{code_fn}` file in a single markdown code block—no extra commentary. "
|
111 |
+
"You have permission to edit files, push updates to the HF Space, and optimize code. "
|
112 |
+
"After each push, await build & run logs before making further changes."
|
113 |
)
|
114 |
}
|
115 |
+
system_debug = {
|
116 |
+
"role":"system",
|
117 |
+
"content":(
|
118 |
+
"You are **Debug Agent**, a meticulous code reviewer. You can read all files, logs, and the app "
|
119 |
+
"preview, but you **cannot** modify or push code. Your task is to analyze the latest code + logs "
|
120 |
+
"and return concise, actionable feedback or “All clear.”"
|
121 |
+
)
|
122 |
+
}
|
123 |
+
|
124 |
+
# initialize each agent’s conversation
|
125 |
+
code_chat = [system_code] + history[:]
|
126 |
+
debug_chat = [system_debug] + history[:]
|
127 |
|
128 |
build_logs = run_logs = ""
|
129 |
backoff = 1
|
|
|
137 |
max_output_tokens=max_output_tokens,
|
138 |
)
|
139 |
|
140 |
+
# --- 1) Code‑Gen generates or updates code ---
|
141 |
+
resp_code = client.models.generate_content(
|
142 |
model="gemini-2.5-flash-preview-04-17",
|
143 |
+
contents=[m["content"] for m in code_chat],
|
144 |
config=cfg
|
145 |
)
|
146 |
+
code = extract_code(resp_code.text)
|
147 |
+
code_chat.append({"role":"assistant","content":code})
|
148 |
+
debug_chat.append({"role":"assistant","content":code})
|
149 |
|
150 |
+
# quick syntax check
|
151 |
try:
|
152 |
compile(code, code_fn, "exec")
|
153 |
except SyntaxError as e:
|
154 |
+
code_chat.append({
|
155 |
"role":"user",
|
156 |
+
"content": f"SyntaxError caught: {e}. Please correct `{code_fn}` only."
|
157 |
})
|
158 |
+
time.sleep(backoff); backoff = min(backoff*2, 30)
|
|
|
159 |
continue
|
160 |
|
161 |
+
# write & push to HF Space
|
162 |
sdk_version = get_sdk_version(sdk_choice)
|
163 |
files = {
|
164 |
code_fn: code,
|
|
|
170 |
app_file: {code_fn}
|
171 |
pinned: false
|
172 |
---
|
|
|
|
|
173 |
""",
|
174 |
"requirements.txt": "pandas\n" + ("streamlit\n" if sdk_choice=="streamlit" else "gradio\n")
|
175 |
}
|
|
|
177 |
with open(fn, "w") as f:
|
178 |
f.write(content)
|
179 |
|
|
|
180 |
create_repo(repo_id=repo_id, token=oauth_token.token,
|
181 |
exist_ok=True, repo_type="space", space_sdk=sdk_choice)
|
182 |
for fn in files:
|
183 |
upload_file(
|
184 |
+
path_or_fileobj=fn, path_in_repo=fn,
|
185 |
+
repo_id=repo_id, token=oauth_token.token,
|
|
|
|
|
186 |
repo_type="space"
|
187 |
)
|
188 |
|
189 |
# fetch logs
|
190 |
build_logs = fetch_logs(repo_id, "build")
|
191 |
run_logs = fetch_logs(repo_id, "run")
|
192 |
+
err_types = classify_errors(build_logs + "\n" + run_logs)
|
193 |
+
|
194 |
+
# --- 2) Debug‑Agent reviews code & logs ---
|
195 |
+
debug_input = (
|
196 |
+
f"🏷 **Attempt {attempt}**\n"
|
197 |
+
f"Error types: {err_types}\n\n"
|
198 |
+
f"**Build logs:**\n{build_logs}\n\n"
|
199 |
+
f"**Run logs:**\n{run_logs}\n\n"
|
200 |
+
"If there are no errors, reply “All clear.” Otherwise, list your recommended changes."
|
201 |
+
)
|
202 |
+
debug_chat.append({"role":"user","content":debug_input})
|
203 |
+
resp_debug = client.models.generate_content(
|
204 |
+
model="gemini-2.5-flash-preview-04-17",
|
205 |
+
contents=[m["content"] for m in debug_chat],
|
206 |
+
config=cfg
|
207 |
+
)
|
208 |
+
feedback = resp_debug.text.strip()
|
209 |
+
debug_chat.append({"role":"assistant","content":feedback})
|
210 |
|
211 |
+
# check for success
|
212 |
if "ERROR" not in build_logs.upper() and \
|
213 |
"ERROR" not in run_logs.upper() and \
|
214 |
check_iframe(iframe_url):
|
215 |
break
|
216 |
|
217 |
+
# feed debug feedback back to Code‑Gen
|
218 |
+
code_chat.append({
|
219 |
"role":"user",
|
220 |
+
"content": f"🔧 Debug feedback:\n{feedback}\nPlease output the full corrected `{code_fn}` code block only."
|
|
|
|
|
|
|
|
|
|
|
|
|
221 |
})
|
222 |
+
time.sleep(backoff); backoff = min(backoff*2, 30)
|
|
|
223 |
|
224 |
+
# prepare UI outputs
|
225 |
+
messages = [
|
226 |
+
{"role": m["role"], "content": m["content"]}
|
227 |
+
for m in code_chat if m["role"] != "system"
|
228 |
+
]
|
229 |
iframe_html = (
|
230 |
f'<iframe src="{iframe_url}" width="100%" height="500px"></iframe>'
|
231 |
+
+ ("" if check_iframe(iframe_url)
|
232 |
+
else "<p style='color:red;'>⚠️ iframe not responding.</p>")
|
233 |
)
|
234 |
return messages, build_logs, run_logs, iframe_html
|
235 |
|
|
|
272 |
|
273 |
refresh_btn = gr.Button("Refresh Logs")
|
274 |
refresh_btn.click(
|
275 |
+
fn=lambda profile, token: (
|
276 |
+
fetch_logs(f"{profile.username}/{profile.username}-auto-space", "build"),
|
277 |
+
fetch_logs(f"{profile.username}/{profile.username}-auto-space", "run")
|
278 |
),
|
279 |
outputs=[build_box, run_box]
|
280 |
)
|