Tool

Analyze LLM costs and find cheaper model routing strategies

Frugon is a free, local, open-source LLM cost analyzer that reads your call logs and recommends which calls to route to cheaper models.

Works with openai

91
Spark score
out of 100
Updated 7 days ago
Version 0.2.6
Models
gpt 4o

Add to Favorites

Why it matters

Frugon helps teams reduce their LLM API bills by analyzing call logs locally to identify which requests can be routed to cheaper models without sacrificing quality, then quantifying the exact savings and providing actionable routing recommendations.

Outcomes

What it gets done

01

Capture LLM API calls via local proxy and save them as structured logs

02

Calculate current spending by tokenizing requests and applying provider pricing

03

Sample traffic through candidate models to measure quality trade-offs

04

Generate routing recommendations showing which calls to move and projected monthly savings

Install

Add it to your toolbox

Run in your project directory:

curl -fsSL https://spark.entire.vc/get/rodiun-frugon | bash

Overview

Frugon

Frugon is a free, local, open-source LLM cost analyzer: point it at your call logs and it prices your traffic against candidate models entirely on your machine, recommending which calls to route to a cheaper model and how much you'd save, with optional quality measurement using your own API keys. Use it when you have real LLM call logs and want an honest, locally-computed routing recommendation before switching models; treat the offline estimate as a starting point, not a verified guarantee - confirm with --measure before actually switching.

What it does

Frugon is a free, local, open-source LLM cost analyzer: point it at your LLM call logs and it shows, entirely on your own machine, how much you'd save by switching or routing models - your data never leaves your machine, and your provider keys go straight to your own providers, never to Frugon. It reads JSONL files in the OpenAI request/response format, produced either by frugon capture (a local HTTP proxy shim that forwards calls unchanged to your real provider while saving each one as a JSONL line) or written directly by your own logging middleware in a documented shape (model required, usage.prompt_tokens/completion_tokens preferred with a fallback tokenizer, timestamp optional).

Cost analysis (frugon analyze) is fully local - no LLM calls, no network - pricing your logged calls against every candidate model using tokenizers and synced pricing data, and outputs a routing recommendation: what percent of calls to move to a cheaper model, what percent to keep on the current model because they're "hard," and the resulting projected monthly saving, with an explicit quality caveat attached. An optional --measure flag (needing pip install 'frugon[measure]' and your own provider API key) samples real prompts through candidate models using your own keys to move from an offline estimate toward an actual measurement, and --measure --judge scores each candidate's output quality with a pairwise, bias-resistant judge (anonymized A/B comparison, defaulting to "tie" unless one answer is clearly better, with a second single-answer check so a tie between two failed answers isn't miscounted as success). --report savings.html (or .md) exports a shareable report for a PR, Slack thread, or budget review. Everything runs comfortably past 100k log records, with the bundled ~56,100-call demo pricing in a few seconds.

The tool is deliberately narrow: six commands (analyze, capture, models, update, pricing, quality) and three capabilities (cost analysis, quality visibility, routing recommendation) - gateways, live routing proxies, web UIs, and multi-tenant accounts are explicitly out of scope. A separate hosted product (frugon.rodiun.io) offers to actually route traffic and hold the savings automatically; the open-source CLI itself is a one-time snapshot tool.

When to use - and when NOT to

Use Frugon when you have real LLM call logs (or can capture them via the bundled proxy shim) and want an honest, locally-computed answer to "how much of my spend could move to a cheaper model without losing quality" - agent builders whose GPT-4o-class agents handle mostly easy hops, AI dev teams where the monthly bill is real money, RAG/support pipelines where the final answer call doesn't need to be a frontier model, data-ETL pipelines doing repeatable batch extraction, or indie hackers where every saved dollar is runway. Because everything is local and keyless by default (measurement is opt-in and uses your own keys), it's also a fit for cost analysis under a strict no-data-leaves-the-machine requirement.

Do not treat a bare analyze run as a verified quality guarantee: it's an offline estimate built on population-level priors (LMArena quality tiers, RouteLLM research savings bands), not a measurement of your specific prompts, and the source is explicit that "within tolerance" needs confirming with --measure before you actually switch. Sampling-based measurement can also miss tail cases, a single draw per prompt can't reveal run-to-run variance (no temperature control), and per-call pricing cannot see second-order costs like retries or human review triggered by a weak answer - so a model that looks cheaper per call is not automatically cheaper once those effects are counted.

Inputs and outputs

Input is a JSONL log file of LLM calls, either captured live (frugon capture --out ./logs.jsonl, then pointing your app's base URL at the local shim, e.g. OPENAI_BASE_URL=http://127.0.0.1:8787) or written directly in the documented request/response/usage shape. Install and run:

uvx frugon analyze ./logs.jsonl
# or: uv tool install frugon && frugon analyze ./logs.jsonl

Output is a terminal report: calls analyzed, current baseline spend, the recommended split (percent routed to a cheaper candidate, percent kept on the current model, percent already on the cheapest option), new projected spend, percent saved, and a per-candidate comparison table with price, percent lower, and quality tier. When --judge runs, each candidate also gets an Eff. $/success figure - price divided by judged-success rate rather than raw price - with explicit n/a reasons (unpriced, no verdicts, zero judged successes) instead of a misleading $0.00 or division-by-zero result. --report savings.html/.md writes the same analysis as a shareable file.

Integrations

Frugon integrates with any OpenAI-API-compatible client via its capture proxy shim, requiring only a base-URL redirect (OPENAI_BASE_URL or the equivalent in code) with no latency overhead on localhost and no calls to any Frugon-operated endpoint. Model pricing syncs from the LiteLLM registry and model quality tiers from LMArena rankings (frugon update refreshes the full live roster); routing recommendations draw on a curated set of current top models per OpenRouter usage rankings. --measure and --judge call out directly to whichever LLM providers you configure via standard provider API keys (e.g. OPENAI_API_KEY), including locally-served models, which can be used keylessly as both a candidate and a judge.

Who it's for

Teams and individual developers who already have (or can capture) real LLM call logs and want a locally-run, honestly-caveated answer to how much of their spend could shift to a cheaper model - agent builders, AI dev teams tracking a real monthly bill, RAG/support pipelines, data-ETL pipelines doing repeatable extraction, and indie hackers optimizing runway - especially anyone who wants that analysis to happen without sending logs, keys, or prompts to a third party. It is licensed under MIT.

Source README

Frugon

Your LLM bill is leaking - see exactly where, on your machine.

Free, local, open-source LLM cost analyzer. Point Frugon at your LLM call logs
and see - on your machine - how much you'd save by switching or routing models.

PyPI
License: MIT
CI
Python
Platforms

Your data never leaves your machine. Your keys go straight to your own providers. Nothing reaches us.

Frugon analyzing a log file and recommending a routing split

Install & run

# one-shot (no install)
uvx frugon analyze ./logs.jsonl

# permanent install
uv tool install frugon          # or: pipx install frugon / pip install frugon
frugon analyze ./logs.jsonl

# for --measure (optional): samples real prompts through your own provider keys
uv tool install 'frugon[measure]'   # or: pip install 'frugon[measure]'
frugon analyze ./logs.jsonl --measure

No logs yet? See Getting your logs below, or run frugon analyze --demo to see it work on a bundled sample.

Getting your logs

frugon reads JSONL files in the OpenAI request/response format. There are two ways to produce them.

Option A - frugon capture (proxy shim)

frugon capture is a local HTTP proxy that sits between your app and your provider.
Every call is forwarded unchanged to your real provider and saved as one JSONL line.

# Start the shim (default port 8787, output file capture.jsonl)
frugon capture --out ./logs.jsonl

# Then point your app's base URL at the shim instead of api.openai.com:
OPENAI_BASE_URL=http://127.0.0.1:8787 your-app           # bash / zsh
$env:OPENAI_BASE_URL="http://127.0.0.1:8787"; your-app   # PowerShell (Windows)
# or in code: client = OpenAI(base_url="http://127.0.0.1:8787/v1")

Options: --port, --out, --upstream (override the forwarding target), --verbose
(print one line per captured call to verify it's recording), --proxy (opt in to route
upstream calls through a proxy - by default frugon ignores any ambient HTTP_PROXY /
HTTPS_PROXY, so your API key never passes through a third-party proxy). The shim adds no
latency overhead on localhost and makes no calls to any frugon endpoint.

Option B - write JSONL directly

If you already capture logs (e.g. via middleware or a provider SDK callback), write one
JSON object per line with this shape:

{
  "model": "gpt-4-turbo",
  "request": {
    "messages": [
      {"role": "system", "content": "You are a helpful assistant."},
      {"role": "user",   "content": "Summarise this document: ..."}
    ]
  },
  "response": {
    "choices": [{"message": {"content": "Here is the summary: ..."}}]
  },
  "usage": {
    "prompt_tokens": 312,
    "completion_tokens": 84
  },
  "timestamp": "2024-11-01T14:22:01Z"
}

usage.prompt_tokens / usage.completion_tokens - preferred when present; frugon falls
back to its own tokenizer when absent. timestamp is optional but enables frugon to
project costs over a real observed span. model is required; everything else degrades
gracefully.

5-minute path from install to first analysis

uv tool install frugon          # or: pipx install frugon / pip install frugon
frugon capture --out ./logs.jsonl &   # start the proxy in the background
# ... run your app, make some LLM calls ...
frugon analyze ./logs.jsonl     # see the cost breakdown and routing recommendation

What it does

  • Cost analysis - fully local, no LLM calls, no network. Tokenizers + pricing + arithmetic on your machine.
  • Quality visibility (--measure, optional) - samples your traffic through candidate models using your own API keys, sent directly to your own providers. Never to us. --measure needs pip install 'frugon[measure]' and a provider API key (OPENAI_API_KEY, etc.); calls go to your own provider, never to us. On --demo, sampling is pinned to a single OpenAI model so the try-out needs only OPENAI_API_KEY; on your own logs, --measure samples the actual recommendation.
  • Routing recommendation - "move these X% of calls to a cheaper model and save ~$Y/mo; keep the hard Z% where they are." Comes with an explicit quality caveat so you know what you're trading.
    Run frugon models to see the model names available for --candidates (optionally frugon models gpt-4o to filter by substring).
  • Share the result - add --report savings.html (or .md) to write a clean, shareable report you can drop into a PR, a Slack thread, or a budget review.
  • Fast on real logs - everything runs locally and is comfortable well past 100k records. The bundled ~56,100-call demo (frugon analyze --demo) prices in a few seconds. Very large logs (>200k records) may take a little longer; Frugon shows a live progress bar and a one-line heads-up so you can see it working. There's no hard limit.

Example output

$ frugon analyze --demo --candidates claude-sonnet-4-5,gpt-4.1,claude-haiku-4-5,gemini-2.5-flash,deepseek-v4-flash

┌─ frugon · cost analysis ────────────────────────────────────────────────────┐
│                                                                             │
│   Analyzed      56,100 calls  ·  baseline gpt-5.5 (your current model)      │
│   Current spend $549.46 / mo                                                │
│                                                                             │
│     Route  36,100 easy calls (64.4%)  →  deepseek-v4-flash   within         │
│   tolerance                                                                 │
│     Keep   10,000 hard calls (17.8%)  →  gpt-5.5                            │
│     Keep   10,000 already on deepseek-v4-flash (17.8%)   already optimal    │
│   — no action                                                               │
│                                                                             │
│   New spend     $343.91 / mo                                                │
│                                                                             │
│   SAVING        $205.55 / mo    ·    37.4% lower                            │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘
                                                                               
  Candidates considered                                                        
  claude-sonnet-4-5  $452.23 / mo  17.7% lower  Strong   considered            
  gpt-4.1            $405.89 / mo  26.1% lower  Capable  considered            
  claude-haiku-4-5   $377.82 / mo  31.2% lower  Capable  considered            
  gemini-2.5-flash   $356.35 / mo  35.1% lower  Strong   considered            
  deepseek-v4-flash  $343.91 / mo  37.4% lower  Strong   recommended           
  Each candidate is shown under the same quality-preserving split (easy calls  
  to the candidate, hard calls kept on baseline); the biggest saving is the    
  headline recommendation, and when savings tie at the precision shown the    
  higher quality tier wins. Run --measure --judge to score each candidate's    
  quality.                                                                     

  Accounting   36,100 routed + 10,000 kept (gpt-5.5) + 10,000 already on 
               cheaper deepseek-v4-flash  =  56,100 analyzed
  Upper bound  a full swap to deepseek-v4-flash saves ~98.1% — run with 
               --verbose for detail
  Quality tier gpt-5.5: Elite  →  deepseek-v4-flash: Strong   (LMArena)
  Prices       synced 2026-07-02
  Quality      synced 2026-07-02

⚠ Quality is not verified — 'within tolerance' is an offline estimate;
  run --measure to confirm it on your real outputs before you switch.

  Your data never leaves your machine. Your keys go to your own providers.
→ Route every call automatically and hold the savings:  https://frugon.rodiun.io

Recommendations use a curated set of current top models across providers, drawn
from OpenRouter usage rankings. Prices synced 2026-07-02 from the LiteLLM 
registry. Run `frugon update` for the full live roster.
This is bundled sample data — run `frugon analyze <your-logs>` for a 
recommendation on your own logs.

Your numbers depend on your logs and your locally synced pricing/quality data.
Run frugon analyze --demo --candidates claude-sonnet-4-5,gpt-4.1,claude-haiku-4-5,gemini-2.5-flash,deepseek-v4-flash
to see the same output on your machine.

Quality tiers for reasoning models reflect the model at its default/typical
reasoning effort - effort changes how many tokens a call spends thinking, not
its per-token rate, so it never affects the price shown above.

How it's different

A provider's billing dashboard tells you what you already spent, and a raw
token counter prices a single call - Frugon prices your real logs against
every model, locally, and tells you which calls to move and which to keep.

Realistic savings

Based on RouteLLM's published research (LMSYS):

Traffic mix Typical saving
General mixed workload 30 - 50%
Easy / repetitive (high MT-Bench similarity) up to ~85%
Hard reasoning / MMLU-heavy ~30%

Your actual number comes from your logs. Frugon never inflates - it shows what the math says for your data.

Limitations

Frugon volunteers its edges. A few things are worth stating plainly before you
rely on a number.

analyze is an offline estimate. The cost analysis reads your logs, prices
them, and applies an easy/hard split heuristic without making a single model
call. It never sees a candidate model's actual output, so "within tolerance" is a
projection, not a measurement. The quality tiers come from LMArena and the savings
bands come from RouteLLM research; both are population priors drawn from public
leaderboards and studies. They describe how models tend to compare across many
users, not a guarantee for your specific prompts. To turn the estimate into a
measurement on your own traffic, run --measure, and --measure --judge to score
it.

Sampling can miss the tail. --measure and --judge grade a sample of your
prompts, not every one. A rare or unusual case may never appear in the sample, and
an average across the sample can hide a model that degrades badly on a small slice
of hard inputs. Raise --samples to widen coverage, and re-run on a fresh sample
to see whether the verdict holds.

A tie gets a second, absolute check. The judge is pairwise: it sees your
current model's answer and the candidate's answer, anonymised as A and B in a
randomised order, and it defaults to a tie unless one answer is clearly and
materially better. That design removes label and position bias, but a tie alone
is silent about why - it covers both "both answers are equally good" and "both
answers equally failed to address the prompt", and only the first is a genuine
judged success. So every tie gets a second, single-answer check: does this
answer attempt to address the prompt at all? When neither side does, --judge
marks the row [both failed] and excludes it from the judged-success count -
a tie no longer silently counts as a win. The check's default is deliberately
honest rather than suspicious: an ambiguous reply from the check itself, or a
transient fault that exhausts its retries, resolves to "addressed" - so the
[both failed] flag under-reports shared failure rather than over-reports it,
and a ~ next to a candidate's Eff. $/success figure (see below) means one or
more of those checks could not complete, so the true rate may be a little worse
than shown.

Eff. $/success turns quality and price into one number. When --judge
runs, each candidate's row divides its price by its judged-success rate - what
you actually pay per answer that held up, not per call made. A /mo suffix
means the figure is on the monthly-projection basis; otherwise it's the
observed-sample basis. It reads n/a in three honest cases: n/a (unpriced)
when this run has no dollar figure for the candidate at all; n/a (no verdicts) when every comparison on that candidate errored before a verdict
was reached; and n/a (0 judged successes) when the candidate has verdicts
but none of them counted as a success (a division by zero is never shown as
$0.00 or $inf - it's named). For the routed candidate on a split
recommendation, the price side of the division is the blended spend - the
whole dataset's cost after routing, including the traffic that never left the
baseline model - divided by that candidate's own judged-success rate; a note
under the table names this basis so the figure is never mistaken for the
candidate's isolated cost.

One draw does not measure variance. Each sampled prompt is answered once, and
Frugon sends no temperature setting, so your provider's default applies. A single
answer cannot tell you how much a model's output varies from run to run, and a
one-draw verdict can mislead on a model that is nondeterministic. Re-running
--measure on a fresh sample is the manual way to check whether a verdict is
stable.

Per-call analysis cannot see second-order cost. Frugon prices the calls in
your logs. It cannot see the cost a weak answer creates downstream: a follow-up
call it triggers, a retry, or a human stepping in to review it. Retry attribution
and human-review time are not in the logs, so a model that looks cheaper per call
is not automatically cheaper once those effects are counted. Sampling real outputs
with --judge is the closest built-in check on whether a cheaper model actually
holds quality on your prompts.

On the judge, and on local models. When the judge is one of the models it is
scoring, its verdict is partly a self-assessment; Frugon detects this and prints a
caution naming the affected models, and you can pass --judge-model to use an
independent judge. A model served on your own machine works here too: it can be
sampled as a candidate or named as the judge, keyless and local. A local model
absent from the pricing table has its measurement cost flagged as unpriced rather
than guessed.

Your number is whatever your own logs and your own evaluation say.

Is this you?

  • Agent builders - your GPT-4o agents are expensive; most easy hops don't need them.
  • AI dev teams - monthly LLM bill is real; routing pays for itself in days.
  • RAG & support - retrieval + rerank is cheap; the final answer call doesn't have to be Opus.
  • Data-ETL pipelines - batch extraction is 100% repeatable; mini models handle it fine.
  • Indie hackers - every dollar saved is a dollar of runway.

Keep the savings

This is a one-time snapshot. Want it to keep routing automatically and hold the savings? → frugon.rodiun.io

Star the repo if this saved you money.

FAQ

Common questions

Discussion

Questions & comments · 0

Sign In Sign in to leave a comment.