196 lines
6.3 KiB
Python
Raw Normal View History

2025-04-01 10:33:50 +02:00
import asyncio
import aiohttp
from aiohttp import web
import uuid
2025-04-01 10:56:11 +02:00
import pathlib
2025-04-25 21:18:45 +02:00
import json
2025-04-01 10:33:50 +02:00
class OllamaServer:
2025-04-01 10:56:11 +02:00
def __init__(self, ws, models):
self.ws = ws
2025-04-01 10:33:50 +02:00
self.queues = {}
2025-04-01 10:56:11 +02:00
self.models = models
2025-04-01 10:33:50 +02:00
@property
def model_names(self):
2025-04-01 10:56:11 +02:00
return [model['name'] for model in self.models]
2025-04-01 10:33:50 +02:00
async def forward_to_http(self, request_id, message):
2025-04-01 10:56:11 +02:00
if request_id not in self.queues:
2025-04-01 10:33:50 +02:00
self.queues[request_id] = asyncio.Queue()
print(message)
2025-04-01 10:33:50 +02:00
await self.queues[request_id].put(message)
2025-04-01 10:56:11 +02:00
async def forward_to_websocket(self, request_id, message, path):
2025-04-01 10:33:50 +02:00
self.queues[request_id] = asyncio.Queue()
print(path,request_id,message)
2025-04-01 10:56:11 +02:00
await self.ws.send_json(dict(request_id=request_id, data=message, path=path))
2025-04-01 10:33:50 +02:00
while True:
chunk = await self.queues[request_id].get()
2025-04-25 21:18:45 +02:00
if chunk:
yield chunk
if not chunk:
yield '\n'
print("CHUNK:", chunk)
#try:
#yield json.loads(chunk)
#except:
# yield chunk
if not 'done' in chunk:
break
2025-04-25 21:18:45 +02:00
if 'stop' in chunk:
break
if chunk['done']:
2025-04-01 10:33:50 +02:00
break
2025-04-25 21:18:45 +02:00
2025-04-01 10:33:50 +02:00
async def serve(self):
async for msg in self.ws:
if msg.type == web.WSMsgType.TEXT:
data = msg.json()
request_id = data['request_id']
await self.forward_to_http(request_id, data['data'])
elif msg.type == web.WSMsgType.ERROR:
break
2025-04-01 10:56:11 +02:00
2025-04-02 10:15:21 +02:00
class NoServerFoundException(BaseException):
pass
2025-04-01 10:33:50 +02:00
class ServerManager:
def __init__(self):
self.servers = []
def add_server(self, server):
self.servers.append(server)
def remove_server(self, server):
self.servers.remove(server)
2025-04-02 10:15:21 +02:00
def get_server_by_model_name(self, model_name):
for server in self.servers:
if model_name in server.model_names:
return server
return None
2025-04-01 10:56:11 +02:00
async def forward_to_websocket(self, request_id, message, path):
2025-04-01 10:33:50 +02:00
try:
server = self.servers.pop(0)
self.servers.append(server)
2025-04-02 10:15:21 +02:00
server = self.get_server_by_model_name(message['model'])
2025-04-25 21:18:45 +02:00
2025-04-02 10:15:21 +02:00
if not server:
raise NoServerFoundException
2025-04-01 10:56:11 +02:00
async for msg in server.forward_to_websocket(request_id, message, path):
2025-04-01 10:33:50 +02:00
yield msg
except:
2025-04-01 10:56:11 +02:00
raise
2025-04-01 10:33:50 +02:00
2025-04-02 10:15:21 +02:00
def get_models(self):
models = {}
for server in self.servers:
for model_name in server.model_names:
if not model_name in models:
2025-04-25 10:57:37 +02:00
models[model_name] = {}
models[model_name]['id'] = model_name
models[model_name]['instances'] = 0
2025-04-25 21:18:45 +02:00
models[model_name]['owned_by'] = 'uberlama'
2025-04-25 10:57:37 +02:00
models[model_name]['object'] = 'model'
2025-04-25 21:18:45 +02:00
models[model_name]['created'] = 1743724800
2025-04-25 10:57:37 +02:00
models[model_name]['instances'] += 1
2025-04-25 21:18:45 +02:00
return {
'object':"list",
'data':list(models.values())
}
2025-04-02 10:15:21 +02:00
2025-04-01 10:33:50 +02:00
server_manager = ServerManager()
async def websocket_handler(request):
ws = web.WebSocketResponse()
await ws.prepare(request)
2025-04-25 21:18:45 +02:00
await asyncio.sleep(1)
try:
models = await ws.receive_json()
except:
print("Non JSON, sleeping three seconds.")
print(request.headers)
2025-04-01 10:33:50 +02:00
2025-04-25 21:18:45 +02:00
#await ws.send_json("YOUR DATA IS CORRUPT. EXIT PROCESS!")
await ws.close()
return ws
2025-04-01 10:56:11 +02:00
server = OllamaServer(ws, models['models'])
2025-04-01 10:33:50 +02:00
server_manager.add_server(server)
2025-04-25 21:18:45 +02:00
try:
async for msg in ws:
if msg.type == web.WSMsgType.TEXT:
data = msg.json()
#print(data)
await server.forward_to_http(data['request_id'], data['data'])
elif msg.type == web.WSMsgType.ERROR:
print(f'WebSocket connection closed with exception: {ws.exception()}')
except:
print("Closing...")
await ws.close()
2025-04-01 10:33:50 +02:00
server_manager.remove_server(server)
return ws
async def http_handler(request):
request_id = str(uuid.uuid4())
data = None
try:
data = await request.json()
except ValueError:
return web.Response(status=400)
2025-04-25 21:18:45 +02:00
# application/x-ndjson text/event-stream
if data['stream']:
resp = web.StreamResponse(headers={'Content-Type': 'text/event-stream', 'Transfer-Encoding': 'chunked'})
else:
resp = web.StreamResponse(headers={'Content-Type': 'application/json', 'Transfer-Encoding': 'chunked'})
2025-04-01 10:33:50 +02:00
await resp.prepare(request)
2025-04-01 10:56:11 +02:00
import json
2025-04-02 10:15:21 +02:00
try:
async for result in server_manager.forward_to_websocket(request_id, data, path=request.path):
2025-04-25 21:18:45 +02:00
try:
a = 3
#result = json.dumps(result)
except:
pass
await resp.write(result.encode())
2025-04-02 10:15:21 +02:00
except NoServerFoundException:
2025-04-08 10:45:45 +02:00
await resp.write(json.dumps(dict(error="No server with that model found.",available=server_manager.get_models())).encode() + b'\r\n')
2025-04-01 10:33:50 +02:00
await resp.write_eof()
return resp
async def index_handler(request):
index_template = pathlib.Path("index.html").read_text()
client_py = pathlib.Path("client.py").read_text()
index_template = index_template.replace("#client.py", client_py)
return web.Response(text=index_template, content_type="text/html")
2025-04-16 19:50:41 +02:00
async def not_found_handler(request):
print("not found:",request.path)
return web.json_response({"error":"not found"})
2025-04-02 10:15:21 +02:00
async def models_handler(self):
2025-04-25 21:18:45 +02:00
print("Listing models.")
response_json = json.dumps(server_manager.get_models(),indent=2)
return web.Response(text=response_json,content_type="application/json")
2025-04-02 10:15:21 +02:00
2025-04-01 10:33:50 +02:00
app = web.Application()
app.router.add_get("/", index_handler)
app.router.add_route('GET', '/publish', websocket_handler)
app.router.add_route('POST', '/api/chat', http_handler)
2025-04-16 19:50:41 +02:00
app.router.add_route('POST', '/v1/chat', http_handler)
app.router.add_route('POST', '/v1/completions', http_handler)
app.router.add_route('POST', '/v1/chat/completions', http_handler)
2025-04-02 10:15:21 +02:00
app.router.add_route('GET', '/models', models_handler)
2025-04-25 10:57:37 +02:00
app.router.add_route('GET', '/v1/models', models_handler)
2025-04-16 19:50:41 +02:00
app.router.add_route('*', '/{tail:.*}', not_found_handler)
2025-04-01 10:33:50 +02:00
if __name__ == '__main__':
2025-04-01 10:56:11 +02:00
web.run_app(app, port=1984)