You use ChatGPT for work emails and personal recipes. Claude for client code and weekend projects. Stop paying for separate personal and business accounts. LedgerLLM routes every API call through a proxy, classifies each one as personal or business, and shows you exactly what's deductible — with one subscription, one API key, one bill.
ChatGPT Plus for personal use ($20/mo). ChatGPT Team for work ($25/user/mo). Claude Pro for personal ($20/mo). Claude Team for work ($30/user/mo). You're paying for the same models twice — just to separate personal from business.
Even if you use one account for everything, how do you prove to the IRS that 80% of your AI spend was business? You can't. So you either overpay for a separate business account, or you lose the deduction entirely.
Spreadsheets. Notepad. "Was that prompt about the client or about dinner?" Every API call is mixed use — you can't manually tag hundreds of interactions per month. It's either too much work or too inaccurate to defend.
Route every AI client — Cursor, Claude Desktop, Hermes, any OpenAI-compatible tool — through a single LiteLLM proxy. One set of API keys, one bill, one source of truth. No more juggling personal vs work accounts.
Each API call is classified as personal, business, or mixed by Jev AI — automatically, in the background, no manual tagging. You get a confidence score, business probability, and deductible probability under IRC §162.
A local dashboard shows spend by provider, project, and tax category. You see exactly what percentage is business, what's deductible, and what's personal. Filter by project, export for Schedule C, or just check the numbers.
Export a year-end report with deductible total, interaction count, and per-project breakdown. Every classification has a confidence score and an audit trail. Defensible documentation, not a guess.
Example assumes $30/mo AI spend, 80% business use, 24% effective tax rate (CA single filer). Actual savings vary. Not tax advice — consult a CPA.
Everything you need to use one AI subscription for both personal and business — and prove the split for taxes.
LiteLLM proxy logs every API call — model, tokens in/out, dollar cost — to Postgres. No manual tracking, no spreadsheets. Just route your AI traffic through the proxy.
Each interaction is classified as personal, business, or mixed by Jev — with business score, deductible probability, and confidence. Fail-closed: errors default to personal (never business).
Define projects with keywords in config.yaml. Prompts are matched automatically — first match wins, case-insensitive. Or toggle to strict mode and let Jev handle project attribution contextually.
Expose spend tracking as MCP tools for any AI client. Classify interactions, query reports, manage projects, export tax data — all from Claude Desktop, Cursor, Hermes, or any MCP-compatible client.
Local FastAPI dashboard (port 8092) with CSS charts — no Chart.js dependency. Four tabs: Overview, Tax Breakdown, Projects, Providers. Light/dark theme, reads SQLite read-only.
Generate year-end reports for Schedule C. Export deductible total, interaction count, provider breakdown, and project spend. Includes disclaimer: not tax advice — consult a CPA.
Switch between hybrid (keywords first, Jev fallback), strict (always Jev for both tax + project), or keyword (free, no Jev). Toggle live via MCP without restarting the server. Cache clears instantly.
Everything runs on your machine. SQLite at ~/.jev-ai-tracker/attribution.db, Postgres in Docker, LiteLLM bound to 127.0.0.1. No data leaves your network except the 500-char Jev API call (which can be PII-redacted).
stdio for local AI clients (Claude Desktop, Hermes, Cursor) — zero network, instant startup. SSE/HTTP for Docker-based or remote setups. Same 24 tools, same interface, your choice of transport.
LiteLLM routes to 100+ providers. LedgerLLM ships with pricing for these models out of the box:
From zero to a working spend tracker in ~15 minutes. Requires Docker + Python 3.10+.
git clone https://github.com/clance1/ledgerllm.git
cd ledgerllm
pip install -e .
cd litellm/
cp .env.example .env # Fill in OPENAI_API_KEY, ANTHROPIC_API_KEY, etc.
cp config.example.yaml config.yaml
# Ensure store_prompts_in_spend_logs: true in config.yaml
docker compose up -d
cd ..
cp config.example.yaml config.yaml # Define your projects + keywords
export LITELLM_MASTER_KEY=$(grep LITELLM_MASTER_KEY litellm/.env | cut -d= -f2)
export JEV_API_KEY=apikey_... # Get one at https://docs.typesafe.ai
export JEV_MODE=dual
jev-ai-tracker init # Checks config, LiteLLM, provider keys, inits DB
jev-ai-tracker test # Test each provider API key
jev-ai-tracker classify # Pull spend logs + classify
jev-ai-tracker report # View monthly report
jev-ai-tracker dashboard # FastAPI on 127.0.0.1:8092 (Docker)
# Open http://127.0.0.1:8092 in your browser
Use mcp-stdio for local clients (recommended). Use mcp for Docker SSE.
{
"mcpServers": {
"ledgerllm": {
"command": "jev-ai-tracker",
"args": ["mcp-stdio"],
"env": {
"JEV_API_KEY": "apikey_...",
"LITELLM_MASTER_KEY": "sk-...",
"JEV_MODE": "dual"
}
}
}
}
# Classify new spend every night at 11 PM
0 23 * * * cd /path/to/ledgerllm && LITELLM_MASTER_KEY=sk-... JEV_API_KEY=apikey_... jev-ai-tracker classify
Architecture, CLI reference, MCP tools, and the cloud roadmap.
jev-ai-tracker init
Guided setup — checks config, env, LiteLLM, provider keys, inits DB
jev-ai-tracker setup
Initialize SQLite database
jev-ai-tracker classify
Pull spend logs from LiteLLM, classify with Jev + keywords
jev-ai-tracker report
Generate monthly report (tax + project breakdown)
jev-ai-tracker status
Database status, unclassified count, mode, routing strategy
jev-ai-tracker test
Test each provider API key (OpenAI, Anthropic, Gemini, xAI, Jev)
jev-ai-tracker mcp
Start MCP server in Docker (SSE transport, :8090)
jev-ai-tracker mcp-stdio
Start MCP server locally (stdio, no Docker — recommended)
jev-ai-tracker dashboard
Start spend dashboard in Docker (FastAPI, :8092)
classify_interaction — Classify a single AI interactionimport_subscription_usage — Import conversation history for classificationget_routing_strategy — Check current strategy (hybrid/strict/keyword)set_routing_strategy — Toggle strategy at runtimeget_monthly_report — Monthly tax + project breakdownget_deductible_total — Year-to-date deductible spendexport_for_tax_filing — Year-end Schedule C exportget_spend_summary — Spend by provider + categoryget_dual_report — Combined tax + project reportadd_project — Add a project with keyword routinglist_projects — List all configured projectsremove_project — Remove a projectget_project_spend — Spend for a specific projectget_all_project_spend — Spend across all projectsget_unassigned_spend — Spend not routed to any projectget_project_monthly_report — Monthly project reportset_budget — Set a monthly budget for a tax tagset_project_budget — Set a monthly budget for a projectget_model_pricing_info — Per-million-token pricing for a modellist_all_model_pricing — All models grouped by providercompute_token_cost — Compute dollar cost from token countsget_mode — Current mode, routing strategy, and projectsget_status — DB status, unclassified count, versionget_available_models — Models available through LiteLLMKeyword routing first (free, deterministic). If no keyword matches, Jev handles project attribution as an additional question in the same API call. Best balance of cost and accuracy.
Always use Jev for both tax classification AND project attribution. Skip keyword routing entirely. Highest accuracy — Jev understands context, not just word matching.
Keyword routing only, no Jev for project attribution. Free, fully deterministic. Unmatched interactions get no project routing. Jev may still run for tax classification if JEV_MODE=dual.
mode: dual # Set via JEV_MODE env var: "tax", "project", or "dual"
projects:
- name: poker-tracker
keywords: [poker, tracker, fastapi]
tax_category: business
description: "Poker tracker app"
- name: cocktail-rating-app
keywords: [cocktail, rating, react, expo]
tax_category: business
description: "Cocktail rating app"
- name: homelab
keywords: [docker, homelab, opnsense, tailscale]
tax_category: business
description: "Homelab infrastructure"
JEV_API_KEY
Required for tax/dual mode. Get at docs.typesafe.ai
LITELLM_MASTER_KEY
Required. LiteLLM proxy admin key
JEV_MODE
dual (default), tax, or project
ROUTING_STRATEGY
hybrid (default), strict, or keyword
LITELLM_API_URL
Default: http://127.0.0.1:4000
MCP_TRANSPORT
stdio (default) or sse
ATTRIBUTION_DB_PATH
Default: ~/.jev-ai-tracker/attribution.db
LedgerLLM Cloud is in production. The cloud version will be a managed LiteLLM proxy — users point their AI client at ledgerllm.dev/v1/, add provider API keys once via the dashboard, and every call is automatically proxied, logged, classified, and visible in a real-time dashboard.
The OSS version is the foundation — same engine, same MCP tools, same Jev classification. The cloud adds zero-config setup, automatic capture, and a hosted dashboard. Users who want data sovereignty stay on the self-hosted version.
LedgerLLM is designed with security and data sovereignty as first principles. Here's how data flows, what's protected, and what you control.
Every API call routes through LiteLLM on 127.0.0.1:4000. Spend logs (model, tokens, cost, prompt content) are stored in Postgres on 127.0.0.1:5432. Both are bound to localhost — no remote access.
Classification results and project routing are stored in ~/.jev-ai-tracker/attribution.db. File permissions set to 0600 (owner-only). The dashboard reads this file in read-only mode (mode=ro).
The first 500 characters of each prompt are sent to api.typesafe.ai for classification. Jev does not store request bodies. Content is forwarded to TypeSafe AI's upstream model for processing but not for training. PII redaction is recommended before transmission.
Your provider API keys (OpenAI, Anthropic, etc.) are stored in litellm/.env (gitignored). LiteLLM uses them to route calls to the correct provider. Keys are never logged, never displayed in the dashboard, and never sent to Jev.
All Jev API parse errors, timeouts, and edge cases default to personal (non-deductible) — never business. A confidence threshold of 0.25 filters low-confidence business classifications down to personal. Conservative by design: better to miss a deduction than to overclaim one.
Everything runs on your machine. SQLite, Postgres, LiteLLM, dashboard — all local. The only data that leaves your network is the 500-char Jev API call. No telemetry, no analytics, no phone-home. Your spend data never touches a server you don't control.
LiteLLM binds to 127.0.0.1:4000. Postgres binds to 127.0.0.1:5432. Dashboard binds to 127.0.0.1:8092. MCP server binds to 127.0.0.1:8090. No service is exposed to the network by default. To capture from other devices, bind to your Tailscale IP explicitly.
All .env files are gitignored. config.yaml (with project keywords) is gitignored. *.db files are gitignored. API keys use os.environ/ references in LiteLLM config (not hardcoded). No secrets in tracked source files.
store_prompts_in_spend_logs: true is required in LiteLLM config for prompt content to be stored. Without it, only model + tokens + cost are logged — no prompt text. The Jev classifier degrades gracefully to keyword-only routing when prompts aren't available.
Before sending prompt content to Jev, a regex-based PII redaction function strips API keys (sk-...), email addresses, phone numbers, bearer tokens, and AWS keys. The 500-char limit provides data minimization. Full redaction is a 20-line addition — see the security review.
The prompt_snippet column exists in the SQLite database (for classification), but the dashboard never displays it in charts, tables, or any UI element. This is a hard rule — enforced in the FastAPI routes that serve dashboard data.
The entire codebase is MIT licensed and open source. Every line is auditable. No obfuscated binaries, no telemetry SDKs, no closed-source dependencies beyond the Jev API call (which you can disable with JEV_MODE=project).
0600 permissions. For encrypted storage, consider SQLCipher. The Postgres spend logs are also unencrypted (standard postgres:16 image).
When the cloud version launches, it will add these security measures on top of the OSS foundation:
tenant_id, enforced at the database level