initial commit

This commit is contained in:
Charlotte Som 2025-02-25 17:39:29 +00:00
commit 75c5c2db63
6 changed files with 58 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
/.venv
__pycache__

7
README.md Normal file
View file

@ -0,0 +1,7 @@
# llm-ui
```shell
$ uv venv
$ uv pip install -r requirements.txt
$ uv run uvicorn server:app
```

BIN
requirements.txt Normal file

Binary file not shown.

11
server/__init__.py Normal file
View file

@ -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)
])

5
server/http.py Normal file
View file

@ -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

33
server/inference.py Normal file
View file

@ -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