refactor: fix architectural issues across frontend and backend

Address all architecture review findings:

P0 fixes:
- Add API key authentication for admin endpoints (analytics, replay, openapi)
  and WebSocket connections via ADMIN_API_KEY env var
- Add PostgreSQL-backed PgSessionManager and PgInterruptManager for
  multi-worker production deployments (in-memory defaults preserved)

P1 fixes:
- Implement actual tool generation in OpenAPI approve_job endpoint
  using generate_tool_code() and generate_agent_yaml()
- Add missing clarification, interrupt_expired, and tool_result message
  handlers in frontend ChatPage

P2 fixes:
- Replace monkey-patching on CompiledStateGraph with typed GraphContext
- Replace 9-param dispatch_message with WebSocketContext dataclass
- Extract duplicate _envelope() into shared app/api_utils.py
- Replace mutable module-level counter with crypto.randomUUID()
- Remove hardcoded mock data from ReviewPage, use api.ts wrappers
- Remove `as any` type escape from ReplayPage

All 516 tests passing, 0 TypeScript errors.
This commit is contained in:
Yaojia Wang
2026-04-06 15:59:14 +02:00
parent b8654aa31f
commit af53111928
29 changed files with 1183 additions and 473 deletions

View File

@@ -13,10 +13,12 @@ from httpx import ASGITransport, AsyncClient
from app.analytics.api import router as analytics_router
from app.callbacks import TokenUsageCallbackHandler
from app.graph_context import GraphContext
from app.interrupt_manager import InterruptManager
from app.openapi.review_api import _job_store, router as openapi_router
from app.replay.api import router as replay_router
from app.session_manager import SessionManager
from app.ws_context import WebSocketContext
from app.ws_handler import dispatch_message
@@ -74,8 +76,6 @@ def make_graph(
) -> MagicMock:
"""Build a mock LangGraph CompiledStateGraph."""
g = MagicMock()
g.intent_classifier = None
g.agent_registry = None
if state is None:
state = make_state()
@@ -93,6 +93,14 @@ def make_graph(
return g
def make_graph_ctx(graph: MagicMock | None = None) -> GraphContext:
"""Build a GraphContext wrapping a mock graph."""
g = graph or make_graph()
registry = MagicMock()
registry.list_agents = MagicMock(return_value=())
return GraphContext(graph=g, registry=registry, intent_classifier=None)
# ---------------------------------------------------------------------------
# Fake database pool
# ---------------------------------------------------------------------------
@@ -148,6 +156,7 @@ def create_e2e_app(
) -> FastAPI:
"""Create a FastAPI app wired with mocked dependencies for E2E testing."""
g = graph or make_graph()
graph_ctx = make_graph_ctx(g)
p = pool or FakePool()
sm = SessionManager(session_ttl_seconds=session_ttl)
im = InterruptManager(ttl_seconds=interrupt_ttl)
@@ -157,7 +166,7 @@ def create_e2e_app(
app.include_router(replay_router)
app.include_router(analytics_router)
app.state.graph = g
app.state.graph_ctx = graph_ctx
app.state.session_manager = sm
app.state.interrupt_manager = im
app.state.pool = p
@@ -175,17 +184,16 @@ def create_e2e_app(
try:
while True:
raw_data = await ws.receive_text()
await dispatch_message(
ws,
app.state.graph,
app.state.session_manager,
TokenUsageCallbackHandler(model_name="test-model"),
raw_data,
ws_ctx = WebSocketContext(
graph_ctx=app.state.graph_ctx,
session_manager=app.state.session_manager,
callback_handler=TokenUsageCallbackHandler(model_name="test-model"),
interrupt_manager=app.state.interrupt_manager,
analytics_recorder=app.state.analytics_recorder,
conversation_tracker=app.state.conversation_tracker,
pool=app.state.pool,
)
await dispatch_message(ws, ws_ctx, raw_data)
except WebSocketDisconnect:
pass