FolioFOLIOdocs
AppApp
BackendBackend
Mobile/AI Assistant
06 / 08

AI Assistant

Your portfolio copilot

AIClaudeChat

Overview

The Assistant tab is a full chat interface powered by Claude. It is not a generic chatbot. It has context about your actual portfolio: your holdings, current prices, allocation breakdown, and performance. You can ask things like "how diversified am I?" or "should I sell my NVDA position?" and get answers that reference your real data. The UI has a clean chat layout with user messages on the right, assistant messages on the left with a pixel art avatar, and suggestion chips at the bottom for common questions. Claude can respond with rich components like charts, tables, and action buttons. There is also image upload support for analyzing screenshots of other portfolios or brokerage statements. Free users get a limited number of messages per day, premium users get unlimited.

How it works

1

Messages are managed by useAssistantStore (Zustand). The store holds the conversation history, loading state, and pending prompts (for when you navigate from analytics).

2

When you send a message, the app builds a system prompt using buildSystemPrompt that includes your portfolio context (holdings, prices, allocation). This gets sent alongside your message to a Supabase Edge Function.

3

The Edge Function (supabase/functions/assistant) forwards the request to the Claude API with the system prompt and conversation history. It returns the full response (not streamed) because we need complete JSON to render rich UI components.

4

Claude responses are parsed as JSON that can contain text blocks and rich components (charts, tables, action buttons). The ChatMessageRenderer component handles rendering each type.

5

Action buttons in responses (like "Add AAPL to portfolio") use an action handler that calls the appropriate store method. The executeAction function maps action types to store operations.

6

Rate limiting for free users is handled by useAssistantRateLimit which tracks message count per day in local storage. Premium status comes from useSubscriptionStore.

Key decisions

Non streaming responses

Most chat UIs stream responses token by token. We chose not to because Claude returns structured JSON with UI components that need to be rendered as complete units. You cannot render half a chart. The tradeoff is a slightly longer wait before seeing the response, but the response is richer and more useful.

Portfolio context in the system prompt

Instead of giving Claude tool access to query your portfolio, we pre build the context and inject it into the system prompt. This is simpler, faster (no back and forth tool calls), and means Claude always has your full portfolio picture when answering. The context builder formats your holdings, prices, and allocation into a structured string that Claude can reference.

Edge Function proxy for API key security

The Claude API key never touches the client. All requests go through a Supabase Edge Function that adds the API key server side. The client only needs a Clerk auth token to authenticate with the Edge Function. This keeps the API key secure and lets us add rate limiting and logging on the server.

Quick Reference

Routeapp/(tabs)/assistant.tsx
StoreuseAssistantStore, useSubscriptionStore
APIssendToClaudeStreaming, buildPortfolioContext
Components
ChatMessageRendererPixelAvatarLiquidGlassSuggestionChipLiquidGlassTextInputUserMessageBubbleAssistantMessageContainerRateLimitBanner
PreviousAnalyticsNextOnboarding