From e6ffb45d8a46c17def8702608f6d51afc19687ec Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 24 Feb 2026 02:49:40 +0000 Subject: [PATCH] Update CLAUDE.md to v3 architecture: extend existing AiModule Major changes from v3 research document: - Extend existing AiModule instead of creating new agent-forge module - LangGraph TS ReAct agent via createReactAgent (not multi-agent) - 7 tools wrapping existing services (portfolio, holdings, activities, market data, risk analysis, account overview) - LangSmith observability instead of custom logging - Verification post-node in LangGraph graph - Document existing AI infrastructure (AiController, AiService, Assistant component, PropertyService) - New Prisma models: Conversation + Message - New endpoints: POST /ai/chat, GET/DELETE /ai/conversations https://claude.ai/code/session_01Uf16jhwBHEixYxMLk2BgWG --- CLAUDE.md | 180 ++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 142 insertions(+), 38 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 8c891353a..e724dc0be 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -16,14 +16,15 @@ Ghostfolio is a privacy-first, open-source personal finance dashboard for tracki ### What AgentForge Adds (The AI Layer) -AgentForge introduces an AI agent module that integrates with Ghostfolio's existing services: +AgentForge extends Ghostfolio's **existing AiModule** (not a new module) to add a full conversational agent: -- **Conversational AI assistant** — natural language interface for portfolio queries and actions -- **Multi-agent orchestration** — Coordinator, Analyst, Executor, and Risk Manager agents -- **Tool registry** — AI-callable tools wrapping Ghostfolio services (portfolio analysis, market data, trade execution, risk assessment, goal tracking) -- **Real-time streaming** — Server-Sent Events (SSE) for streaming AI responses -- **Conversation memory** — persistent chat history stored in PostgreSQL via Prisma -- **Permission-based tool access** — tools gated by user roles and subscription tier +- **LangGraph TS ReAct agent** — `createReactAgent` with tool-calling loop and verification post-node +- **7 tools** wrapping existing Ghostfolio services (portfolio summary, performance, holdings, activities, market data, risk analysis, account overview) +- **Real-time streaming** — Server-Sent Events (SSE) for streaming AI responses via new `POST /api/v1/ai/chat` endpoint +- **Conversation memory** — persistent chat history stored in PostgreSQL via Prisma (`Conversation` + `Message` models) +- **LangSmith observability** — tracing, latency metrics, and tool-call auditing +- **Admin-configurable LLM** — API keys and model selection stored via PropertyService (already exists) +- **Extended Assistant UI** — Angular Assistant component gains a chat tab alongside existing search --- @@ -36,7 +37,7 @@ AgentForge introduces an AI agent module that integrates with Ghostfolio's exist | **Frontend** | Angular 21.x with Angular Material | | **Database** | PostgreSQL 15 via Prisma 6.x ORM | | **Cache** | Redis (Bull queues for background jobs) | -| **AI/LLM** | LangChain / LangGraph (planned) | +| **AI/LLM** | LangGraph TS (`@langchain/langgraph`), Vercel AI SDK, LangSmith | | **Auth** | JWT, Google OAuth, OIDC, WebAuthn (Passport strategies) | | **Containerization** | Docker / Docker Compose | | **i18n** | Angular i18n (12 languages) | @@ -225,47 +226,150 @@ Key models in `prisma/schema.prisma`: --- -## AgentForge Integration Plan +## Existing AI Infrastructure -The AI agent layer will be implemented as a new NestJS module within the API app: +Ghostfolio already has a lightweight AI module. AgentForge extends it rather than replacing it. -### New Module: `apps/api/src/app/agent-forge/` +### Current State +| Component | Location | What It Does | +|-----------|----------|-------------| +| **AiController** | `apps/api/src/app/endpoints/ai/ai.controller.ts` | `GET /api/v1/ai/prompt/:mode` — returns a formatted prompt string | +| **AiService** | `apps/api/src/app/endpoints/ai/ai.service.ts` | `getPrompt()` builds a markdown holdings table; `generateText()` calls OpenRouter via Vercel AI SDK | +| **AiModule** | `apps/api/src/app/endpoints/ai/ai.module.ts` | Imports PortfolioService, AccountService, MarketDataService, etc. | +| **Assistant UI** | `libs/ui/src/lib/assistant/assistant.component.ts` | Search/navigation modal (accounts, holdings, asset profiles, quick links) — **not yet a chat UI** | +| **PropertyService** | `apps/api/src/services/property/property.service.ts` | Stores `API_KEY_OPENROUTER` and `OPENROUTER_MODEL` in the `Property` DB table | +| **Config constants** | `libs/common/src/lib/config.ts` | `PROPERTY_API_KEY_OPENROUTER`, `PROPERTY_OPENROUTER_MODEL`, `PROPERTY_SYSTEM_MESSAGE` | +| **Permission** | `libs/common/src/lib/permissions.ts` | `readAiPrompt` — granted to ADMIN, DEMO, USER roles | +| **AI SDK deps** | `package.json` | `ai` (Vercel AI SDK 4.x), `@openrouter/ai-sdk-provider` | + +### Current Data Flow + +1. User opens Analysis page → clicks "Copy AI Prompt" (portfolio or analysis mode) +2. Frontend calls `GET /api/v1/ai/prompt/{mode}` with optional filters +3. `AiService.getPrompt()` fetches holdings via `PortfolioService.getDetails()`, formats a markdown table +4. Prompt is copied to clipboard; user pastes into external LLM (Duck.ai, ChatGPT, etc.) + +**Key insight:** The current flow is prompt-generation-only with no in-app chat, no tool calling, and no conversation memory. + +--- + +## AgentForge Integration Plan (v3) + +The core principle: **extend the existing `AiModule`** — do not create a new module. Add files alongside `ai.controller.ts` and `ai.service.ts`. + +### Architecture: LangGraph TS ReAct Agent + +A single `createReactAgent` (from `@langchain/langgraph`) with a tool-calling loop and a **verification post-node** that reviews tool outputs before responding to the user. + +``` +User message + │ + ▼ +┌─────────┐ ┌───────────┐ ┌──────────────┐ +│ Agent │────▶│ Tools │────▶│ Verification │ +│ (ReAct) │◀────│ (7 tools) │◀────│ Post-Node │ +└─────────┘ └───────────┘ └──────────────┘ + │ + ▼ +Streamed response (SSE) ``` -agent-forge/ -├── agent-forge.module.ts # NestJS module registration -├── agent-forge.controller.ts # REST + SSE endpoints -├── agent-forge.service.ts # Core orchestration service -├── tools/ # AI-callable tool definitions -│ ├── portfolio-analysis.tool.ts # Wraps PortfolioService + +### Extended Module Structure + +``` +apps/api/src/app/endpoints/ai/ +├── ai.module.ts # Extended with new providers +├── ai.controller.ts # Extended with POST /chat and GET /conversations +├── ai.service.ts # Extended with agent orchestration +├── tools/ # NEW — LangGraph tool definitions +│ ├── portfolio-summary.tool.ts # Wraps PortfolioService.getDetails() +│ ├── portfolio-performance.tool.ts # Wraps PortfolioService.getPerformance() +│ ├── holdings-lookup.tool.ts # Wraps PortfolioService.getHoldings() +│ ├── activity-search.tool.ts # Wraps OrderService │ ├── market-data.tool.ts # Wraps DataProviderService -│ ├── trade-execution.tool.ts # Wraps OrderService -│ ├── risk-assessment.tool.ts # Wraps portfolio rules engine -│ └── goal-tracking.tool.ts # Financial goal management -├── agents/ # Multi-agent definitions -│ ├── coordinator.agent.ts # Routes queries to specialist agents -│ ├── analyst.agent.ts # Portfolio & market analysis -│ ├── executor.agent.ts # Trade execution with confirmation -│ └── risk-manager.agent.ts # Risk assessment & guardrails -├── memory/ # Conversation persistence -│ └── conversation.service.ts # Chat history via Prisma -└── streaming/ # Real-time response streaming - └── sse.service.ts # Server-Sent Events +│ ├── risk-analysis.tool.ts # Wraps RulesService / portfolio rules +│ └── account-overview.tool.ts # Wraps AccountService +├── memory/ # NEW — Conversation persistence +│ └── conversation.service.ts # CRUD for Conversation + Message via Prisma +└── streaming/ # NEW — SSE streaming + └── sse.service.ts # Server-Sent Events for token delivery ``` -### New Prisma Models (to be added) +### New API Endpoints + +| Method | Route | Purpose | +|--------|-------|---------| +| `POST` | `/api/v1/ai/chat` | Send message, get streamed SSE response | +| `GET` | `/api/v1/ai/conversations` | List user's conversations | +| `GET` | `/api/v1/ai/conversations/:id` | Get conversation with messages | +| `DELETE` | `/api/v1/ai/conversations/:id` | Delete a conversation | + +### 7 Tools (LangGraph `DynamicStructuredTool`) + +Each tool wraps an existing Ghostfolio service — **no duplicate business logic**. + +| Tool Name | Wraps | Input Schema | Returns | +|-----------|-------|-------------|---------| +| `portfolio_summary` | `PortfolioService.getDetails()` | filters (accounts, tags, assetClasses) | Holdings table with allocations, sectors, currencies | +| `portfolio_performance` | `PortfolioService.getPerformance()` | dateRange, filters | ROI, TWR, MWR, chart data for time range | +| `holdings_lookup` | `PortfolioService.getHoldings()` | symbol (optional), filters | Detailed holding info (quantity, price, P&L) | +| `activity_search` | `OrderService.getOrders()` | symbol, type, dateRange | Filtered transaction history | +| `market_data` | `DataProviderService.getQuotes()` | symbols[] | Current quotes, daily change, market state | +| `risk_analysis` | `RulesService` + portfolio rules | filters | Rule evaluations (cluster risk, currency risk, etc.) | +| `account_overview` | `AccountService.getAccounts()` | accountId (optional) | Account balances, platforms, cash positions | + +### New Prisma Models + +```prisma +model Conversation { + id String @id @default(uuid()) + createdAt DateTime @default(now()) + messages Message[] + title String? + updatedAt DateTime @updatedAt + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + userId String +} + +model Message { + id String @id @default(uuid()) + content String + conversation Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade) + conversationId String + createdAt DateTime @default(now()) + role String // 'user' | 'assistant' | 'tool' + toolCalls Json? // Serialized tool invocations + tokenCount Int? +} +``` + +### Frontend: Extended Assistant Component + +The existing `GfAssistantComponent` (`libs/ui/src/lib/assistant/`) gains a **chat tab** alongside the current search functionality: + +- New tab or mode toggle: **Search** (existing) | **Chat** (new) +- Chat tab renders conversation history with streaming message display +- Input field sends messages to `POST /api/v1/ai/chat` +- SSE consumption for real-time token rendering +- Conversation list sidebar for switching between chats + +### Observability: LangSmith -- `Conversation` — chat session metadata (userId, title, timestamps) -- `Message` — individual messages (role, content, toolCalls, tokens) -- `FinancialGoal` — user-defined financial goals with progress tracking +- All agent runs traced via `@langchain/core` callbacks +- Tool call latency, token usage, and error rates tracked +- Admin-configurable via `PROPERTY_LANGSMITH_API_KEY` in PropertyService +- Tracing can be toggled on/off without redeployment ### Key Design Principles -1. **Wrap, don't replace** — AI tools call existing Ghostfolio services; no duplicate business logic -2. **Permission-gated tools** — tool access respects user roles and subscription tiers -3. **Human-in-the-loop** — trade execution requires explicit user confirmation -4. **Streaming-first** — all AI responses use SSE for real-time token delivery -5. **Auditable** — all tool invocations are logged for compliance and debugging +1. **Extend, don't fork** — add to the existing `AiModule`; keep all current prompt-generation functionality working +2. **Wrap, don't replace** — tools call existing services; no duplicate business logic +3. **Verification post-node** — LangGraph graph includes a node after tool execution that validates outputs before responding +4. **Streaming-first** — all chat responses use SSE for real-time token delivery +5. **Permission-gated** — tool access respects existing `readAiPrompt` permission and user roles +6. **Admin-configurable** — LLM provider, model, and API keys stored in PropertyService (no env vars needed) +7. **Auditable** — LangSmith tracing for all tool invocations; Message model stores toolCalls JSON ---