feat(ui): implement premium beige design system and ux refinements

This commit is contained in:
Yaojia Wang
2026-04-05 22:35:48 +02:00
parent d2b4610df9
commit 189a0fad34
30 changed files with 3651 additions and 801 deletions

View File

@@ -4,6 +4,8 @@ from __future__ import annotations
from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable
from psycopg.types.json import Json
if TYPE_CHECKING:
from psycopg_pool import AsyncConnectionPool
@@ -89,7 +91,7 @@ class PostgresAnalyticsRecorder:
"duration_ms": duration_ms,
"success": success,
"error_message": error_message,
"metadata": metadata or {},
"metadata": Json(metadata or {}),
}
async with self._pool.connection() as conn:
await conn.execute(_INSERT_SQL, params)

View File

@@ -17,7 +17,7 @@ class Settings(BaseSettings):
database_url: str
llm_provider: Literal["anthropic", "openai", "google"] = "anthropic"
llm_provider: Literal["anthropic", "openai", "azure_openai", "google"] = "anthropic"
llm_model: str = "claude-sonnet-4-6"
session_ttl_minutes: int = 30
@@ -34,6 +34,10 @@ class Settings(BaseSettings):
anthropic_api_key: str = ""
openai_api_key: str = ""
azure_openai_api_key: str = ""
azure_openai_endpoint: str = ""
azure_openai_api_version: str = "2024-12-01-preview"
azure_openai_deployment: str = ""
google_api_key: str = ""
@model_validator(mode="after")
@@ -41,6 +45,7 @@ class Settings(BaseSettings):
key_map = {
"anthropic": self.anthropic_api_key,
"openai": self.openai_api_key,
"azure_openai": self.azure_openai_api_key,
"google": self.google_api_key,
}
key = key_map.get(self.llm_provider, "")
@@ -49,4 +54,13 @@ class Settings(BaseSettings):
f"API key for provider '{self.llm_provider}' is required. "
f"Set the corresponding environment variable."
)
if self.llm_provider == "azure_openai":
if not self.azure_openai_endpoint:
raise ValueError(
"AZURE_OPENAI_ENDPOINT is required for azure_openai provider."
)
if not self.azure_openai_deployment:
raise ValueError(
"AZURE_OPENAI_DEPLOYMENT is required for azure_openai provider."
)
return self

View File

@@ -9,7 +9,7 @@ if TYPE_CHECKING:
_ENSURE_SQL = """
INSERT INTO conversations
(thread_id, started_at, last_activity)
(thread_id, created_at, last_activity)
VALUES
(%(thread_id)s, NOW(), NOW())
ON CONFLICT (thread_id) DO NOTHING

View File

@@ -31,6 +31,16 @@ def create_llm(settings: Settings) -> BaseChatModel:
api_key=settings.openai_api_key,
)
if provider == "azure_openai":
from langchain_openai import AzureChatOpenAI
return AzureChatOpenAI(
azure_deployment=settings.azure_openai_deployment,
azure_endpoint=settings.azure_openai_endpoint,
api_key=settings.azure_openai_api_key,
api_version=settings.azure_openai_api_version,
)
if provider == "google":
from langchain_google_genai import ChatGoogleGenerativeAI
@@ -39,4 +49,7 @@ def create_llm(settings: Settings) -> BaseChatModel:
google_api_key=settings.google_api_key,
)
raise ValueError(f"Unknown LLM provider: '{provider}'. Use 'anthropic', 'openai', or 'google'.")
raise ValueError(
f"Unknown LLM provider: '{provider}'. "
"Use 'anthropic', 'openai', 'azure_openai', or 'google'."
)

View File

@@ -54,7 +54,10 @@ async def handle_user_message(
interrupt_manager: InterruptManager | None = None,
) -> None:
"""Process a user message through the graph and stream results back."""
if session_manager.is_expired(thread_id):
# Touch first so new sessions are created before expiry check.
# For existing sessions, touch resets the sliding window.
existing = session_manager.get_state(thread_id)
if existing is not None and session_manager.is_expired(thread_id):
msg = "Session expired. Please start a new conversation."
await _send_json(ws, {"type": "error", "message": msg})
return