Metergraph

SDK, server and hosted engine

01

The install

See what every LLM call costs. Prove a cheaper one holds quality. Then ship it.

Apache-2.0 · Python and TypeScript · zero runtime dependencies · never in your request path

Free workspace, verified email, no sales call. Or run the whole thing yourself: pip install metergraph then docker compose up.

You write

client = metergraph.wrap(OpenAI())

You get, per route

yourapp.billing:summarize_invoice  route=ticket-classifier  4.2M calls/mo  in=1,284 out=190  1.71s

What this route costs you today $418k/yr

Only then does Metergraph open the pull request

97±2% quality parity held, over 4,000 replayed production inputs −$38k/yr cost removed, once it cleared the bar

Illustrative route. Your numbers depend on your traffic, and nothing ships until the interval clears your own parity margin.

Wrap the OpenAI or Anthropic client you already constructed and every call lands as a row with the function that made it, the route it belongs to, its tokens, its latency and its cost. Then the hosted engine replays that traffic against cheaper models on your own keys and scores what comes back, so the question stops being which model is cheapest and starts being which cheap model your users cannot tell apart.

This is about the calls your application makes in production, not the coding assistants your team runs locally. If you want the version of this page written for whoever signs the invoice, read the report.

  1. Capture is asynchronous and out of your request path. Metergraph is a library, not a gateway, so nothing we do can add latency or fail your call.
  2. Cost is priced server side from an effective-dated catalog, so history reprices correctly when a provider changes what a model costs.
  3. Point the same SDK at the hosted engine and it shadow-tests your traffic against cheaper models on your own keys, scores every quality characteristic with a confidence interval, and opens a pull request only once that interval clears your parity margin.

02

The capture

One wrap, both languages, no framework to adopt.

The SDK wraps the provider client you already constructed. Your call sites do not change, your types do not change, and removing it is deleting one line.

Python

# the client you already have
from openai import OpenAI
import metergraph

client = metergraph.wrap(OpenAI())

# the stack walk names the function; route() groups it
@metergraph.track
def classify(ticket):
    with metergraph.route("ticket-classifier"):
        return client.chat.completions.create(...)

TypeScript · npm install metergraph

// the client you already have
import OpenAI from "openai";
import * as mg from "metergraph";

const client = mg.wrap(new OpenAI());

// name first, so a bundler cannot rename it
export const classify = mg.track(
  "billing.classify",
  () => mg.route("ticket-classifier",
    () => client.chat.completions.create(...)));

Figure 1 · What one captured call looks like

yourapp.billing:summarize_invoice  route=ticket-classifier  in=1,284 out=190  1.71s  $0.0083

Self-hosting is docker compose up: a FastAPI server, Postgres, and a React dashboard on localhost:8787. You do not need an account to run it, and nothing on the tracking layer is held back.

03

Your content

What the SDK sends, and where it stops.

The self-hosted server and the hosted service answer this differently. Both answers are below, because averaging them would be useless to anyone doing a security review.

Figure 2 · The path content takes, per edition

Always sent · both editions

model & provider token counts latency function & route
Request and response text is separate, and the two editions treat it differently from here.

Self-hosted server · Apache-2.0

Ingest Column allowlist Postgres, with no content column

Hosted service

Scrub credentials and headers Store, capped at 100 KiB per side
The self-hosted guarantee is structural. The allowlist means there is no column for content to land in, so it holds whether or not you trust us. On the hosted service capture is on by default and you turn it off with METERGRAPH_CAPTURE_TEXT=0 or a per-route opt-out.

04

The loop

Change one environment variable and tracking becomes a standing loop.

Same SDK, same routes, same call sites. The hosted engine picks up where the dashboard stops.

  1. Step 01

    Capture

    The SDK or native OpenTelemetry GenAI ingestion logs every call and fingerprints recurring call sites into routes.

  2. Step 02

    Watch

    Per-route anomaly detection from week one: spend spikes, retry loops on billable errors, cost-per-unit drift. Alerts arrive with a diagnosis rather than a red line on a chart.

  3. Step 03

    Shadow

    Sampled production inputs get replayed against challenger models and configurations on your own accounts.1 Never in your request path.

  4. Step 04

    Score

    Per-characteristic non-inferiority testing with bootstrap confidence intervals, plus cost and latency deltas. Everything is denominated in your unit economics: cost per answer, per resolution, per report.

  5. Step 05

    Act

    Scorecard to pull request to session-sticky canary to promotion, with rollback on a config flag. Eval sets get committed to your repo as code.

Scorecard · ticket-classifier · challenger vs production Non-inferior · −$71,400/yr
accuracy +0.3
faithfulness +0.1
format validity +0.0
tool-call correctness +0.4
tone −0.6
Illustrative scorecard. tone at −0.6 does not block the recommendation because it still clears the −2% margin. Sampling stops at interval convergence, not at a round number of runs.
  • Replay runs inside your allow-lists, residency rules and unit-cost ceilings. A challenger you have not allow-listed never gets called at all.

05

What it finds

Model swaps are the headline. The rest of the list is where the hours are.

You already know most of this is worth doing. Each row is its own project, which is the whole reason it stays in the backlog.

Schedule A · Levers, typical yield, and the work each one actually is

Lever Typical reduction What the work is
Model substitution 40–70% Build an eval set, keep it honest as the domain shifts, then argue the quality case with data you do not have yet.
Model-up budget-neutral Send the hard slice to a stronger model and pay for it out of what the easy slice gives back.
Batch API migration ~30% Split the latency-tolerant routes out, handle the async result path, backfill the failures. Two weeks that never wins a sprint.
Prompt distillation 50–80% Perfect the prompt on a frontier model, then compile an example-rich version a cheap one can run.
Triage routing 30–60% Cheap model first, static validation, escalate on failure. Safe only once you can measure the share it handles at parity.
Parameter tuning 10–30% Reasoning budgets, retry policy, fallback chains. You cannot see any of it until cost is attributed per feature.
Cache strategy 10–25% Cache-control tuning and KV-cache price differences between providers. None of it reaches a dashboard you own.
Feature cost drivers varies A provider flips web search to full-token pricing and it becomes your largest per-answer cost. You find out on the invoice.

Per-route ranges applied to the slice of traffic that qualifies, not to your total bill.

Findings arrive looking like this

  • support-triage 4.2M calls/mo · $0.019/answer · parity 97±2% −$38k/yr
  • contract-extract batch-eligible · deferred 4 months −30%, PR ready
  • report-writer web search flipped to full-token pricing now 41% of cost per answer
  • rag-answers retry loop billing on failed calls $1.1k/hr, caught in 4 min

06

Editions

Free to track. Hosted to act.

Same SDK and the same routes either way. What changes is whether Metergraph shows you the cost or goes and does something about it.

Schedule B · What each edition includes

Capability Open source Hosted
Apache-2.0, self-hosted, no account · the hosted engine, on your keys
Capture SDKs for Python and TypeScriptyesyes
Cost, token and latency tracking by function and routeyesyes
Server and dashboard you run yourselfyesmanaged
Effective-dated community price catalogyesyes
Content stripped at ingest by column allowlistyesopt-out
Anomaly detection with a diagnosis attachedyes
Continuous shadow evals of live trafficyes
Judge-qualified scorecards with confidence intervalsyes
Evidence-backed pull requests, canary, rollbackyes
Routes, traces and scorecards queryable over MCPyes

07

Questions

What engineers ask before they install it.

Can this break my product?

It is a library, not a gateway, and capture is asynchronous. Nothing is proxied, so an outage on our side cannot fail your call or add latency to it. Enforcement for canary and promotion rides a dynamic-config value the SDK polls, with the same semantics as any feature flag you already run.

What does it do to my call sites?

Nothing. You wrap the client object once at construction. Your call sites, your types and your error handling are untouched, and uninstalling is deleting the wrap.

How does attribution survive a bundler?

In Python the SDK walks the stack, so you get module:function for free, and @metergraph.track pins an explicit one. In TypeScript minification destroys function names, so you wrap with track("stable.name", fn) and put the name first. Either way route() groups call sites into the product surface you actually reason about.

Do you see my prompts and completions?

On the self-hosted server, no, and the reason is structural: ingest runs a column allowlist, so there is no content column for it to land in. The hosted service captures scrubbed request and response text by default, with credentials and transport headers removed and a 100 KiB cap per side. Turn it off with METERGRAPH_CAPTURE_TEXT=0 or a per-route opt-out.

Whose provider keys does shadow replay spend?

Yours, on your own accounts, inside your allow-lists, residency rules and unit-cost ceilings. The tokens spent on judging and scoring are ours.

I already run OpenTelemetry.

Then point it at us. The server accepts OpenTelemetry spans on /v1/traces without the SDK, though you give up automatic function attribution and have to set route names yourself.

How do I get the data back out?

It is your Postgres if you self-host. Eval sets are committed to your repo as code, and routes, traces and scorecards are queryable over MCP. Switching between self-hosted and hosted is METERGRAPH_INGEST_URL, in either direction.

What is actually open source?

The capture SDKs on PyPI and npm, plus the server and dashboard, all Apache-2.0. No account, no token ceiling, no features held back on the tracking layer.

08

Start

Create a workspace, copy the key, send one call.

A verified email gets you a free workspace automatically, owned by you. Mint an ingest key from the Setup screen and it is shown once. There is no approval step and no sales conversation in the way.

The whole setup

pip install metergraph

# from Setup in the dashboard. Leave it unset and the
# SDK is a no-op, so it is safe in every environment.
export METERGRAPH_APP_TOKEN=mg_...

# then, once, where you build the client
client = metergraph.wrap(OpenAI())

Read-only SDK · cannot break production · per-operation content opt-out

Or start without an account · open source on GitHub ↗ · read the docs