PromptLens is an observability stack for LLM applications and AI agents. It captures prompts, responses, tool calls, memory operations, RAG retrievals, sessions, and cost metrics, then surfaces them in a real-time dashboard similar to browser DevTools.
Use it to debug agent workflows, inspect what was sent to a model, trace failures, and understand usage patterns across sessions.
- Session tracing — group related LLM interactions into sessions with status and metadata
- Event ingestion — record typed events (prompts, tool calls, memory, RAG, and more) via REST API
- Real-time updates — WebSocket stream pushes new and updated events to the dashboard
- Analytics — dashboard metrics for throughput, latency, and cost over configurable time windows
- Timeline view — chronological event stream per session
- Plugin system — extend event types and integrations through the Python SDK plugin API
- Multi-provider SDK — instrument OpenAI, Ollama, and LangChain with minimal code changes
| Path | Description |
|---|---|
backend/ |
FastAPI API server, database models, WebSocket broadcaster, Alembic migrations |
Frontend/ |
TanStack Start dashboard (React, Vite, Nitro) |
sdk/ |
Python SDK for ingesting traces from your applications |
plugins/ |
Plugin documentation and examples |
scripts/ |
Demo scripts that generate sample traffic |
- Python 3.10+ for the backend and SDK
- Node.js 20+ for the frontend
- Optional: Docker and Docker Compose for containerized deployment
- Optional: Ollama or an OpenAI-compatible API for running the demo scripts
cd backend
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
cp .env.example .env
uvicorn app:app --reload --host 127.0.0.1 --port 8000Verify the API is running:
- Health check: http://127.0.0.1:8000/api/v1/health
- OpenAPI docs (development only): http://127.0.0.1:8000/docs
In a second terminal:
cd Frontend
npm install
npm run devOpen http://localhost:5173.
In development, the dashboard connects directly to http://127.0.0.1:8000/api/v1. You can override the URL and test connectivity from Settings in the UI.
pip install -e "sdk/[ollama,langchain]"
# Ollama (requires a running Ollama instance)
python scripts/e2e_ollama.py --model mistral:latest
# LangChain memory and RAG demos
python scripts/e2e_langchain_memory.py
python scripts/e2e_langchain_rag.py
python scripts/e2e_prompt_inspector.pyOpen a specific session in the dashboard by appending ?sessionId=<uuid> to any route.
| Route | Purpose |
|---|---|
/ |
Overview and navigation |
/sessions |
Browse and filter sessions |
/prompts |
Prompt and response inspector |
/tools |
Tool call events |
/memory |
Memory read/write events |
/rag |
RAG retrieval events |
/context |
Context assembly events |
/agents |
Agent-related activity |
/timeline |
Chronological session timeline |
/analytics |
Aggregated metrics and charts |
/settings |
API URL, API key, and connection status |
Minimal example with OpenAI:
from promptlens import PromptLens
from openai import OpenAI
lens = PromptLens(model="gpt-4o", agent="my-agent")
lens.instrument_openai()
client = OpenAI()
client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Hello"}],
)Install optional integrations:
pip install 'promptlens[openai]'
pip install 'promptlens[ollama]'
pip install 'promptlens[langchain]'See sdk/README.md for full SDK documentation and sdk/PLUGIN_SDK.md for plugin development.
PromptLens does not auto-generate API keys. When you want to protect the API, create a secret yourself and configure it on the server and all clients.
Any long random string works. Examples:
# Recommended
openssl rand -hex 32
# Alternative
python -c "import secrets; print(secrets.token_urlsafe(32))"Add the key to backend/.env:
PROMPTLENS_API_KEY=your-generated-secret-hereOr export it in the shell before starting the server:
export PROMPTLENS_API_KEY=your-generated-secret-here
uvicorn app:app --reload| Client | Configuration |
|---|---|
| Dashboard | Settings → API key (stored in browser localStorage) |
| Dashboard (build-time) | VITE_PROMPTLENS_API_KEY environment variable |
| Python SDK | PROMPTLENS_API_KEY environment variable |
| HTTP clients | Header X-API-Key: <key> or Authorization: Bearer <key> |
| WebSocket | Query parameter ?api_key=<key> or the same headers |
| Environment | API key set | Result |
|---|---|---|
development |
No | Auth disabled; all routes open except documented public endpoints |
development |
Yes | Auth required on all /api/v1 routes except /health |
production |
Required | Server refuses to start without PROMPTLENS_API_KEY |
The /api/v1/health endpoint remains public so load balancers and orchestrators can probe availability.
All backend settings use the PROMPTLENS_ prefix. See backend/.env.example for the full list.
| Variable | Default | Description |
|---|---|---|
PROMPTLENS_ENVIRONMENT |
development |
development, staging, or production |
PROMPTLENS_API_KEY |
(unset) | Shared secret for API authentication |
PROMPTLENS_DATABASE_URL |
sqlite+aiosqlite:///./data/promptlens.db |
SQLAlchemy database URL |
PROMPTLENS_RUN_MIGRATIONS_ON_STARTUP |
false |
Run Alembic migrations on server start |
PROMPTLENS_RATE_LIMIT_ENABLED |
true |
Enable write-endpoint rate limiting |
PROMPTLENS_RATE_LIMIT_WRITE_REQUESTS_PER_MINUTE |
120 |
Max POST/PUT/PATCH/DELETE requests per client per minute |
PROMPTLENS_MAX_EVENT_METADATA_BYTES |
524288 |
Maximum JSON metadata size per event (512 KB) |
PROMPTLENS_LOG_LEVEL |
INFO |
Log level |
PROMPTLENS_LOG_FORMAT |
text |
text or json |
PROMPTLENS_CORS_ORIGINS |
localhost dev origins | JSON array of allowed CORS origins |
PostgreSQL example:
PROMPTLENS_DATABASE_URL=postgresql+asyncpg://promptlens:promptlens@localhost:5432/promptlens| Variable | Default | Description |
|---|---|---|
PROMPTLENS_BASE_URL |
http://localhost:8000/api/v1 |
Ingest API base URL |
PROMPTLENS_API_KEY |
(unset) | API key sent with SDK requests |
PROMPTLENS_MODEL |
unknown |
Default model name for new sessions |
PROMPTLENS_AGENT |
(unset) | Default agent name |
PROMPTLENS_TIMEOUT |
30 |
HTTP request timeout in seconds |
| Variable | Default | Description |
|---|---|---|
VITE_PROMPTLENS_API_URL |
http://127.0.0.1:8000/api/v1 in dev |
API base URL override at build time |
VITE_PROMPTLENS_API_KEY |
(unset) | API key override at build time |
Runtime overrides for URL and API key are available in Settings without rebuilding.
Base path: /api/v1
| Area | Endpoints |
|---|---|
| Health | GET /health |
| Sessions | GET/POST /sessions, GET/PATCH/DELETE /sessions/{id} |
| Events | GET/POST /events, GET/PATCH/DELETE /events/{id} |
| Timeline | GET /sessions/{id}/timeline |
| Analytics | GET /analytics/dashboard, additional analytics routes |
| Plugins | GET/POST /plugins/registry |
| WebSocket | WS /ws/events |
Interactive API documentation is available at /docs when PROMPTLENS_ENVIRONMENT=development.
cd backend
docker compose up --buildThis starts the API on port 8000 with SQLite storage and runs migrations on startup.
The production compose file at the repository root runs all services behind Caddy with optional automatic HTTPS.
1. Configure environment
cp .env.prod.example .env.prodEdit .env.prod and set at minimum:
| Variable | Example | Purpose |
|---|---|---|
PROMPTLENS_API_KEY |
output of openssl rand -hex 32 |
API authentication |
POSTGRES_PASSWORD |
strong random password | Database password |
PROMPTLENS_SITE_ADDRESS |
:80 or dashboard.example.com |
Caddy listen address |
ACME_EMAIL |
[email protected] |
Let's Encrypt email (HTTPS only) |
PROMPTLENS_CORS_ORIGINS |
'["https://dashboard.example.com"]' |
Allowed browser origins |
2. Start the stack
chmod +x scripts/prod-up.sh
./scripts/prod-up.shOr manually:
docker compose --env-file .env.prod -f docker-compose.prod.yml up --build -d3. Open the dashboard
| Mode | PROMPTLENS_SITE_ADDRESS |
URL |
|---|---|---|
| Local HTTP | :80 |
http://localhost |
| Public HTTPS | dashboard.example.com |
https://dashboard.example.com |
Point DNS for your domain to the server. Caddy obtains a TLS certificate automatically when PROMPTLENS_SITE_ADDRESS is a public domain and ACME_EMAIL is set.
Architecture
Browser
|
v
Caddy (:80 / :443, TLS)
|-- /api/v1/* --> API (FastAPI + PostgreSQL)
'-- /* --> Frontend (TanStack Start, port 3000)
The frontend is built with VITE_PROMPTLENS_API_URL=/api/v1 so the dashboard and API share the same origin through Caddy — no cross-origin setup required for the UI.
Enter the same PROMPTLENS_API_KEY in Settings → API key on first visit (or set VITE_PROMPTLENS_API_KEY in .env.prod before building).
CORS for external SDK clients
If your Python SDK runs outside the browser (different machine or domain), add its origin to PROMPTLENS_CORS_ORIGINS:
PROMPTLENS_CORS_ORIGINS='["https://dashboard.example.com","http://my-app.internal:8080"]'nginx alternative
An example nginx config is in docker/nginx.conf if you prefer nginx over Caddy.
Frontend only (without Docker)
If you host the frontend separately (e.g. on a PaaS):
cd Frontend
npm ci
VITE_PROMPTLENS_API_URL=https://api.example.com/api/v1 npm run build
npm run startSet PROMPTLENS_CORS_ORIGINS on the API to include your frontend URL.
See SECURITY.md for production hardening guidance.
# Backend
cd backend
pip install -r requirements-dev.txt
pytest
# SDK
cd sdk
pip install -e .
pytest
# Frontend production build
cd Frontend
npm run buildcd Frontend
npm run lint
npm run formatcd backend
alembic revision --autogenerate -m "describe change"
alembic upgrade head
alembic checkCI runs backend tests, SDK tests, frontend build, and alembic check on every push and pull request.
Application (Python SDK)
|
| HTTP POST /events, /sessions
v
FastAPI Backend -----> SQLite / PostgreSQL
|
| WebSocket /ws/events
v
TanStack Dashboard
- Your application uses the SDK to create sessions and emit events.
- The backend persists events and broadcasts changes over WebSocket.
- The dashboard subscribes to the stream and renders sessions, timelines, and analytics.
- Treat event metadata as sensitive; it may contain prompts, tool outputs, and retrieved documents.
- Always set
PROMPTLENS_API_KEYin production and restrictPROMPTLENS_CORS_ORIGINSto trusted dashboard origins. - Use PostgreSQL instead of SQLite when running more than one API instance.
- Terminate TLS at a reverse proxy or load balancer in production.
- OpenAPI docs (
/docs) are disabled automatically outside development.
Report security issues privately as described in SECURITY.md.
Contributions are welcome. See CONTRIBUTING.md for setup instructions and pull request guidelines.
MIT — see LICENSE.
sdk/README.md— SDK installation and instrumentationsdk/PLUGIN_SDK.md— building custom pluginsplugins/README.md— plugin packaging and entry pointsSECURITY.md— security policy and deployment guidanceCONTRIBUTING.md— development workflow
| File | Purpose |
|---|---|
Logo.png |
Source logo — edit this file |
Frontend/public/logo.png |
Logo used in the app (copy of Logo.png) |
After editing Logo.png, refresh the app copy:
cp Logo.png Frontend/public/logo.png
sips -Z 32 Logo.png --out Frontend/public/favicon.png