Spaces:
Runtime error
Runtime error
tech-envision
commited on
Commit
Β·
4f46c78
1
Parent(s):
c4aea9f
Add VM file management API endpoints
Browse files- README.md +4 -0
- api_app/__init__.py +68 -2
README.md
CHANGED
@@ -91,6 +91,10 @@ uvicorn api_app:app --host 0.0.0.0 --port 8000
|
|
91 |
- `POST /chat/stream` β stream the assistant's response as plain text.
|
92 |
- `POST /upload` β upload a document that can be referenced in chats.
|
93 |
- `GET /sessions/{user}` β list available session names for a user.
|
|
|
|
|
|
|
|
|
94 |
|
95 |
Example request:
|
96 |
|
|
|
91 |
- `POST /chat/stream` β stream the assistant's response as plain text.
|
92 |
- `POST /upload` β upload a document that can be referenced in chats.
|
93 |
- `GET /sessions/{user}` β list available session names for a user.
|
94 |
+
- `GET /vm/{user}/list` β list files in a directory under `/data`.
|
95 |
+
- `GET /vm/{user}/file` β read a file from the VM.
|
96 |
+
- `POST /vm/{user}/file` β create or overwrite a file in the VM.
|
97 |
+
- `DELETE /vm/{user}/file` β delete a file or directory from the VM.
|
98 |
|
99 |
Example request:
|
100 |
|
api_app/__init__.py
CHANGED
@@ -1,14 +1,17 @@
|
|
1 |
from __future__ import annotations
|
2 |
|
3 |
-
from fastapi import FastAPI, UploadFile, File, Form
|
4 |
from fastapi.responses import StreamingResponse
|
5 |
-
from fastapi import HTTPException
|
6 |
from fastapi.middleware.cors import CORSMiddleware
|
7 |
from pydantic import BaseModel
|
8 |
import asyncio
|
9 |
import os
|
10 |
import tempfile
|
11 |
from pathlib import Path
|
|
|
|
|
|
|
|
|
12 |
|
13 |
from src.team import TeamChatSession
|
14 |
from src.log import get_logger
|
@@ -24,6 +27,26 @@ class ChatRequest(BaseModel):
|
|
24 |
prompt: str
|
25 |
|
26 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
def create_app() -> FastAPI:
|
28 |
app = FastAPI(title="LLM Backend API")
|
29 |
|
@@ -84,6 +107,49 @@ def create_app() -> FastAPI:
|
|
84 |
async def health():
|
85 |
return {"status": "ok"}
|
86 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
87 |
return app
|
88 |
|
89 |
|
|
|
1 |
from __future__ import annotations
|
2 |
|
3 |
+
from fastapi import FastAPI, UploadFile, File, Form, HTTPException
|
4 |
from fastapi.responses import StreamingResponse
|
|
|
5 |
from fastapi.middleware.cors import CORSMiddleware
|
6 |
from pydantic import BaseModel
|
7 |
import asyncio
|
8 |
import os
|
9 |
import tempfile
|
10 |
from pathlib import Path
|
11 |
+
from typing import List
|
12 |
+
import shutil
|
13 |
+
|
14 |
+
from src.config import UPLOAD_DIR
|
15 |
|
16 |
from src.team import TeamChatSession
|
17 |
from src.log import get_logger
|
|
|
27 |
prompt: str
|
28 |
|
29 |
|
30 |
+
class FileWriteRequest(BaseModel):
|
31 |
+
path: str
|
32 |
+
content: str
|
33 |
+
|
34 |
+
|
35 |
+
def _vm_host_path(user: str, vm_path: str) -> Path:
|
36 |
+
"""Return the host path for a given ``vm_path`` inside ``/data``."""
|
37 |
+
|
38 |
+
try:
|
39 |
+
rel = Path(vm_path).relative_to("/data")
|
40 |
+
except ValueError as exc: # pragma: no cover - invalid path
|
41 |
+
raise HTTPException(status_code=400, detail="Path must start with /data") from exc
|
42 |
+
|
43 |
+
base = (Path(UPLOAD_DIR) / user).resolve()
|
44 |
+
target = (base / rel).resolve()
|
45 |
+
if not target.is_relative_to(base):
|
46 |
+
raise HTTPException(status_code=400, detail="Invalid path")
|
47 |
+
return target
|
48 |
+
|
49 |
+
|
50 |
def create_app() -> FastAPI:
|
51 |
app = FastAPI(title="LLM Backend API")
|
52 |
|
|
|
107 |
async def health():
|
108 |
return {"status": "ok"}
|
109 |
|
110 |
+
@app.get("/vm/{user}/list")
|
111 |
+
async def list_vm_dir(user: str, path: str = "/data"):
|
112 |
+
target = _vm_host_path(user, path)
|
113 |
+
if not target.exists():
|
114 |
+
raise HTTPException(status_code=404, detail="Directory not found")
|
115 |
+
if not target.is_dir():
|
116 |
+
raise HTTPException(status_code=400, detail="Not a directory")
|
117 |
+
entries: List[dict[str, str | bool]] = []
|
118 |
+
for entry in sorted(target.iterdir()):
|
119 |
+
entries.append({"name": entry.name, "is_dir": entry.is_dir()})
|
120 |
+
return {"entries": entries}
|
121 |
+
|
122 |
+
@app.get("/vm/{user}/file")
|
123 |
+
async def read_vm_file(user: str, path: str):
|
124 |
+
target = _vm_host_path(user, path)
|
125 |
+
if not target.exists():
|
126 |
+
raise HTTPException(status_code=404, detail="File not found")
|
127 |
+
if target.is_dir():
|
128 |
+
raise HTTPException(status_code=400, detail="Path is a directory")
|
129 |
+
try:
|
130 |
+
content = target.read_text()
|
131 |
+
except UnicodeDecodeError:
|
132 |
+
raise HTTPException(status_code=400, detail="Binary file not supported")
|
133 |
+
return {"content": content}
|
134 |
+
|
135 |
+
@app.post("/vm/{user}/file")
|
136 |
+
async def write_vm_file(user: str, req: FileWriteRequest):
|
137 |
+
target = _vm_host_path(user, req.path)
|
138 |
+
target.parent.mkdir(parents=True, exist_ok=True)
|
139 |
+
target.write_text(req.content)
|
140 |
+
return {"status": "ok"}
|
141 |
+
|
142 |
+
@app.delete("/vm/{user}/file")
|
143 |
+
async def delete_vm_file(user: str, path: str):
|
144 |
+
target = _vm_host_path(user, path)
|
145 |
+
if target.is_dir():
|
146 |
+
shutil.rmtree(target)
|
147 |
+
elif target.exists():
|
148 |
+
target.unlink()
|
149 |
+
else:
|
150 |
+
raise HTTPException(status_code=404, detail="File not found")
|
151 |
+
return {"status": "deleted"}
|
152 |
+
|
153 |
return app
|
154 |
|
155 |
|