aboutsummaryrefslogtreecommitdiff
path: root/agent/orchestrator/api.py
blob: 39d14092903b3fedb9ac27709e8a71b1c595133a (plain)
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
"""OpenAI-compatible HTTP API for Sénia.

Open WebUI (and other OpenAI-API clients) talks to this endpoint set. We accept
the standard `/v1/chat/completions` shape, translate to internal types, hand
off to the orchestrator, and translate the response back to OpenAI shape.
"""
import asyncio
from contextlib import asynccontextmanager

from fastapi import FastAPI, HTTPException, Request
from pydantic import BaseModel, ConfigDict

from .loop import handle_turn
from .matrix_bot import run as matrix_run
from .models import Message


@asynccontextmanager
async def lifespan(app: FastAPI):
    task = asyncio.create_task(matrix_run())
    try:
        yield
    finally:
        task.cancel()


app = FastAPI(title="Sénia", lifespan=lifespan)


class ChatMessage(BaseModel):
    role: str
    content: str


class ChatCompletionRequest(BaseModel):
    model_config = ConfigDict(extra="allow")
    model: str
    messages: list[ChatMessage]
    user: str | None = None


@app.get("/v1/models")
def list_models():
    return {
        "object": "list",
        "data": [{"id": "senia", "object": "model", "owned_by": "local"}],
    }


@app.post("/v1/chat/completions")
def chat_completions(req: ChatCompletionRequest, request: Request):
    # Open WebUI sends user info via custom headers when ENABLE_FORWARD_USER_INFO_HEADERS=true
    user_name = request.headers.get("x-openwebui-user-name")
    user_email = request.headers.get("x-openwebui-user-email")
    # prefer name (friendlier), fall back to email, fall back to None
    user = user_name or user_email
    try:
        response_text = handle_turn(
            messages=[Message(role=m.role, content=m.content) for m in req.messages],
            user=user,
        )
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))
    return {
        "id": "chat-1",
        "object": "chat.completion",
        "model": req.model,
        "choices": [
            {
                "index": 0,
                "message": {"role": "assistant", "content": response_text},
                "finish_reason": "stop",
            }
        ],
    }