Spaces:
Runtime error
Runtime error
File size: 5,348 Bytes
dd07930 76ca3e9 dd07930 76ca3e9 49a61bb 76ca3e9 dd07930 76ca3e9 49a61bb 76ca3e9 49a61bb 76ca3e9 dd07930 49a61bb dd07930 76ca3e9 dd07930 76ca3e9 dd07930 76ca3e9 dd07930 76ca3e9 dd07930 49a61bb dd07930 76ca3e9 dd07930 76ca3e9 dd07930 76ca3e9 dd07930 76ca3e9 dd07930 76ca3e9 dd07930 76ca3e9 dd07930 76ca3e9 dd07930 76ca3e9 dd07930 76ca3e9 dd07930 76ca3e9 dd07930 49a61bb dd07930 49a61bb |
1 2 3 4 5 6 7 8 9 10 11 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 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 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 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
# server.py
from dataclasses import dataclass, asdict
import secrets
import logging
import asyncio
import json
from typing import Tuple
from quart import Quart, websocket, request
from quart_schema import QuartSchema, validate_request, validate_response
from quart_cors import cors
from uvicorn.middleware.proxy_headers import ProxyHeadersMiddleware
from broker import SessionBroker, SessionDoesNotExist, ClientRequest, ClientResponse, ClientError
# Configuración
VERSION = "1.0.0" # Versión de la API
TIMEOUT: int = 40
LOG_LEVEL: int = logging.DEBUG
TRUSTED_HOSTS: list[str] = ["127.0.0.1", "10.16.38.136", "10.16.3.13", "10.16.13.73"]
# Create app
app = Quart(__name__)
app = cors(app,
allow_origin=["https://*.hf.space", "https://*.huggingface.co"],
allow_methods=["GET", "POST", "OPTIONS"],
allow_headers=["Content-Type"],
max_age=3600
)
QuartSchema(app)
app.asgi_app = ProxyHeadersMiddleware(app.asgi_app, trusted_hosts=TRUSTED_HOSTS)
app.logger.setLevel(LOG_LEVEL)
broker = SessionBroker()
# Modelos de datos
@dataclass
class Status:
status: str
version: str
@dataclass
class Session:
session_id: str
@dataclass
class Command:
session_id: str
command: str
@dataclass
class Read:
session_id: str
path: str
@dataclass
class Write:
session_id: str
path: str
content: str
@dataclass
class CommandResponse:
return_code: int
stdout: str
stderr: str
@dataclass
class ReadResponse:
content: str
@dataclass
class WriteResponse:
size: int
@dataclass
class ErrorResponse:
error: str
# Rutas API
@app.get("/status")
@validate_response(Status)
async def status() -> Status:
return Status(status="OK", version=VERSION)
@app.websocket('/session')
async def session_handler():
session_id = secrets.token_hex()
app.logger.info(f"{websocket.remote_addr} - NEW SESSION - {session_id}")
await websocket.send_as(Session(session_id=session_id), Session)
task = None
try:
task = asyncio.ensure_future(_receive(session_id))
async for request in broker.subscribe(session_id):
app.logger.info(f"{websocket.remote_addr} - REQUEST - {session_id} - {json.dumps(asdict(request))}")
await websocket.send_as(request, ClientRequest)
finally:
if task is not None:
task.cancel()
await task
async def _receive(session_id: str) -> None:
while True:
response = await websocket.receive_as(ClientResponse)
app.logger.info(f"{websocket.remote_addr} - RESPONSE - {session_id} - {json.dumps(asdict(response))}")
await broker.receive_response(session_id, response)
@app.post('/command')
@validate_request(Command)
@validate_response(CommandResponse, 200)
@validate_response(ErrorResponse, 500)
async def command(data: Command) -> Tuple[CommandResponse | ErrorResponse, int]:
try:
response = CommandResponse(**await broker.send_request(
data.session_id,
{'action': 'command', 'command': data.command},
timeout=TIMEOUT))
return response, 200
except SessionDoesNotExist:
app.logger.warning(f"{request.remote_addr} - INVALID SESSION ID - {repr(data.session_id)}")
return ErrorResponse('Session does not exist.'), 500
except ClientError as e:
return ErrorResponse(e.message), 500
except asyncio.TimeoutError:
return ErrorResponse('Timeout when waiting for client.'), 500
@app.post('/read')
@validate_request(Read)
@validate_response(ReadResponse, 200)
@validate_response(ErrorResponse, 500)
async def read(data: Read) -> Tuple[ReadResponse | ErrorResponse, int]:
try:
response = ReadResponse(**await broker.send_request(
data.session_id,
{'action': 'read', 'path': data.path},
timeout=TIMEOUT))
return response, 200
except SessionDoesNotExist:
app.logger.warning(f"{request.remote_addr} - INVALID SESSION ID - {repr(data.session_id)}")
return ErrorResponse('Session does not exist.'), 500
except ClientError as e:
return ErrorResponse(e.message), 500
except asyncio.TimeoutError:
return ErrorResponse('Timeout when waiting for client.'), 500
@app.post('/write')
@validate_request(Write)
@validate_response(WriteResponse, 200)
@validate_response(ErrorResponse, 500)
async def write(data: Write) -> Tuple[WriteResponse | ErrorResponse, int]:
try:
response = WriteResponse(**await broker.send_request(
data.session_id,
{'action': 'write', 'path': data.path, 'content': data.content},
timeout=TIMEOUT))
return response, 200
except SessionDoesNotExist:
app.logger.warning(f"{request.remote_addr} - INVALID SESSION ID - {repr(data.session_id)}")
return ErrorResponse('Session does not exist.'), 500
except ClientError as e:
return ErrorResponse(e.message), 500
except asyncio.TimeoutError:
return ErrorResponse('Timeout when waiting for client.'), 500
# Agregar un endpoint de health check y root
@app.route("/")
async def root():
return {"message": "Kaio API Server", "version": VERSION}
@app.route("/health")
async def health_check():
return {"status": "healthy"}
def run():
app.run(host='0.0.0.0', port=7860)
if __name__ == "__main__":
run() |