Spaces:
Running on Zero
Running on Zero
atakan Claude Sonnet 5 commited on
Commit ·
b24b1e1
1
Parent(s): ef72bf0
fix: Include the actual Gradio/ZeroGPU code changes missed in the last commit
Browse filesThe previous commit's git add silently failed on the already-deleted
Dockerfile pathspec, so it only recorded the Dockerfile removal --
README.md, app.py, and requirements.txt (the actual SDK/ZeroGPU
changes described in that commit's message) never got staged. This
left the Space briefly in a broken half-migrated state (no Dockerfile,
but README still declaring sdk: docker). This commit adds the missing
changes.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- README.md +2 -2
- app.py +35 -3
- requirements.txt +2 -1
README.md
CHANGED
|
@@ -2,8 +2,8 @@
|
|
| 2 |
title: ControlAI Agent
|
| 3 |
colorFrom: blue
|
| 4 |
colorTo: indigo
|
| 5 |
-
sdk:
|
| 6 |
-
|
| 7 |
pinned: false
|
| 8 |
---
|
| 9 |
|
|
|
|
| 2 |
title: ControlAI Agent
|
| 3 |
colorFrom: blue
|
| 4 |
colorTo: indigo
|
| 5 |
+
sdk: gradio
|
| 6 |
+
app_file: app.py
|
| 7 |
pinned: false
|
| 8 |
---
|
| 9 |
|
app.py
CHANGED
|
@@ -12,6 +12,8 @@ from contextlib import asynccontextmanager
|
|
| 12 |
from pathlib import Path
|
| 13 |
from typing import Any
|
| 14 |
|
|
|
|
|
|
|
| 15 |
from fastapi import FastAPI, File, HTTPException, UploadFile
|
| 16 |
from fastapi.middleware.cors import CORSMiddleware
|
| 17 |
from fastapi.responses import FileResponse, HTMLResponse, JSONResponse
|
|
@@ -80,6 +82,20 @@ def get_agent() -> ControlAIAgent:
|
|
| 80 |
return agent_instance
|
| 81 |
|
| 82 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 83 |
class ChatRequest(BaseModel):
|
| 84 |
message: str
|
| 85 |
history: list[dict[str, str]] = []
|
|
@@ -186,7 +202,7 @@ async def chat_stream_endpoint(req: ChatRequest):
|
|
| 186 |
def event_generator():
|
| 187 |
try:
|
| 188 |
with inference_lock:
|
| 189 |
-
for event in
|
| 190 |
yield f"data: {json.dumps(event, ensure_ascii=False)}\n\n"
|
| 191 |
except Exception as exc:
|
| 192 |
yield f"data: {json.dumps({'type': 'error', 'error': str(exc)}, ensure_ascii=False)}\n\n"
|
|
@@ -211,7 +227,7 @@ async def chat_endpoint(req: ChatRequest) -> ChatResponse:
|
|
| 211 |
try:
|
| 212 |
agent = get_agent()
|
| 213 |
with inference_lock:
|
| 214 |
-
result =
|
| 215 |
elapsed = time.time() - t0
|
| 216 |
|
| 217 |
# Collect tool traces
|
|
@@ -249,10 +265,26 @@ async def chat_endpoint(req: ChatRequest) -> ChatResponse:
|
|
| 249 |
)
|
| 250 |
|
| 251 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 252 |
def main() -> None:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 253 |
import threading
|
| 254 |
import webbrowser
|
| 255 |
-
import uvicorn
|
| 256 |
|
| 257 |
def _open_browser() -> None:
|
| 258 |
time.sleep(1.2)
|
|
|
|
| 12 |
from pathlib import Path
|
| 13 |
from typing import Any
|
| 14 |
|
| 15 |
+
import gradio as gr
|
| 16 |
+
import spaces
|
| 17 |
from fastapi import FastAPI, File, HTTPException, UploadFile
|
| 18 |
from fastapi.middleware.cors import CORSMiddleware
|
| 19 |
from fastapi.responses import FileResponse, HTMLResponse, JSONResponse
|
|
|
|
| 82 |
return agent_instance
|
| 83 |
|
| 84 |
|
| 85 |
+
# On ZeroGPU Spaces, actual CUDA work may only happen inside a function
|
| 86 |
+
# decorated with @spaces.GPU (it requests physical GPU time for the call and
|
| 87 |
+
# releases it afterward). Outside of a ZeroGPU Space this decorator is a
|
| 88 |
+
# harmless no-op, so it's safe to always wrap these.
|
| 89 |
+
@spaces.GPU(duration=120)
|
| 90 |
+
def _run_stream_on_gpu(agent: ControlAIAgent, message: str, history: list[dict[str, str]]):
|
| 91 |
+
yield from agent.run_stream(message, history=history)
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
@spaces.GPU(duration=120)
|
| 95 |
+
def _run_on_gpu(agent: ControlAIAgent, message: str, history: list[dict[str, str]]):
|
| 96 |
+
return agent.run(message, history=history, verbose=False)
|
| 97 |
+
|
| 98 |
+
|
| 99 |
class ChatRequest(BaseModel):
|
| 100 |
message: str
|
| 101 |
history: list[dict[str, str]] = []
|
|
|
|
| 202 |
def event_generator():
|
| 203 |
try:
|
| 204 |
with inference_lock:
|
| 205 |
+
for event in _run_stream_on_gpu(agent, req.message.strip(), req.history):
|
| 206 |
yield f"data: {json.dumps(event, ensure_ascii=False)}\n\n"
|
| 207 |
except Exception as exc:
|
| 208 |
yield f"data: {json.dumps({'type': 'error', 'error': str(exc)}, ensure_ascii=False)}\n\n"
|
|
|
|
| 227 |
try:
|
| 228 |
agent = get_agent()
|
| 229 |
with inference_lock:
|
| 230 |
+
result = _run_on_gpu(agent, req.message.strip(), req.history)
|
| 231 |
elapsed = time.time() - t0
|
| 232 |
|
| 233 |
# Collect tool traces
|
|
|
|
| 265 |
)
|
| 266 |
|
| 267 |
|
| 268 |
+
# A minimal Gradio Blocks app is mounted (at a sub-path, not "/") purely so
|
| 269 |
+
# this Space is recognized as a Gradio SDK app -- required for ZeroGPU
|
| 270 |
+
# hardware. The real UI is still served by our own FastAPI routes above.
|
| 271 |
+
_gpu_demo = gr.Blocks()
|
| 272 |
+
with _gpu_demo:
|
| 273 |
+
gr.Markdown("ControlAI is running. Visit the Space's root URL for the full app.")
|
| 274 |
+
app = gr.mount_gradio_app(app, _gpu_demo, path="/gradio")
|
| 275 |
+
|
| 276 |
+
|
| 277 |
def main() -> None:
|
| 278 |
+
import uvicorn
|
| 279 |
+
|
| 280 |
+
if os.environ.get("SPACE_ID"):
|
| 281 |
+
port = int(os.environ.get("PORT", 7860))
|
| 282 |
+
print(f"ControlAI Web UI running on Hugging Face Spaces (port {port})...")
|
| 283 |
+
uvicorn.run(app, host="0.0.0.0", port=port, log_level="info")
|
| 284 |
+
return
|
| 285 |
+
|
| 286 |
import threading
|
| 287 |
import webbrowser
|
|
|
|
| 288 |
|
| 289 |
def _open_browser() -> None:
|
| 290 |
time.sleep(1.2)
|
requirements.txt
CHANGED
|
@@ -1,4 +1,5 @@
|
|
| 1 |
-
|
|
|
|
| 2 |
huggingface-hub>=0.23.0
|
| 3 |
torch>=2.0.0
|
| 4 |
transformers>=4.40.0
|
|
|
|
| 1 |
+
gradio>=5.9.0
|
| 2 |
+
spaces>=0.30.0
|
| 3 |
huggingface-hub>=0.23.0
|
| 4 |
torch>=2.0.0
|
| 5 |
transformers>=4.40.0
|