Conversational AI music artist recommendations.
- Python 51.2%
- TypeScript 43.5%
- Dockerfile 2.3%
- JavaScript 2.2%
- HTML 0.7%
| alembic | ||
| app | ||
| frontend | ||
| nginx | ||
| .env.example | ||
| .gitignore | ||
| .python-version | ||
| alembic.ini | ||
| CLAUDE.md | ||
| docker-compose.yml | ||
| Dockerfile | ||
| LICENSE | ||
| pyproject.toml | ||
| README.md | ||
| uv.lock | ||
Findarr
A self-hosted AI music discovery app. Chat with Claude to find artists you'll love — based on your taste, not charts.
How it works
Findarr is a split-pane chat interface: describe the music you love on the left, and artist suggestions appear as cards on the right. As you chat and react to suggestions, a taste profile builds up and informs future recommendations.
- Conversational discovery — the AI asks questions, learns your taste, and suggests artists when you're ready
- Artist cards — each suggestion shows genre tags, a one-sentence explanation, and links to Spotify, YouTube, Last.fm, and Bandcamp
- Feedback buttons — Like, Dislike, Meh, Already Have, or flag a suggestion as non-existent; each response updates your taste profile and prompts a follow-up
- Taste profile — a live prose summary of your musical preferences, updated automatically as the conversation develops
- Pluggable mode architecture — Similar Artists is the first mode; new discovery modes slot in without changing core logic
Requirements
- Python 3.12+ with uv
- Node.js 22+ (frontend dev only)
- An Anthropic API key
- Docker + Docker Compose (for containerised deployment)
Quick Start (Docker)
cp .env.example .env
# Edit .env — set ANTHROPIC_API_KEY and SECRET_KEY
docker compose up -d
The app is available at http://localhost:8088.
Development Setup
# Install Python dependencies
uv sync
# Copy and configure environment
cp .env.example .env # set ANTHROPIC_API_KEY
# Start the backend (auto-reload on :8000)
uv run uvicorn app.main:app --reload
# In a second terminal — start the frontend dev server (on :5173)
cd frontend
npm install
npm run dev
Open http://localhost:5173.
Environment Variables
| Variable | Default | Description |
|---|---|---|
DATABASE_URL |
sqlite+aiosqlite:///./findarr.db |
SQLite path or postgresql+asyncpg://… DSN |
ANTHROPIC_API_KEY |
— | Required. Claude API key |
SECRET_KEY |
— | Random secret for encrypting stored credentials (future use) |
LASTFM_API_KEY |
— | Optional. Enables artist images on cards |
LOG_LEVEL |
info |
debug, info, warning, or error |
Project Structure
app/
├── main.py # FastAPI app, lifespan, route registration
├── config.py # Pydantic settings
├── database.py # SQLAlchemy async engine
├── api/
│ ├── chat.py # POST /chat → SSE stream
│ ├── feedback.py # POST /feedback
│ └── taste_profile.py # GET /taste-profile
├── ai/
│ ├── client.py # Anthropic async client
│ ├── tools.py # suggest_artists + update_taste_profile tool schemas
│ └── prompts.py # System prompt builder
├── modes/
│ ├── base.py # Abstract ChatMode
│ ├── registry.py # Mode registry
│ └── similar_artists.py
├── models/
│ └── taste_profile.py # Singleton DB model
├── schemas/ # Pydantic request/response types
└── taste/
├── profile.py # DB helpers
└── updater.py # Feedback → profile mutations
frontend/src/
├── api/ # Typed API client + shared types
├── components/
│ ├── artists/ # ArtistGrid, ArtistCard, FeedbackButtons, ListeningLinks
│ ├── chat/ # ChatPane, MessageBubble, ChatInput
│ ├── layout/ # SplitPane
│ └── taste-profile/ # TasteProfileDrawer
└── hooks/ # useChat (SSE), useTasteProfile
Adding a New Mode
- Create
app/modes/your_mode.py— subclassChatMode, setmode_id,display_name,description, and implementsystem_instructions - Register it in
app/modes/registry.py - Pass
mode: "your_mode_id"in the chat request from the frontend
License
MIT — see LICENSE.