A modern, open-source financial assistant powered by AI that helps users explore their transactions, discover merchants that align with their personal values, and make more intentional spending decisions.
This repo serves as the starter for the Building Delightful AI Chat Applications w/ Dynamic UI' Workshop at KnightHacks XIII, and allows hackers to easily add functionality or create dynamic UI chat apps for other use-cases.
Impact AI is a conversational chatbot that combines spending analytics with values-based insights. Users can:
- Explore transactions - Search and visualize spending patterns by category, merchant, or time period
- Discover aligned merchants - Find companies matching their personal values (e.g., women-owned, sustainable, local)
- Define personal values - Create a personal values profile and track spending alignment
- Compare spending - Analyze spending between different company types or values categories
- Get recommendations - Receive suggestions for alternative merchants that align with their values
impact-chatkit-starter/
βββ backend/ # Python FastAPI + AI agent
β βββ app/
β β βββ agent/ # ChatKit agent & modular tools
β β βββ services/ # Data access layer
β β βββ main.py # FastAPI entrypoint
β β βββ chat.py # ChatKit server & agent setup
β β βββ database.py # Prisma database layer
β β βββ constants.py # AI instructions & configuration
β βββ prisma/
β β βββ schema.prisma # Database models
β β βββ seed.py # Sample data generator
β βββ widgets/ # Reusable JSON widget templates
β
βββ frontend/ # React + Vite + TypeScript
β βββ src/
β βββ components/ # React components
β βββ hooks/ # Custom React hooks
β βββ lib/ # Config and utilities
β
βββ docker-compose.yml # Local dev environment (PostgreSQL + backend + frontend)
- Docker & Docker Compose (recommended for local dev)
- OR: Node.js 18+, Python 3.11+, PostgreSQL 14+
- OpenAI API Key (required for AI agent)
# Clone the repository
git clone https://github.com/KnightHacks/impact-chatkit-starter.git
cd impact-chatkit-starter
# Set environment variables
cp backend/.env.example backend/.env # Add your API keys
# Start all services (PostgreSQL, backend, frontend)
docker-compose up --build -d
# Frontend: http://localhost:5170
# Backend API: http://localhost:8000Backend:
cd backend
uv sync # Install dependencies
uv run uvicorn app.main:app --reload --port 8000Frontend:
cd frontend
npm install
npm run dev # Runs on http://localhost:5173Database:
# Set DATABASE_URL in backend/.env
# Run migrations
cd backend
uv run python prisma/seed.pyUser Input β ChatKit Frontend β Backend /chatkit endpoint β OpenAI Agent
The AI agent processes user queries and invokes available tools:
# Examples of agent interactions
"Show my restaurant spending last week"
β show_transactions(query="restaurant last week")
"Find women-owned sustainable clothing brands"
β show_companies(tags=['women-owned', 'sustainable'], category='Retail')
"Compare my spending at women-owned vs chain restaurants"
β compare_spending(filter_a={...}, filter_b={...})All responses include JSON widgets rendered in the ChatKit UI:
- Transaction lists - Detailed transaction history with filters
- Company cards - Company info with values/tags
- Charts - Spending trends, comparisons, alignment metrics
- Value dashboards - User's values + spending alignment
Prisma ORM manages three core models:
- Users - Stores personal values & preferences
- Companies - Merchant data + tags/values
- Transactions - Spending history + merchant links
Tools are modular Python functions that the AI agent can invoke. They're registered in backend/app/chat.py.
1. Create a tool file backend/app/agent/tools/my_tool.py:
from agents import tool
from chatkit.agents import AgentContext
from typing import Annotated, Optional
@tool
async def analyze_savings(
category: Annotated[str, "Spending category to analyze (e.g., 'Restaurant')"] = None,
days: Annotated[Optional[int], "Days to look back (default 30)"] = None,
) -> dict:
"""Analyze potential savings in a spending category."""
# Your implementation here
return {
"category": category,
"savings_potential": 150.00,
"savings_suggestions": ["Switch to cheaper restaurants", "Cook at home"]
}2. Register in chat.py:
# In app.chat.py - add to imports
from .agent.tools import analyze_savings
# In FactAssistantServer.__init__ - add to tools list
tools = [
# ... existing tools ...
analyze_savings,
]3. Update agent instructions in constants.py:
INSTRUCTIONS = """
...
β’ `analyze_savings(category?, days?)` β find potential savings in a category
Returns: JSON with suggestions and estimated savings.
...
"""Widgets are JSON templates with support for dynamic data via {{variable}} syntax.
1. Design in Widget Builder:
- Go to https://widgets.chatkit.studio
- Design your widget visually
- Export as JSON
2. Save template to backend/widgets/my_widget.json:
{
"type": "Card",
"padding": 4,
"children": [
{ "type": "Title", "value": "{{title}}", "size": "lg" },
{ "type": "Text", "value": "Amount: ${{amount}}" }
]
}3. Use in your tool:
from app.agent.utils import load_widget_from_file
widget = load_widget_from_file(
"backend/widgets/my_widget.json",
{"title": "Savings Summary", "amount": "150.00"}
)
await ctx.context.stream_widget(widget, copy_text="Summary text for history")See backend/widgets/README.md for complete widget documentation.
All data queries go through the services layer in backend/app/services/.
To modify transaction queries:
# backend/app/services/transactions.py
async def get_transactions_by_merchant(
user_id: str,
merchant: str,
days: int = 30
) -> list[Transaction]:
"""Fetch transactions for a specific merchant."""
db = await get_database()
cutoff_date = datetime.now() - timedelta(days=days)
return await db.transaction.find_many(
where={
"userId": user_id,
"merchantName": {"contains": merchant, "mode": "insensitive"},
"date": {"gte": cutoff_date}
},
order={"date": "desc"}
)To modify company queries:
# backend/app/services/companies.py
async def search_companies_by_values(
tags: list[str],
category: str | None = None,
limit: int = 10
) -> list[Company]:
"""Find companies matching specific values/tags."""
db = await get_database()
query = {
"tags": {"hasSome": tags}
}
if category:
query["category"] = category
return await db.company.find_many(
where=query,
take=limit
)Edit backend/prisma/seed.py to add test users, companies, and transactions:
# backend/prisma/seed.py
async def main():
db = Prisma()
await db.connect()
# Create test company
await db.company.create({
"data": {
"name": "Sustainable Cafe",
"category": "Restaurant",
"tags": ["sustainable", "local", "women-owned"],
"description": "Local cafe with sustainable practices"
}
})
# Create test user
user = await db.user.create({
"data": {
"name": "Test User",
"email": "test@example.com",
"values": ["sustainable", "women-owned"]
}
})
# Create test transactions
await db.transaction.create({
"data": {
"userId": user.id,
"merchantName": "Sustainable Cafe",
"amount": 15.50,
"category": "Restaurant",
"date": datetime.now()
}
})
await db.disconnect()
asyncio.run(main())Run seeding:
cd backend
uv run python prisma/seed.pyApp.tsx - Root component managing theme state
Home.tsx - Page layout with header and ChatKit panel
ChatKitPanel.tsx - ChatKit integration + client-side tool handlers
useColorScheme.ts - Theme state management (light/dark)
config.ts - API endpoints and ChatKit configuration
Change theme colors in frontend/src/components/Home.tsx:
const containerClass = clsx(
"min-h-screen transition-colors duration-300",
scheme === "dark" ? "bg-slate-900 text-slate-100" : "bg-white text-slate-900"
);Update API endpoints in frontend/src/lib/config.ts:
export const CHATKIT_API_URL =
import.meta.env.VITE_CHATKIT_API_URL ?? "/chatkit";Modify starter prompts in frontend/src/lib/config.ts:
export const STARTER_PROMPTS = [
{
label: "Your custom prompt",
prompt: "Your prompt text",
icon: "search",
},
];CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT uuid(),
name VARCHAR NOT NULL,
email VARCHAR UNIQUE NOT NULL,
values TEXT[] DEFAULT '{}', -- e.g., ['women-owned', 'sustainable']
createdAt TIMESTAMP DEFAULT NOW(),
updatedAt TIMESTAMP DEFAULT NOW()
);CREATE TABLE companies (
id UUID PRIMARY KEY DEFAULT uuid(),
name VARCHAR NOT NULL,
category VARCHAR NOT NULL, -- 'Restaurant', 'Retail', etc.
description TEXT,
logoUrl VARCHAR,
tags TEXT[] DEFAULT '{}', -- e.g., ['women-owned', 'sustainable', 'local']
website VARCHAR,
createdAt TIMESTAMP DEFAULT NOW(),
updatedAt TIMESTAMP DEFAULT NOW()
);CREATE TABLE transactions (
id UUID PRIMARY KEY DEFAULT uuid(),
userId UUID NOT NULL,
companyId UUID REFERENCES companies(id),
merchantName VARCHAR NOT NULL,
amount FLOAT NOT NULL,
category VARCHAR NOT NULL, -- 'Restaurant', 'Retail', 'Transport', etc.
date TIMESTAMP NOT NULL,
description TEXT,
tags TEXT[] DEFAULT '{}',
createdAt TIMESTAMP DEFAULT NOW()
);Backend - backend/.env:
DATABASE_URL=postgresql://impactai:password@localhost:5432/impactai
OPENAI_API_KEY=sk-xxx...
CHATKIT_API_DOMAIN_KEY=domain_pk_xxx...Frontend - Set via Docker or .env:
VITE_CHATKIT_API_URL=http://localhost:8000/chatkit
VITE_CHATKIT_API_DOMAIN_KEY=domain_pk_localhost_dev| File | Purpose |
|---|---|
backend/app/chat.py |
ChatKit server setup + tool registration |
backend/app/constants.py |
AI agent instructions + tool definitions |
backend/app/agent/tools/*.py |
Individual tool implementations |
backend/app/services/*.py |
Database query logic |
backend/widgets/*.json |
Widget templates for UI responses |
frontend/src/components/ChatKitPanel.tsx |
ChatKit UI + client-side tool handling |
docker-compose.yml |
Local dev environment config |
"ChatKit dependencies are missing"
- Ensure
openai-chatkitis installed:pip list | grep chatkit
Database connection errors
- Check
DATABASE_URLinbackend/.env - Verify PostgreSQL is running:
psql -U impactai -d impactai -c "SELECT 1"
Widgets not rendering
- Check widget JSON syntax in
backend/widgets/ - Verify template variables in
load_widget_from_file()calls
Agent not calling tools
- Check tool registration in
chat.py - Verify tool docstring for proper parameter descriptions
- Check OpenAI API key in
backend/.env
- Backend: See
backend/README.md - Frontend: See
frontend/README.md - Widgets: See
backend/widgets/README.md - Prisma: https://www.prisma.io/docs
Docker:
docker build -f backend/Dockerfile -t impact-ai-backend ./backend
docker build -f frontend/Dockerfile -t impact-ai-frontend ./frontend
docker push your-registry/impact-ai-backend:latest
docker push your-registry/impact-ai-frontend:latestProduction Setup:
- Use managed/hosted PostgreSQL
- Set production OpenAI API key
- Use proper ChatKit domain key registered at https://platform.openai.com
- Enable CORS for your frontend domain
- Use environment-specific secrets management
- Customize AI behavior β Edit
backend/app/constants.py(INSTRUCTIONS) - Add your companies β Run
backend/prisma/seed.pywith your data - Add your transactions β Use database migrations or API endpoints
- Create custom widgets β Design in https://widgets.chatkit.studio
- Deploy β Follow deployment guide above


