- New backend/app/mcp/ module: MCPServer instance with a single
search_regulations tool backed by the existing AgentConversationService.
- MCPAuthMiddleware reuses existing JWT auth (no new auth mechanism).
- Mounted at /mcp/ in api/main.py via Streamable HTTP transport; wired the
MCP session manager into the existing lifespan() via AsyncExitStack
(app.mount() does not propagate nested ASGI lifespans automatically).
- Fixed a doubled /mcp/mcp path by setting streamable_http_path to "/"
(MCPServer.streamable_http_app() defaults to registering its own /mcp route).
- Verified end-to-end with the real mcp Python client: list_tools() returns
search_regulations, auth correctly 401s without or with an invalid token.
- 7 new tests, 76 total (up from 69), all passing.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix 1 (bootstrap.py): wrap store.flush() in asyncio.to_thread() inside the
periodic _flush_loop() to avoid blocking the async event loop every 60s.
Synchronous signatures of _start/_stop_model_usage_persistence() and the
one-time seed/shutdown flushes are left unchanged per review scope.
Fix 2 (test_stream_chat_usage_capture.py): add the two-line stream_options
assertion to test_qwen_vl_stream_chat_returns_usage_from_trailing_chunk,
matching the identical check already present in the DeepSeek and Qwen tests.
Fix 3 (design doc): note the single-worker assumption in A3's write strategy
section — multi-worker deployments get last-writer-wins per-row semantics.
Tests: 69 passed, 0 failed (python -m pytest backend/tests -q)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add seed() method to ModelUsageTracker for bulk-loading persisted entries at startup
- Create PostgresModelUsageStore for persistence of model usage counters to Postgres
- Store only current cumulative snapshots (no historical time-series)
- Use standard CREATE TABLE IF NOT EXISTS idiom matching other Postgres stores
- Add comprehensive mocked unit tests for both components
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Live verification via POST /status/models/ping (after confirming the
gateway itself is reachable -- embedding role succeeded, 1.5s latency)
shows http://6.86.80.4:30080/v1/rerank returns a fast, reproducible
'503 Service Unavailable' -- not a timeout/fluke. The gateway's model
catalog (19 models: deepseek-*, glm-*, kimi-*, qwen3*, text-embedding-v3/4)
contains no cross-encoder/rerank-capable model, confirming no reranker
service is deployed behind this gateway today. Leaving RERANKER_ENABLED=true
would be a permanent no-op (graceful fallback to unranked order every call)
plus a misleading permanent error badge on the Status page. Revert until a
reranker model is actually deployed on the gateway.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sandbox verification via /status/models/ping was inconclusive: the gateway
(6.86.80.4:30080) is unreachable from this environment entirely (embedding
role failed with the identical connection-timeout pattern, which is a
feature that definitely works in real deployment) -- not evidence that
/rerank specifically is unsupported. Reranker code already has graceful
TEI/Cohere fallback + falls back to unranked order on any failure, so this
is zero-risk to retrieval even if misconfigured. Please verify via
POST /status/models/ping in an environment with real gateway access;
revert RERANKER_ENABLED to false if that shows a genuine error.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Implement manual generator driving using next()/StopIteration to capture
the return value (trailing usage dict) from inner stream_chat() implementations,
enabling token tracking for streaming LLM calls.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix UI components (Badge, Button, Card, Input, ProgressBar, ScoreBar,
ChatPanel) to use CSS variables instead of old theme object pattern.
Clean up barrel exports (common/index.ts, layout/index.ts) and remove
stale router/tabs import from shell-config.ts. Build: tsc zero errors,
Vite production build succeeds (1760 modules, 270 kB JS, 22 kB CSS).