What You’ll Build
A personal AI assistant that:- Remembers user preferences (dietary restrictions, work schedule, communication style)
- Maintains context across multiple chat sessions
- Provides personalized recommendations based on user history
- Handles multiple conversation topics while maintaining context
Choose Your Implementation
Python + FastAPI
Thoroughly tested, production-ready. Uses FastAPI + Streamlit + OpenAI.
TypeScript + AI SDK
Modern React approach. Uses Next.js + Vercel AI SDK + Supermemory tools.
Prerequisites
- Python 3.8+ or Node.js 18+
- Supermemory API key (get one here)
- OpenAI API key (get one here)
Python Implementation
Step 1: Project Setup
.env file:
Step 2: Backend (FastAPI)
Createmain.py. Let’s build it step by step:
Import Dependencies
- FastAPI: Web framework for building the API endpoint
- StreamingResponse: Enables real-time response streaming (words appear as they’re generated)
- AsyncOpenAI: OpenAI client that supports async/await for non-blocking operations
- Supermemory: Client for storing and retrieving long-term memories
- uuid: Creates stable, deterministic user IDs from emails
Initialize Application and Clients
load_dotenv() loads API keys from your .env file into environment variables. We create two clients:
- OpenAI client: Handles conversations and generates responses
- Supermemory client: Stores and retrieves user-specific memories
Define System Prompt
- Be proactive about learning user preferences
- Always search memory before responding
- Respect privacy boundaries
Create Identity Helpers
"User@Mail.com" and " user@mail.com " should map to the same person. We trim whitespace and lowercase to ensure consistency.
Why UUIDv5? It’s deterministic—same email always produces the same ID. This means:
- User memories persist across sessions
- No raw emails in logs or database tags
- Privacy-preserving yet stable identity
uuid.NAMESPACE_DNS as the namespace to ensure uniqueness.
Memory Search Function
q: The search query (usually the user’s latest message)container_tag: Isolates memories per user (e.g.,user_abc123)limit=5: Returns top 5 most relevant memories
Memory Storage Function
content: The text to remembercontainer_tag: User isolation tagmetadata: Additional context (type of info, associated email)
Main Chat Endpoint
messages: Full conversation history[{role: "user", content: "..."}]email: User’s email for identity
Derive User Identity
user_abc123) isolates this user’s memories from everyone else’s. Each user has their own “memory box.”
Search and Inject Memories
Stream OpenAI Response
model="gpt-5": Fast, capable modelmessages: Full conversation + memory contexttemperature=0.7: Balanced creativity (0=deterministic, 1=creative)stream=True: Enables word-by-word streaming
Handle Streaming
- Receives chunks from OpenAI as they’re generated
- Extracts the text content from each chunk
- Formats it as Server-Sent Events (SSE):
data: {...}\n\n - Yields it to the client
Optional Memory Storage
Return Streaming Response
StreamingResponse keeps the HTTP connection open and sends chunks as they’re generated. The frontend receives them in real-time.
Local Development Server
python main.py and the server starts on port 8000. 0.0.0.0 means it accepts connections from any IP (useful for testing from other devices).
Step 3: Frontend (Streamlit)
Createstreamlit_app.py:
Complete Frontend Code
Complete Frontend Code
Step 4: Run It
Terminal 1 - Start backend:http://localhost:8501 in your browser.
TypeScript Implementation
Step 1: Project Setup
.env.local:
Step 2: API Route
Createapp/api/chat/route.ts. Let’s break it down:
Import Dependencies
- streamText: Vercel AI SDK function that handles streaming responses and tool calling
- createOpenAI: Factory function to create an OpenAI provider
- supermemoryTools: Pre-built tools for memory search and storage
Initialize OpenAI Provider
! tells TypeScript “this definitely exists” (because we set it in .env.local).
This provider object will be passed to streamText to specify which AI model to use.
Define System Prompt
- When to use tools: Search memories before responding, add memories when users share info
- Personality: Be helpful and personalized
- Boundaries: Respect privacy
searchMemories and addMemory tools automatically.
Create POST Handler
/api/chat.
We extract:
messages: Chat history array[{role, content}]email: User identifier
Validate Input
- At least one message to respond to
- An email to isolate user memories
Create Container Tag
Call streamText with Tools
model: openai('gpt-5')
- Specifies which AI model to use
- The AI SDK handles the API calls
messages
- Full conversation history
- Format:
[{role: "user"|"assistant", content: "..."}]
tools: supermemoryTools(...)
- Gives the AI access to memory operations
- The AI SDK automatically:
- Decides when to call tools based on the conversation
- Calls
searchMemorieswhen it needs context - Calls
addMemorywhen users share information - Handles tool execution and error handling
containerTags: [containerTag]
- Scopes all memory operations to this specific user
- Ensures User A can’t access User B’s memories
system: SYSTEM_PROMPT
- Guides the AI’s behavior and tool usage
- User: “Remember that I’m vegetarian”
- AI SDK detects this is memory-worthy
- Automatically calls
addMemory("User is vegetarian") - Stores in Supermemory with the user’s container tag
- Responds: “Got it, I’ll remember that!”
- User: “What should I eat?”
- AI SDK calls
searchMemories("food preferences") - Retrieves: “User is vegetarian”
- Responds: “How about a delicious veggie stir-fry?”
Return Streaming Response
toAIStreamResponse() converts the streaming result into a format the frontend can consume. It:
- Sets appropriate headers for streaming
- Formats data for the
useChathook - Handles errors gracefully
Error Handling
Key Differences from Python:
| Aspect | Python | TypeScript |
|---|---|---|
| Memory Search | Manual search_user_memories() call | AI SDK calls searchMemories tool automatically |
| Memory Add | Manual add_user_memory() call | AI SDK calls addMemory tool automatically |
| Tool Decision | You decide when to search/add | AI decides based on conversation context |
| Streaming | Manual SSE formatting | toAIStreamResponse() handles it |
| Error Handling | Try/catch in each function | AI SDK handles tool errors |
Step 3: Chat UI
Replaceapp/page.tsx:
Complete Frontend Code
Complete Frontend Code
Step 4: Run It
http://localhost:3000
Testing Your Assistant
Try these conversations to test memory: Personal Preferences:Verify Memory Storage
Python
Createcheck_memories.py:
TypeScript
Createscripts/check-memories.ts:
Troubleshooting
Memory not persisting?- Verify container tags are consistent
- Check API key has write permissions
- Ensure email is properly normalized
- Increase search limit to find more memories
- Check that memories are being added
- Verify system prompt guides tool usage
- Reduce search limits
- Implement caching for frequent queries
- Use appropriate thresholds
Built with Supermemory. Customize based on your needs.