From e808031aa65e65087a525012980114e487867989 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Mar 2026 10:48:49 +0000 Subject: [PATCH] docs: update docs/agent/ with AgentOS architecture, components, conventions, and ADR 013 Co-authored-by: aguzererler <6199053+aguzererler@users.noreply.github.com> Agent-Logs-Url: https://github.com/aguzererler/TradingAgents/sessions/7cf1fa1a-8cb0-46b4-bc20-525aa3d38d7d --- docs/agent/CURRENT_STATE.md | 39 +++++----- docs/agent/context/ARCHITECTURE.md | 78 ++++++++++++++++++- docs/agent/context/COMPONENTS.md | 59 +++++++++++++- docs/agent/context/CONVENTIONS.md | 16 +++- docs/agent/context/GLOSSARY.md | 20 ++++- docs/agent/context/TECH_STACK.md | 39 +++++++++- .../013-agentos-websocket-streaming.md | 57 ++++++++++++++ 7 files changed, 282 insertions(+), 26 deletions(-) create mode 100644 docs/agent/decisions/013-agentos-websocket-streaming.md diff --git a/docs/agent/CURRENT_STATE.md b/docs/agent/CURRENT_STATE.md index 8d02bb2c..fad695dc 100644 --- a/docs/agent/CURRENT_STATE.md +++ b/docs/agent/CURRENT_STATE.md @@ -1,34 +1,31 @@ # Current Milestone -Portfolio Manager feature fully implemented (Phases 1–10). All 588 tests passing (14 skipped). +AgentOS visual observability layer shipped. Portfolio Manager fully implemented (Phases 1–10). All 725 tests passing (14 skipped). # Recent Progress +- **AgentOS (current PR)**: Full-stack visual observability layer for agent execution + - `agent_os/backend/` — FastAPI backend (port 8088) with REST + WebSocket streaming + - `agent_os/frontend/` — React + Vite 8 + Chakra UI + ReactFlow dashboard + - `agent_os/backend/services/langgraph_engine.py` — LangGraph event mapping engine (4 run types: scan, pipeline, portfolio, auto) + - `agent_os/backend/routes/websocket.py` — WebSocket streaming endpoint (`/ws/stream/{run_id}`) + - `agent_os/backend/routes/runs.py` — REST run triggers (`POST /api/run/{type}`) + - `agent_os/backend/routes/portfolios.py` — Portfolio REST API with field mapping (backend models → frontend shape) + - `agent_os/frontend/src/Dashboard.tsx` — 2-page layout (dashboard + portfolio), agent graph + terminal + controls + - `agent_os/frontend/src/components/AgentGraph.tsx` — ReactFlow live graph visualization + - `agent_os/frontend/src/components/PortfolioViewer.tsx` — Holdings, trade history, summary views + - `agent_os/frontend/src/components/MetricHeader.tsx` — Top-3 metrics (Sharpe, regime, drawdown) + - `agent_os/frontend/src/hooks/useAgentStream.ts` — WebSocket hook with status tracking + - `tests/unit/test_langgraph_engine_extraction.py` — 14 tests for event mapping + - Pipeline recursion limit fix: passes `config={"recursion_limit": propagator.max_recur_limit}` to `astream_events()` + - Portfolio field mapping fix: shares→quantity, portfolio_id→id, cash→cash_balance, trade_date→executed_at - **PR #32 merged**: Portfolio Manager data foundation — models, SQL schema, module scaffolding - - `tradingagents/portfolio/` — full module: models, config, exceptions, supabase_client (psycopg2), report_store, repository - - `migrations/001_initial_schema.sql` — 4 tables (portfolios, holdings, trades, snapshots) with constraints, indexes, triggers - - `tests/portfolio/` — 51 tests: 20 model, 15 report_store, 12 repository unit, 4 integration - - Uses `psycopg2` direct PostgreSQL via Supabase pooler (`aws-1-eu-west-1.pooler.supabase.com:6543`) - - Business logic: avg cost basis, cash accounting, trade recording, snapshots -- **PR #22 merged**: Unified report paths, structured observability logging, memory system update -- **feat/daily-digest-notebooklm** (shipped): Daily digest consolidation + NotebookLM source sync -- **Portfolio Manager Phases 2-5** (implemented): - - `tradingagents/portfolio/risk_evaluator.py` — pure-Python risk metrics (log returns, Sharpe, Sortino, VaR, max drawdown, beta, sector concentration, constraint checking) - - `tradingagents/portfolio/candidate_prioritizer.py` — conviction × thesis × diversification × held_penalty scoring - - `tradingagents/portfolio/trade_executor.py` — executes BUY/SELL (SELLs first), constraint pre-flight, EOD snapshot - - `tradingagents/agents/portfolio/holding_reviewer.py` — LLM holding review agent (run_tool_loop pattern) - - `tradingagents/agents/portfolio/pm_decision_agent.py` — pure-reasoning PM decision agent (no tools) - - `tradingagents/portfolio/portfolio_states.py` — PortfolioManagerState (MessagesState + reducers) - - `tradingagents/graph/portfolio_setup.py` — PortfolioGraphSetup (sequential 6-node workflow) - - `tradingagents/graph/portfolio_graph.py` — PortfolioGraph (mirrors ScannerGraph pattern) - - 48 new tests (28 risk_evaluator + 10 candidate_prioritizer + 10 trade_executor) +- **Portfolio Manager Phases 2-5** (implemented): risk_evaluator, candidate_prioritizer, trade_executor, holding_reviewer, pm_decision_agent, portfolio_states, portfolio_setup, portfolio_graph - **Portfolio CLI integration**: `portfolio`, `check-portfolio`, `auto` commands in `cli/main.py` -- **Documentation updated**: Flow diagram in `docs/portfolio/00_overview.md` aligned with actual 6-node sequential implementation; token estimation per model added; CLI & test commands added to README.md # In Progress -- Refinement of macro scan synthesis prompts (ongoing) -- End-to-end integration testing with live LLM + Supabase +- None — PR ready for merge # Active Blockers diff --git a/docs/agent/context/ARCHITECTURE.md b/docs/agent/context/ARCHITECTURE.md index f0ce6a97..bf92ee63 100644 --- a/docs/agent/context/ARCHITECTURE.md +++ b/docs/agent/context/ARCHITECTURE.md @@ -1,4 +1,4 @@ - + # Architecture @@ -131,6 +131,70 @@ Source: `tradingagents/observability.py` Source: `cli/main.py`, `cli/stats_handler.py` +## AgentOS — Visual Observability Layer + +Full-stack web UI for monitoring and controlling agent execution in real-time. + +### Architecture + +``` +┌──────────────────────────────────┐ ┌───────────────────────────────────┐ +│ Frontend (React + Vite 8) │ │ Backend (FastAPI) │ +│ localhost:5173 │◄─WS──►│ 127.0.0.1:8088 │ +│ │ │ │ +│ Dashboard (2 pages via sidebar) │ │ POST /api/run/{type} — queue run │ +│ ├─ dashboard: graph+terminal │ │ WS /ws/stream/{run_id} — execute │ +│ └─ portfolio: PortfolioViewer │ │ GET /api/portfolios/* — data │ +│ │ │ │ +│ ReactFlow (live agent graph) │ │ LangGraphEngine │ +│ Terminal (event stream) │ │ ├─ run_scan() │ +│ MetricHeader (Sharpe/regime) │ │ ├─ run_pipeline() │ +│ Param panel (date/ticker/id) │ │ ├─ run_portfolio() │ +│ │ │ └─ run_auto() [scan→pipe→port] │ +└──────────────────────────────────┘ └───────────────────────────────────┘ +``` + +### Run Types + +| Type | REST Trigger | WebSocket Executor | Description | +|------|-------------|-------------------|-------------| +| `scan` | `POST /api/run/scan` | `run_scan()` | 3-phase macro scanner | +| `pipeline` | `POST /api/run/pipeline` | `run_pipeline()` | Per-ticker trading analysis | +| `portfolio` | `POST /api/run/portfolio` | `run_portfolio()` | Portfolio manager workflow | +| `auto` | `POST /api/run/auto` | `run_auto()` | Sequential: scan → pipeline → portfolio | + +REST endpoints only queue runs (in-memory store). WebSocket is the sole executor — streaming LangGraph events to the frontend in real-time. + +### Event Streaming + +`LangGraphEngine._map_langgraph_event()` maps LangGraph v2 events to 4 frontend event types: + +| Event | LangGraph Trigger | Content | +|-------|------------------|---------| +| `thought` | `on_chat_model_start` | Prompt text, model name | +| `tool` | `on_tool_start` | Tool name, arguments | +| `tool_result` | `on_tool_end` | Tool output | +| `result` | `on_chat_model_end` | Response text, token counts, latency | + +Each event includes optional `prompt` and `response` full-text fields. Model name extraction uses 3 fallbacks: `invocation_params` → serialized kwargs → `metadata.ls_model_name`. Event mapping uses try/except per type and `_safe_dict()` helper to prevent crashes from non-dict metadata. + +### Portfolio API + +| Endpoint | Description | +|----------|-------------| +| `GET /api/portfolios/` | List all portfolios | +| `GET /api/portfolios/{id}` | Get portfolio details | +| `GET /api/portfolios/{id}/summary` | Top-3 metrics (Sharpe, regime, drawdown) | +| `GET /api/portfolios/{id}/latest` | Holdings, trades, snapshot with field mapping | + +The `/latest` endpoint maps backend model fields to frontend shape: `Holding.shares` → `quantity`, `Portfolio.portfolio_id` → `id`, `cash` → `cash_balance`, `Trade.trade_date` → `executed_at`. Computed runtime fields (`market_value`, `unrealized_pnl`) are included from enriched Holding properties. + +### Pipeline Recursion Limit + +`run_pipeline()` passes `config={"recursion_limit": propagator.max_recur_limit}` (default 100) to `astream_events()`. Without it, LangGraph defaults to 25 which is too low for the debate + risk cycles. + +Source: `agent_os/backend/`, `agent_os/frontend/` + ## Key Source Files | File | Purpose | @@ -138,8 +202,10 @@ Source: `cli/main.py`, `cli/stats_handler.py` | `tradingagents/default_config.py` | All config keys, defaults, env var override pattern | | `tradingagents/graph/trading_graph.py` | `TradingAgentsGraph` class, LLM wiring, tool nodes | | `tradingagents/graph/scanner_graph.py` | `ScannerGraph` class, 3-phase workflow | +| `tradingagents/graph/portfolio_graph.py` | `PortfolioGraph` class, 6-node portfolio workflow | | `tradingagents/graph/setup.py` | `GraphSetup` — agent node creation, graph compilation | | `tradingagents/graph/scanner_setup.py` | `ScannerGraphSetup` — scanner graph compilation | +| `tradingagents/graph/portfolio_setup.py` | `PortfolioGraphSetup` — portfolio graph compilation | | `tradingagents/dataflows/interface.py` | `route_to_vendor`, `VENDOR_METHODS`, `FALLBACK_ALLOWED` | | `tradingagents/agents/utils/tool_runner.py` | `run_tool_loop()`, `MAX_TOOL_ROUNDS=5`, `MIN_REPORT_LENGTH=2000` | | `tradingagents/agents/utils/agent_states.py` | `AgentState`, `InvestDebateState`, `RiskDebateState` | @@ -150,3 +216,13 @@ Source: `cli/main.py`, `cli/stats_handler.py` | `tradingagents/report_paths.py` | Unified report path helpers (`get_market_dir`, `get_ticker_dir`, etc.) | | `tradingagents/observability.py` | `RunLogger`, `_LLMCallbackHandler`, structured event logging | | `tradingagents/dataflows/config.py` | `set_config()`, `get_config()`, `initialize_config()` | +| `agent_os/backend/main.py` | FastAPI app, CORS, route mounting, health check | +| `agent_os/backend/services/langgraph_engine.py` | `LangGraphEngine` — run orchestration, LangGraph event mapping | +| `agent_os/backend/routes/websocket.py` | WebSocket streaming endpoint (`/ws/stream/{run_id}`) | +| `agent_os/backend/routes/runs.py` | REST run triggers (`POST /api/run/{type}`) | +| `agent_os/backend/routes/portfolios.py` | Portfolio REST API with field mapping | +| `agent_os/frontend/src/Dashboard.tsx` | 2-page dashboard, graph + terminal + controls | +| `agent_os/frontend/src/hooks/useAgentStream.ts` | WebSocket hook, `AgentEvent` type, status tracking | +| `agent_os/frontend/src/components/AgentGraph.tsx` | ReactFlow live agent graph visualization | +| `agent_os/frontend/src/components/PortfolioViewer.tsx` | Holdings table, trade history, snapshot summary | +| `agent_os/frontend/src/components/MetricHeader.tsx` | Top-3 portfolio metrics display | diff --git a/docs/agent/context/COMPONENTS.md b/docs/agent/context/COMPONENTS.md index f2f5787f..c367f6e1 100644 --- a/docs/agent/context/COMPONENTS.md +++ b/docs/agent/context/COMPONENTS.md @@ -1,4 +1,4 @@ - + # Components @@ -93,6 +93,41 @@ tradingagents/ cli/ └── main.py # Typer app, MessageBuffer, Rich UI, 3 commands + +agent_os/ +├── __init__.py +├── DESIGN.md # Visual observability design document +├── README.md # AgentOS overview and setup instructions +├── backend/ +│ ├── __init__.py +│ ├── main.py # FastAPI app, CORS, route mounting (port 8088) +│ ├── dependencies.py # get_current_user() (V1 hardcoded), get_db_client() +│ ├── store.py # In-memory run store (Dict[str, Dict]) +│ ├── routes/ +│ │ ├── __init__.py +│ │ ├── runs.py # POST /api/run/{scan,pipeline,portfolio,auto} +│ │ ├── websocket.py # WS /ws/stream/{run_id} — sole executor +│ │ └── portfolios.py # GET /api/portfolios/* — CRUD + summary + latest +│ └── services/ +│ ├── __init__.py +│ └── langgraph_engine.py # LangGraphEngine: run_scan/pipeline/portfolio/auto, event mapping +└── frontend/ + ├── package.json # React 18 + Vite 8 + Chakra UI + ReactFlow + ├── tsconfig.json + ├── vite.config.ts + ├── index.html + └── src/ + ├── main.tsx # React entry point + ├── App.tsx # ChakraProvider wrapper + ├── Dashboard.tsx # 2-page layout: dashboard (graph+terminal) / portfolio + ├── theme.ts # Dark theme customization + ├── index.css # Global styles + ├── hooks/ + │ └── useAgentStream.ts # WebSocket hook, AgentEvent type, status ref + └── components/ + ├── AgentGraph.tsx # ReactFlow live graph with incremental nodes + ├── MetricHeader.tsx # Top-3 metrics: Sharpe, regime, drawdown + └── PortfolioViewer.tsx # Holdings table, trade history, snapshot view ``` ## Agent Factory Inventory (17 factories + 1 utility) @@ -161,6 +196,27 @@ cli/ | `scan` | `run_scan(date)` | 3-phase macro scanner, saves 5 report files | | `pipeline` | `run_pipeline()` | Full pipeline: scan JSON → filter by conviction → per-ticker deep dive | +## AgentOS Frontend Components + +| Component | File | Description | +|-----------|------|-------------| +| `Dashboard` | `agent_os/frontend/src/Dashboard.tsx` | 2-page layout with sidebar (dashboard/portfolio), run buttons, param panel | +| `AgentGraph` | `agent_os/frontend/src/components/AgentGraph.tsx` | ReactFlow live graph — incremental node addition via useRef(Set) dedup | +| `MetricHeader` | `agent_os/frontend/src/components/MetricHeader.tsx` | Top-3 metrics: Sharpe ratio, market regime+beta, drawdown+VaR | +| `PortfolioViewer` | `agent_os/frontend/src/components/PortfolioViewer.tsx` | 3-tab view: holdings table, trade history, snapshot summary | +| `useAgentStream` | `agent_os/frontend/src/hooks/useAgentStream.ts` | WebSocket hook with `statusRef` to avoid stale closures | + +## AgentOS Backend Services + +| Service | File | Description | +|---------|------|-------------| +| `LangGraphEngine` | `agent_os/backend/services/langgraph_engine.py` | Orchestrates 4 run types, maps LangGraph v2 events to frontend events | +| `runs` router | `agent_os/backend/routes/runs.py` | REST triggers: `POST /api/run/{type}` — queues runs in memory store | +| `websocket` router | `agent_os/backend/routes/websocket.py` | `WS /ws/stream/{run_id}` — sole executor, streams events to frontend | +| `portfolios` router | `agent_os/backend/routes/portfolios.py` | Portfolio CRUD, summary metrics, holdings/trades with field mapping | +| `dependencies` | `agent_os/backend/dependencies.py` | `get_current_user()` (V1 hardcoded), `get_db_client()` | +| `store` | `agent_os/backend/store.py` | In-memory `Dict[str, Dict]` run store (demo, not persisted) | + ## Test Organization | Test File | Type | What It Covers | Markers | @@ -190,5 +246,6 @@ cli/ | `test_ttm_analysis.py` | Mixed | TTM metrics computation, report format | `integration` on live test | | `test_vendor_failfast.py` | Unit | ADR 011 fail-fast behavior, error chaining | — | | `test_yfinance_integration.py` | Unit | Full yfinance data layer (all mocked) | — | +| `test_langgraph_engine_extraction.py` | Unit | LangGraph event mapping, model/prompt extraction, _safe_dict helper | — | Pytest markers: `integration` (live API), `paid_tier` (Finnhub paid subscription), `slow` (long-running). Defined in `conftest.py`. diff --git a/docs/agent/context/CONVENTIONS.md b/docs/agent/context/CONVENTIONS.md index 507346f3..d9133190 100644 --- a/docs/agent/context/CONVENTIONS.md +++ b/docs/agent/context/CONVENTIONS.md @@ -1,4 +1,4 @@ - + # Conventions @@ -94,3 +94,17 @@ - Fail-fast by default — no silent fallback unless method is in `FALLBACK_ALLOWED`. (ADR 011) - Alpha Vantage hierarchy: `AlphaVantageError` → `APIKeyInvalidError`, `RateLimitError`, `ThirdPartyError`, `ThirdPartyTimeoutError`, `ThirdPartyParseError`. (`alpha_vantage_common.py`) - Finnhub hierarchy: `FinnhubError` → `APIKeyInvalidError`, `RateLimitError`, `ThirdPartyError`, `ThirdPartyTimeoutError`, `ThirdPartyParseError`. (`finnhub_common.py`) + +## AgentOS Patterns + +- **REST endpoints only queue runs** — WebSocket is the sole executor. POST `/api/run/{type}` writes to in-memory store, WS `/ws/stream/{run_id}` picks up and executes. (`runs.py`, `websocket.py`) +- **Event mapping is crash-proof** — `_map_langgraph_event()` wraps each event type branch in try/except. `_safe_dict()` helper converts non-dict metadata to empty dict. (`langgraph_engine.py`) +- **Model name extraction** uses 3 fallbacks: `invocation_params` → serialized kwargs → `metadata.ls_model_name`. (`langgraph_engine.py`) +- **Prompt extraction** tries 5 locations: `data.messages` → `data.input.messages` → `data.input` → `data.kwargs.messages` → raw dump. (`langgraph_engine.py`) +- **ReactFlow nodes are incremental** — never rebuilt from scratch. `useRef(Set)` deduplication prevents duplicates. (`AgentGraph.tsx`) +- **useAgentStream uses statusRef** to avoid stale closures in WebSocket callbacks. Status is not a useCallback dependency. (`useAgentStream.ts`) +- **Pipeline recursion limit** — `run_pipeline()` must pass `config={"recursion_limit": propagator.max_recur_limit}` to `astream_events()`. Default LangGraph limit of 25 is too low for debate+risk cycles. (`langgraph_engine.py`) +- **Portfolio field mapping** — `/latest` endpoint maps backend model fields to frontend shape: `shares` → `quantity`, `portfolio_id` → `id`, `cash` → `cash_balance`, `trade_date` → `executed_at`. Computed fields (`market_value`, `unrealized_pnl`) included from runtime properties. (`portfolios.py`) +- **Dashboard drawer has 2 modes** — `'event'` (single event detail from terminal click) and `'node'` (all events for a graph node from ReactFlow click). (`Dashboard.tsx`) +- **Run buttons track activeRunType** — only the triggered button spins, others disabled during run. (`Dashboard.tsx`) +- **Collapsible param panel** — date/ticker/portfolio_id with per-run-type validation. (`Dashboard.tsx`) diff --git a/docs/agent/context/GLOSSARY.md b/docs/agent/context/GLOSSARY.md index 7369d88d..085ab5e4 100644 --- a/docs/agent/context/GLOSSARY.md +++ b/docs/agent/context/GLOSSARY.md @@ -1,4 +1,4 @@ - + # Glossary @@ -109,3 +109,21 @@ | Finnhub _RATE_LIMIT | `60` calls/min | `dataflows/finnhub_common.py` | | AV API_BASE_URL | `"https://www.alphavantage.co/query"` | `dataflows/alpha_vantage_common.py` | | Finnhub API_BASE_URL | `"https://finnhub.io/api/v1"` | `dataflows/finnhub_common.py` | +| _MAX_CONTENT_LEN | `300` (event message truncation) | `agent_os/backend/services/langgraph_engine.py` | +| _MAX_FULL_LEN | `50_000` (full prompt/response cap) | `agent_os/backend/services/langgraph_engine.py` | + +## AgentOS + +| Term | Definition | Source | +|------|-----------|--------| +| AgentOS | Full-stack visual observability layer for agent execution — FastAPI backend + React frontend | `agent_os/` | +| LangGraphEngine | Backend service that orchestrates run execution and maps LangGraph v2 events to frontend events | `agent_os/backend/services/langgraph_engine.py` | +| Run Type | One of 4 execution modes: `scan`, `pipeline`, `portfolio`, `auto` | `agent_os/backend/routes/runs.py` | +| AgentEvent | TypeScript interface for frontend events: `thought`, `tool`, `tool_result`, `result`, `log`, `system` | `agent_os/frontend/src/hooks/useAgentStream.ts` | +| useAgentStream | React hook that connects to `/ws/stream/{run_id}` and provides events + status | `agent_os/frontend/src/hooks/useAgentStream.ts` | +| AgentGraph | ReactFlow-based live graph visualization of agent workflow nodes | `agent_os/frontend/src/components/AgentGraph.tsx` | +| PortfolioViewer | 3-tab portfolio view: holdings, trade history, snapshot summary | `agent_os/frontend/src/components/PortfolioViewer.tsx` | +| MetricHeader | Top-3 dashboard metrics: Sharpe ratio, market regime+beta, drawdown+VaR | `agent_os/frontend/src/components/MetricHeader.tsx` | +| _safe_dict | Helper that converts non-dict metadata to empty dict to prevent crashes | `agent_os/backend/services/langgraph_engine.py` | +| Inspector Drawer | Side panel showing full prompt/response content for an event or node | `agent_os/frontend/src/Dashboard.tsx` | +| Field Mapping | `/latest` endpoint translates backend model fields to frontend shape (shares→quantity, etc.) | `agent_os/backend/routes/portfolios.py` | diff --git a/docs/agent/context/TECH_STACK.md b/docs/agent/context/TECH_STACK.md index e1cf8798..4995341a 100644 --- a/docs/agent/context/TECH_STACK.md +++ b/docs/agent/context/TECH_STACK.md @@ -1,4 +1,4 @@ - + # Tech Stack @@ -73,3 +73,40 @@ From `[dependency-groups]`: - Version: `0.2.1` - Entry point: `tradingagents = cli.main:app` - Package discovery: `tradingagents*`, `cli*` + +## AgentOS Frontend Dependencies + +From `agent_os/frontend/package.json`: + +| Package | Constraint | Purpose | +|---------|-----------|---------| +| `react` | `^18.3.0` | UI framework | +| `react-dom` | `^18.3.0` | React DOM rendering | +| `@chakra-ui/react` | `^2.10.0` | Component library (dark theme) | +| `@emotion/react` | `^11.13.0` | CSS-in-JS for Chakra | +| `@emotion/styled` | `^11.13.0` | Styled components for Chakra | +| `framer-motion` | `^10.18.0` | Animation library (Chakra dependency) | +| `reactflow` | `^11.11.0` | Graph/DAG visualization for agent workflow | +| `axios` | `^1.13.5` | HTTP client for REST API calls | +| `lucide-react` | `^0.460.0` | Icon library | + +Dev dependencies: TypeScript `^5.6.0`, Vite `^8.0.1`, ESLint `^8.57.0`, TailwindCSS `^3.4.0`. + +## AgentOS Backend Dependencies + +From `pyproject.toml` (additions for agent_os): + +| Package | Purpose | +|---------|---------| +| `fastapi` | Web framework for REST + WebSocket backend | +| `uvicorn` | ASGI server (port 8088) | +| `httpx` | Async HTTP client (used by FastAPI test client) | + +## AgentOS Build & Run + +| Command | Description | +|---------|-------------| +| `uvicorn agent_os.backend.main:app --host 0.0.0.0 --port 8088` | Start backend | +| `cd agent_os/frontend && npm run dev` | Start frontend (Vite dev server, port 5173) | +| `cd agent_os/frontend && npx vite build` | Production build | +| `cd agent_os/frontend && node_modules/.bin/tsc --noEmit` | TypeScript check | diff --git a/docs/agent/decisions/013-agentos-websocket-streaming.md b/docs/agent/decisions/013-agentos-websocket-streaming.md new file mode 100644 index 00000000..2d856ee4 --- /dev/null +++ b/docs/agent/decisions/013-agentos-websocket-streaming.md @@ -0,0 +1,57 @@ +# ADR 013: AgentOS WebSocket Streaming Architecture + +## Status + +Accepted + +## Context + +TradingAgents needed a visual observability layer to monitor agent execution in real-time. The CLI (Rich-based) works well for terminal users but doesn't provide graph visualization or persistent portfolio views. Key requirements: + +1. Stream LangGraph events to a web UI in real-time +2. Visualize the agent workflow as a live graph +3. Show portfolio holdings, trades, and metrics +4. Support all 4 run types (scan, pipeline, portfolio, auto) + +## Decision + +### REST + WebSocket Split + +REST endpoints (`POST /api/run/{type}`) **only queue** runs to an in-memory store. The WebSocket endpoint (`WS /ws/stream/{run_id}`) is the **sole executor** — it picks up queued runs, calls the appropriate LangGraph engine method, and streams events back to the frontend. + +This avoids the complexity of background task coordination. The frontend triggers a REST call, gets a `run_id`, then connects via WebSocket to that `run_id` to receive all events. + +### Event Mapping + +LangGraph v2's `astream_events()` produces raw events with varying structures per provider. `LangGraphEngine._map_langgraph_event()` normalizes these into 4 event types: `thought`, `tool`, `tool_result`, `result`. Each event includes: + +- `node_id`, `parent_node_id` for graph construction +- `metrics` (model, tokens, latency) +- Optional `prompt` and `response` full-text fields + +The mapper uses try/except per event type and a `_safe_dict()` helper to prevent crashes from non-dict metadata (e.g., some providers return strings or lists). + +### Field Mapping (Backend → Frontend) + +Portfolio models use different field names than the frontend expects. The `/latest` endpoint maps: `shares` → `quantity`, `portfolio_id` → `id`, `cash` → `cash_balance`, `trade_date` → `executed_at`. Computed runtime fields (`market_value`, `unrealized_pnl`) are included from enriched Holding properties. + +### Pipeline Recursion Limit + +`run_pipeline()` passes `config={"recursion_limit": propagator.max_recur_limit}` (default 100) to `astream_events()`. Without this, LangGraph defaults to 25, which is insufficient for the debate + risk cycles (up to ~10 iterations). + +## Consequences + +- **Pro**: Real-time visibility into agent execution with zero CLI changes +- **Pro**: Crash-proof event mapping — one bad event doesn't kill the stream +- **Pro**: Clean separation — frontend can reconnect to ongoing runs +- **Con**: In-memory run store is not persistent (acceptable for V1) +- **Con**: Single-tenant auth (hardcoded user) — needs JWT for production + +## Source Files + +- `agent_os/backend/services/langgraph_engine.py` +- `agent_os/backend/routes/websocket.py` +- `agent_os/backend/routes/runs.py` +- `agent_os/backend/routes/portfolios.py` +- `agent_os/frontend/src/hooks/useAgentStream.ts` +- `agent_os/frontend/src/Dashboard.tsx`