feat: one gateway port with path-mounted frontends, markdown chat, collapsible sidebars

This commit is contained in:
hh
2026-08-29 00:49:06 +02:00
parent 540327efa1
commit 16b6bbddda
32 changed files with 691 additions and 441 deletions
@@ -33,8 +33,6 @@ shape.
from __future__ import annotations
import asyncio
import contextlib
import json
import logging
import time
@@ -107,31 +105,22 @@ class MarkdownFrontend(Frontend):
name = FRONTEND
kinds = ("deep",)
path = "/md"
def __init__(
self,
*,
vault_path: Path | str,
host: str = "0.0.0.0", # noqa: S104
port: int = 8003,
default_agent: str | None = None,
log_all_chats: bool = False,
logged_subdir: str = "_logs",
chat_path: Callable[[str, str, Path], Path] | None = None,
public_base_url: str | None = None,
) -> None:
self.vault_path = Path(vault_path).expanduser().resolve()
self.host = host
self.port = port
self.default_agent = default_agent
self.log_all_chats = log_all_chats
self.logged_subdir = logged_subdir
self.chat_path = chat_path
# External URL prefix when behind a reverse proxy — same role as
# on the other bearer frontends. Trailing slash trimmed for
# idempotent concatenation; ``None`` means "no proxy / advertise
# raw host:port".
self.public_base_url = public_base_url.rstrip("/") if public_base_url else None
self._runtime: GatewayRuntime | None = None
self._app: FastAPI | None = None
# Files currently being processed by an in-flight ``POST /chat``.
@@ -180,23 +169,11 @@ class MarkdownFrontend(Frontend):
async def materialize(self, conv: Conversation) -> ConversationBinding | None:
return await self.mirror.materialize(conv)
async def serve(self) -> None:
import uvicorn
def app(self) -> FastAPI | None:
return self._app
if self._app is None:
msg = "configure() must be called before serve()"
raise RuntimeError(msg)
config = uvicorn.Config(
self._app, host=self.host, port=self.port, log_level="info"
)
server = uvicorn.Server(config)
mirror = asyncio.create_task(self.mirror.run())
try:
await server.serve()
finally:
mirror.cancel()
with contextlib.suppress(asyncio.CancelledError):
await mirror
async def serve(self) -> None:
await self.mirror.run()
# ---- app builder ---------------------------------------------------