From 75c5c2db63de6fc519897318c39d1ebeec0c055b Mon Sep 17 00:00:00 2001 From: Charlotte Som Date: Tue, 25 Feb 2025 17:39:29 +0000 Subject: [PATCH] initial commit --- .gitignore | 2 ++ README.md | 7 +++++++ requirements.txt | Bin 0 -> 136 bytes server/__init__.py | 11 +++++++++++ server/http.py | 5 +++++ server/inference.py | 33 +++++++++++++++++++++++++++++++++ 6 files changed, 58 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 requirements.txt create mode 100644 server/__init__.py create mode 100644 server/http.py create mode 100644 server/inference.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3f32964 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/.venv +__pycache__ diff --git a/README.md b/README.md new file mode 100644 index 0000000..dbb0ea6 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# llm-ui + +```shell +$ uv venv +$ uv pip install -r requirements.txt +$ uv run uvicorn server:app +``` diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..620b0c7e037c17e90338a4fd7a840be91da1124f GIT binary patch literal 136 zcmX}kOA3H63`Nm97F>!_Y!N3eM#Mp(Dt?!*K8uz&2uV(IUxC0#s~AcuB`0HJ(cH5$ oP;jVv^;>jS&eD8tr(@M_ka_k0$x+Lhs18ou_so)05*C-l18p!BEC2ui literal 0 HcmV?d00001 diff --git a/server/__init__.py b/server/__init__.py new file mode 100644 index 0000000..935c59b --- /dev/null +++ b/server/__init__.py @@ -0,0 +1,11 @@ +from server.http import Starlette, Route, Request, Response, JSONResponse, WebSocketRoute +from server.inference import list_conversations, connect_to_conversation + +async def status(request: Request) -> Response: + return JSONResponse({"status": "ok"}) + +app = Starlette(debug=True, routes=[ + Route("/api/", status), + Route("/api/conversation", list_conversations, methods=["GET"]), + WebSocketRoute("/api/conversation/:conversation/connect", connect_to_conversation) +]) diff --git a/server/http.py b/server/http.py new file mode 100644 index 0000000..e0572b0 --- /dev/null +++ b/server/http.py @@ -0,0 +1,5 @@ +from starlette.applications import Starlette +from starlette.routing import Route, WebSocketRoute +from starlette.responses import * +from starlette.requests import * +from starlette.websockets import WebSocket diff --git a/server/inference.py b/server/inference.py new file mode 100644 index 0000000..0c1fd0d --- /dev/null +++ b/server/inference.py @@ -0,0 +1,33 @@ +import llm, llm.cli, sqlite_utils +from .http import Request, JSONResponse, WebSocket, RedirectResponse +import json + +db = sqlite_utils.Database(llm.cli.logs_db_path()) + +async def list_conversations(request: Request): + conversations = [] + for row in db["conversations"].rows: + conversations.append({ "id": row["id"], "name": row["name"] }) + return JSONResponse(conversations) + +girlypop_prompt = llm.cli.load_template("girlypop").system + +async def connect_to_conversation(ws: WebSocket): + conversation_id = ws.path_params["conversation"] + if conversation_id == "new": + conversation = llm.AsyncConversation(llm.get_async_model()) + else: + try: + conversation: llm.AsyncConversation = llm.cli.load_conversation(conversation_id, async_=True) + except: + await ws.send_denial_response(JSONResponse({ + "error": "unable to load conversation {}".format(conversation_id) + })) + return + + await ws.accept() + async for message in ws.iter_text(): + response = conversation.prompt(message, system=girlypop_prompt) + async for chunk in response: + ws.send_text(json.dumps({"c": chunk})) + ws.send_text(json.dumps({"d": True})) # done