Spaces:
Paused
Paused
Update app.py
Browse files
app.py
CHANGED
|
@@ -3,13 +3,15 @@ import gradio as gr
|
|
| 3 |
import subprocess, os, sys, time
|
| 4 |
import urllib.request, urllib.error
|
| 5 |
from fastapi import Request
|
| 6 |
-
from fastapi.responses import Response, PlainTextResponse, HTMLResponse
|
| 7 |
|
| 8 |
PORT = 9001
|
| 9 |
FM_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "file-manager")
|
| 10 |
MODEL_ID = "Qwen/Qwen2.5-0.5B-Instruct"
|
| 11 |
|
| 12 |
-
#
|
|
|
|
|
|
|
| 13 |
proc = {"p": None}
|
| 14 |
def start_fm():
|
| 15 |
if proc["p"] and proc["p"].poll() is None:
|
|
@@ -23,7 +25,9 @@ def start_fm():
|
|
| 23 |
|
| 24 |
start_fm()
|
| 25 |
|
| 26 |
-
#
|
|
|
|
|
|
|
| 27 |
@spaces.GPU
|
| 28 |
def gpu_report():
|
| 29 |
import torch
|
|
@@ -41,7 +45,9 @@ def gpu_report():
|
|
| 41 |
lines.append(f"nvidia-smi error: {e}")
|
| 42 |
return "\n".join(lines)
|
| 43 |
|
| 44 |
-
#
|
|
|
|
|
|
|
| 45 |
_state = {"model": None, "tok": None}
|
| 46 |
|
| 47 |
@spaces.GPU
|
|
@@ -60,9 +66,11 @@ def run_llm(prompt: str):
|
|
| 60 |
reply = tok.decode(out[0][inputs.input_ids.shape[1]:], skip_special_tokens=True)
|
| 61 |
return f"[GPU: {torch.cuda.get_device_name(0)}]\n\n{reply}"
|
| 62 |
|
| 63 |
-
#
|
|
|
|
|
|
|
| 64 |
with gr.Blocks() as demo:
|
| 65 |
-
gr.Markdown("Self-editing computer. File manager + terminal at /fm")
|
| 66 |
with gr.Row():
|
| 67 |
prompt = gr.Textbox(label="Ask the LLM")
|
| 68 |
out = gr.Textbox(label="Reply")
|
|
@@ -74,26 +82,36 @@ demo.queue()
|
|
| 74 |
demo.launch(server_name="0.0.0.0", server_port=7860,
|
| 75 |
ssr_mode=False, prevent_thread_lock=True)
|
| 76 |
|
| 77 |
-
app = demo.app
|
| 78 |
|
| 79 |
-
#
|
|
|
|
|
|
|
| 80 |
@app.get("/reload")
|
| 81 |
def reload_fm():
|
| 82 |
start_fm()
|
| 83 |
time.sleep(1)
|
| 84 |
return HTMLResponse('Reloaded file-manager process. <a href="/fm/">back to /fm</a>')
|
| 85 |
|
| 86 |
-
#
|
|
|
|
|
|
|
| 87 |
@app.get("/gpuinfo")
|
| 88 |
def gpuinfo():
|
| 89 |
return PlainTextResponse(gpu_report())
|
| 90 |
|
| 91 |
-
#
|
|
|
|
|
|
|
| 92 |
@app.get("/llm")
|
| 93 |
def llm(q: str = "Say hello and tell me which GPU you are running on."):
|
| 94 |
return PlainTextResponse(run_llm(q))
|
| 95 |
|
| 96 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
| 97 |
@app.api_route("/fm", methods=["GET", "POST"])
|
| 98 |
@app.api_route("/fm/{path:path}", methods=["GET", "POST"])
|
| 99 |
async def proxy(request: Request, path: str = ""):
|
|
@@ -104,16 +122,33 @@ async def proxy(request: Request, path: str = ""):
|
|
| 104 |
fwd = {k: v for k, v in request.headers.items() if k.lower() != "host"}
|
| 105 |
req = urllib.request.Request(url, data=body or None, method=request.method, headers=fwd)
|
| 106 |
try:
|
| 107 |
-
|
| 108 |
-
data, status, headers = r.read(), r.status, r.headers
|
| 109 |
except urllib.error.HTTPError as e:
|
| 110 |
-
|
|
|
|
|
|
|
| 111 |
except urllib.error.URLError:
|
| 112 |
return Response(content=b"file-manager not up yet on 9001", status_code=502)
|
| 113 |
-
out = {k: v for k, v in headers.items()
|
| 114 |
-
if k.lower() in ("set-cookie", "location", "content-type")}
|
| 115 |
-
return Response(content=data, status_code=status, headers=out)
|
| 116 |
|
| 117 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 118 |
while True:
|
| 119 |
time.sleep(3600)
|
|
|
|
| 3 |
import subprocess, os, sys, time
|
| 4 |
import urllib.request, urllib.error
|
| 5 |
from fastapi import Request
|
| 6 |
+
from fastapi.responses import Response, PlainTextResponse, HTMLResponse, StreamingResponse
|
| 7 |
|
| 8 |
PORT = 9001
|
| 9 |
FM_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "file-manager")
|
| 10 |
MODEL_ID = "Qwen/Qwen2.5-0.5B-Instruct"
|
| 11 |
|
| 12 |
+
# =================================================================
|
| 13 |
+
# Launch the file manager as a background child process
|
| 14 |
+
# =================================================================
|
| 15 |
proc = {"p": None}
|
| 16 |
def start_fm():
|
| 17 |
if proc["p"] and proc["p"].poll() is None:
|
|
|
|
| 25 |
|
| 26 |
start_fm()
|
| 27 |
|
| 28 |
+
# =================================================================
|
| 29 |
+
# GPU info: runs nvidia-smi INSIDE the gate (GPU only attached here)
|
| 30 |
+
# =================================================================
|
| 31 |
@spaces.GPU
|
| 32 |
def gpu_report():
|
| 33 |
import torch
|
|
|
|
| 45 |
lines.append(f"nvidia-smi error: {e}")
|
| 46 |
return "\n".join(lines)
|
| 47 |
|
| 48 |
+
# =================================================================
|
| 49 |
+
# LLM inference: model loads + generates INSIDE the gate
|
| 50 |
+
# =================================================================
|
| 51 |
_state = {"model": None, "tok": None}
|
| 52 |
|
| 53 |
@spaces.GPU
|
|
|
|
| 66 |
reply = tok.decode(out[0][inputs.input_ids.shape[1]:], skip_special_tokens=True)
|
| 67 |
return f"[GPU: {torch.cuda.get_device_name(0)}]\n\n{reply}"
|
| 68 |
|
| 69 |
+
# =================================================================
|
| 70 |
+
# Gradio owns the ONLY public port 7860
|
| 71 |
+
# =================================================================
|
| 72 |
with gr.Blocks() as demo:
|
| 73 |
+
gr.Markdown("Self-editing computer. File manager + terminal + Python REPL at /fm")
|
| 74 |
with gr.Row():
|
| 75 |
prompt = gr.Textbox(label="Ask the LLM")
|
| 76 |
out = gr.Textbox(label="Reply")
|
|
|
|
| 82 |
demo.launch(server_name="0.0.0.0", server_port=7860,
|
| 83 |
ssr_mode=False, prevent_thread_lock=True)
|
| 84 |
|
| 85 |
+
app = demo.app # Gradio's FastAPI app on 7860
|
| 86 |
|
| 87 |
+
# =================================================================
|
| 88 |
+
# Hot-reload: respawn ONLY the file-manager process, no Space rebuild
|
| 89 |
+
# =================================================================
|
| 90 |
@app.get("/reload")
|
| 91 |
def reload_fm():
|
| 92 |
start_fm()
|
| 93 |
time.sleep(1)
|
| 94 |
return HTMLResponse('Reloaded file-manager process. <a href="/fm/">back to /fm</a>')
|
| 95 |
|
| 96 |
+
# =================================================================
|
| 97 |
+
# GPU info route
|
| 98 |
+
# =================================================================
|
| 99 |
@app.get("/gpuinfo")
|
| 100 |
def gpuinfo():
|
| 101 |
return PlainTextResponse(gpu_report())
|
| 102 |
|
| 103 |
+
# =================================================================
|
| 104 |
+
# LLM route
|
| 105 |
+
# =================================================================
|
| 106 |
@app.get("/llm")
|
| 107 |
def llm(q: str = "Say hello and tell me which GPU you are running on."):
|
| 108 |
return PlainTextResponse(run_llm(q))
|
| 109 |
|
| 110 |
+
# =================================================================
|
| 111 |
+
# Proxy: /fm/... -> 127.0.0.1:9001/fm/...
|
| 112 |
+
# Streams text/plain (live shell) chunk by chunk; buffers everything else.
|
| 113 |
+
# Forwards method + body so POST forms and the JSON REPL work.
|
| 114 |
+
# =================================================================
|
| 115 |
@app.api_route("/fm", methods=["GET", "POST"])
|
| 116 |
@app.api_route("/fm/{path:path}", methods=["GET", "POST"])
|
| 117 |
async def proxy(request: Request, path: str = ""):
|
|
|
|
| 122 |
fwd = {k: v for k, v in request.headers.items() if k.lower() != "host"}
|
| 123 |
req = urllib.request.Request(url, data=body or None, method=request.method, headers=fwd)
|
| 124 |
try:
|
| 125 |
+
r = urllib.request.urlopen(req)
|
|
|
|
| 126 |
except urllib.error.HTTPError as e:
|
| 127 |
+
return Response(
|
| 128 |
+
content=e.read(), status_code=e.code,
|
| 129 |
+
headers={"content-type": e.headers.get("content-type", "text/plain")})
|
| 130 |
except urllib.error.URLError:
|
| 131 |
return Response(content=b"file-manager not up yet on 9001", status_code=502)
|
|
|
|
|
|
|
|
|
|
| 132 |
|
| 133 |
+
ctype = r.headers.get("content-type", "text/html")
|
| 134 |
+
# Stream the live shell output; buffer normal pages
|
| 135 |
+
if ctype.startswith("text/plain"):
|
| 136 |
+
def gen():
|
| 137 |
+
while True:
|
| 138 |
+
chunk = r.read(1024)
|
| 139 |
+
if not chunk:
|
| 140 |
+
break
|
| 141 |
+
yield chunk
|
| 142 |
+
return StreamingResponse(gen(), media_type="text/plain")
|
| 143 |
+
|
| 144 |
+
out_headers = {"content-type": ctype}
|
| 145 |
+
for k, v in r.headers.items():
|
| 146 |
+
if k.lower() in ("set-cookie", "location"):
|
| 147 |
+
out_headers[k] = v
|
| 148 |
+
return Response(content=r.read(), status_code=r.status, headers=out_headers)
|
| 149 |
+
|
| 150 |
+
# =================================================================
|
| 151 |
+
# Keep the process alive (launch returned control)
|
| 152 |
+
# =================================================================
|
| 153 |
while True:
|
| 154 |
time.sleep(3600)
|