Hanna is an AI-powered personal assistant designed to help manage household tasks, utilities, and daily activities. Built as a learning project to explore LangChain, LLM agents, and modern web technologies, it features a conversational interface with persistent memory and Google service integrations.
- π§ AI Chat Interface: Conversational assistant using OpenAI via LangChain/LangGraph
- πΎ Persistent Memory: Redis-based conversation history with multi-conversation support
- π Secure Authentication: Clerk integration with JWT validation
- π Google Integration: OAuth2 flow for Gmail, Calendar, and Drive access
- π¦ Modern Architecture: FastAPI backend with async patterns, Next.js 15 frontend
- π³ Easy Development: One-command startup with Docker Compose
- π Document organization via Google Drive
- ποΈ Calendar event management
- π§° Project and utility tracking
- π Reminder engine with notifications
- π§ Email/mobile notification system
hanna/
βββ backend/ # FastAPI application
β βββ app/
β β βββ main.py # App entry point, CORS, lifecycle
β β βββ core/ # Config, database, Redis setup
β β βββ auth/ # JWT validation, Google OAuth
β β βββ chat/ # Chat endpoints, LangGraph engine
β β βββ models/ # SQLAlchemy models
β β βββ services/ # Google API integration
β βββ requirements.txt
βββ frontend/ # Next.js 15 application
β βββ src/
β β βββ app/ # App router pages
β β βββ components/ # React components
β β βββ lib/ # API client, utilities
β βββ package.json
βββ mcp_servers/ # Model Context Protocol servers
βββ docker-compose.yml # PostgreSQL + Redis containers
βββ start-dev.sh # Development startup script
βββ stop-dev.sh # Development shutdown script
βββ CLAUDE.md # AI assistant instructionsWhen developing new tools for the AI agent (e.g., integrating with new services like Calendar, Drive, etc.), follow these guidelines to maintain consistency and organization within the project.
All tool inputs and outputs should be defined using Pydantic BaseModel schemas. These schemas serve multiple purposes:
- Input Validation: Ensure the AI agent provides valid arguments to the tool.
- Output Definition: Clearly define the structure of the data returned by the tool.
- LLM Documentation: LangChain uses these schemas to automatically generate documentation for the LLM, helping it understand how to use the tool.
- Type Safety: Improve code readability and maintainability with strong type hinting.
Recommended Structure for Tool Schemas:
Create a dedicated directory for each tool under backend/app/tools/, and place its specific Pydantic schemas in a schemas.py file within that tool's directory.
backend/
βββ app/
β βββ ...
β βββ tools/ # Directory for all tool-related files
β βββ __init__.py
β βββ <tool_name>/ # e.g., gmail/, calendar/
β β βββ __init__.py
β β βββ schemas.py # <--- Place tool-specific Pydantic schemas here
β βββ ...
Example (for Gmail tool):
# backend/app/tools/gmail/schemas.py
from pydantic import BaseModel
from typing import List, Optional
class EmailSearchInput(BaseModel):
"""Input for searching emails."""
query: str
max_results: int = 3
# ... (other Gmail-related schemas like EmailSearchResult, EmailContentInput, etc.)Once your Pydantic schemas are defined, create the LangChain tool wrappers. These wrappers will encapsulate the logic for interacting with your service (e.g., gmail_service.py) and expose it to the AI agent.
Recommended Structure for Tool Wrappers:
Place the LangChain tool wrappers in a tools.py file within the same tool-specific directory.
backend/
βββ app/
β βββ ...
β βββ tools/
β βββ ...
β βββ <tool_name>/
β βββ __init__.py
β βββ schemas.py
β βββ tools.py # <--- Place LangChain tool wrappers here
β βββ ...
Example (for Gmail tool):
# backend/app/tools/gmail/tools.py
from langchain.tools import tool
from app.services.gmail_service import search_emails, get_email_content, download_attachment
from .schemas import EmailSearchInput, EmailContentInput, DownloadAttachmentInput, EmailSearchResults, EmailContentResult
@tool(args_schema=EmailSearchInput)
async def search_gmail_emails(query: str, max_results: int = 3) -> EmailSearchResults:
"""Searches for emails in Gmail based on a query."""
# ... (implementation using search_emails from gmail_service)
pass
# ... (other tool wrappers for get_email_content, download_attachment)# Start entire development environment
./start-dev.sh
# Stop entire development environment
./stop-dev.shThis will:
- Start PostgreSQL and Redis via Docker Compose
- Launch the FastAPI backend with hot reload
- Start the Next.js frontend development server
- Handle all necessary database migrations
cd backend
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
# Start PostgreSQL and Redis
docker compose up -d
# Run the backend
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000cd frontend
npm install
npm run devFrontend runs at http://localhost:3000 and connects to backend at http://localhost:8000.
To test Google service integrations (e.g., Gmail, Calendar), you'll often need a user_id associated with an authenticated user. You can retrieve an existing user_id from the database using the following command:
docker exec hanna-db psql -U postgres -d hanna -c "SELECT user_id FROM google_oauth_tokens LIMIT 1;"This command will return the user_id of the first entry in the google_oauth_tokens table.
To run the implemented Gmail service tests (located in backend/tests/services/test_gmail_service.py), navigate to the backend directory and execute the following command:
cd backend
python -m tests.services.test_gmail_serviceThis will execute the test script, which includes functions for searching emails, getting email content, and downloading attachments.
# OpenAI
OPENAI_API_KEY=your-openai-key
# Database
DATABASE_URL=postgresql+asyncpg://hanna:hanna@localhost:5432/hanna
REDIS_URL=redis://localhost:6379
# Clerk Authentication
CLERK_JWKS_URL=https://your-clerk-instance.clerk.accounts.dev/.well-known/jwks.json
CLERK_JWT_ISSUER=https://your-clerk-instance.clerk.accounts.dev
# Google OAuth
GOOGLE_CLIENT_SECRET_PATH=path/to/client_secret.json
# CORS
FRONTEND_ORIGIN=http://localhost:3000# API Configuration
NEXT_PUBLIC_API_URL=http://localhost:8000/api/v1
# Clerk Configuration
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
CLERK_SECRET_KEY=sk_test_...- FastAPI: Async Python web framework
- LangChain/LangGraph: AI orchestration and conversation flow
- SQLAlchemy: Async ORM for PostgreSQL
- Redis: Session storage and chat memory
- Pydantic: Data validation and settings
- Next.js 15: React framework with App Router
- React 19: UI library
- TailwindCSS v4: Utility-first CSS
- Clerk: Authentication and user management
- TypeScript: Type safety
- OpenAI API: Powers the conversational AI
- Google OAuth2: Secure access to Gmail, Calendar, and Drive
- JWT Authentication: Secure API communication
- Basic chat with OpenAI
- LangChain/LangGraph integration with memory
- Multi-user support with Clerk authentication
- Google OAuth2 integration
- Persistent conversation history
- Docker Compose setup
- User onboarding (name and home address personalization)
- Google Calendar event management
- Google Drive document search/access
- Reminder engine with notifications
- Project and utility tracking
- Email/mobile notifications
- LangChain agents for task automation
- Backend: FastAPI, Python 3.11+
- Frontend: Next.js 15, React 19, TypeScript
- Database: PostgreSQL with async SQLAlchemy
- Cache: Redis
- AI/ML: OpenAI API, LangChain, LangGraph
- Clerk: User authentication and management
- JWT: API authentication
- OAuth2: Google service integration
- Docker & Docker Compose: Containerization
- ESLint: Code linting
- Pydantic: Data validation
- Uvicorn: ASGI server
Hanna is a personal learning project β but PRs, issues, and ideas are welcome.
MIT Β© Juan Pablo Hurtado