cliproxy-costs

command module
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 6, 2026 License: MIT Imports: 7 Imported by: 0

README

cliproxy-costs

A native plugin for CLIProxyAPI (CPA) that prices every proxied request, keeps a durable usage ledger, tracks subscription quota, returns cost to clients and serves a dashboard.

  • Pricing from the same catalog omp uses (USD per 1M tokens), with aliases, manual overrides and context tiers. Snapshot persisted locally; pricing keeps working when the feed is down.
  • Ledger in SQLite: one row per execution attempt (tokens, cost, model, credential, client fingerprint, latency, TTFT), daily rollups, retention.
  • Quota: latest Codex and Claude subscription windows per credential, observed passively from upstream response headers.
  • Cost on responses: usage.cost / usage.cost_details in Chat Completions, Responses and Messages bodies (stream and non-stream) plus X-CliProxy-Pricing / X-CliProxy-Cost-USD headers.
  • API for tools such as omp-cliproxy-usage, under CPA's management API (management key).
  • Dashboard: today / week / month spend with month projection, cache savings, subscription value and failure rate at a glance; quota limits with burn rate and projected exhaustion; spend over time; per-credential, per-client and per-model breakdowns with latency percentiles; most expensive requests; failures by status; feed health; request drill-down. Self-contained, works on phones, dark/light.
  • Display currency: amounts are stored and computed in USD; the dashboard can show them in EUR, CNY or any other ECB reference currency, converted at the latest ECB rate (labelled with its date). Configurable fixed rates fill gaps or replace the ECB feed.

Non-goals: billing or blocking requests; pricing image/audio/search tariffs (those rows are unsupported or unknown, never 0); active quota polling.

Compatibility

  • CLIProxyAPI v8 with native plugin ABI 1 / JSON schema 6 (developed against commit e5b5a1c). Native integration tests also target unmodified v8.0.15 (a4acc9f752bd46571f737a10c04bf413656ab06b). CPA Home mode is not supported.
  • Plugins run in-process; upgrading the plugin requires a CPA restart.
  • Linux amd64 and arm64 (glibc ≥ 2.34; release builds are made on Debian bookworm, the CPA container base), macOS amd64 and arm64 (12 or later), and Windows amd64.

Install

  1. Download cliproxy-costs_<version>_<os>_<arch>.zip from the releases page, verify it against checksums.txt, and unzip it.

  2. Put the library at <plugins dir>/<os>/<arch>/cliproxy-costs.<ext> (.so on Linux, .dylib on macOS, .dll on Windows; the base name is the plugin id and must stay cliproxy-costs). The release zips use the CLIProxyAPI plugin-store layout, so CPA's own installer can also install them from a release.

  3. Add the plugin block to CPA's config.yaml, see examples/config.yaml:

    plugins:
      enabled: true
      dir: plugins
      configs:
        cliproxy-costs:
          enabled: true
          db-path: ./data/cliproxy-costs/ledger.db
    
  4. Set the client-fingerprint secret in CPA's environment (optional but recommended; without it clients are not told apart):

    export CLIPROXY_COSTS_HMAC_SECRET="$(openssl rand -hex 32)"
    
  5. Restart CPA. The log shows plugin registered plugin_id=cliproxy-costs.

All options: docs/config.md.

Dashboard

http(s)://<cpa-host>:<port>/v0/resource/plugins/cliproxy-costs/dashboard

The page itself contains no data. It asks for CPA's management key (the same one CPA's management panel uses; subject to CPA's management.allow-remote, remote-management.allow-remote in pre-v8 configs), keeps it in this page's memory only, and fetches aggregates from the management API. The currency selector in the header switches every amount to the chosen currency (hover a value for USD); the footer shows the ECB reference date and any stale or error state. Only that choice is remembered, in sessionStorage for the tab.

API

GET /v0/management/cliproxy-costs/v1/{summary,quota,requests,rates,fx}, authenticated by CPA with the management key.

fx returns display-only exchange rates (units per 1 USD); every other endpoint stays USD.

Look up the cost of a response with its X-Cpa-Trace-Id header:

curl -s -H "Authorization: Bearer $CPA_MANAGEMENT_KEY" \
  "http://localhost:8317/v0/management/cliproxy-costs/v1/requests?trace_id=<X-Cpa-Trace-Id value>"

Full reference with examples: docs/api.md.

Security model

  • The plugin is trusted native code inside the CPA process.
  • CPA does not authenticate /v0/resource/... routes, so the plugin serves only static dashboard assets there. Every data endpoint is a management route, which CPA authenticates with the management key before the plugin sees it.
  • Raw client API keys, upstream keys (Source), failure bodies and response headers are never stored or logged. Clients are identified by HMAC-SHA256(secret, caller_scope), the same value CPA derives for its interceptors. Only allowlisted quota headers are parsed.
  • Responses include CPA's raw auth id (which may contain file names or e-mail addresses) next to the opaque auth index.

Interpreting cost and savings

Amounts are token-price estimates, not provider invoices or proof of a subscription charge. The catalog and manual overrides determine the rates; check the rate source, model alias, context tier and missing-price indicators before comparing totals. A configured subscription price is an operator input, not a verified payment. Dropped records and unpriced requests make totals incomplete.

With a front proxy such as client → Headroom → CLIProxyAPI, this plugin sees usage after compression. Its cache-savings estimate compares cache tariffs with uncached input tariffs; it does not measure Headroom compression savings. That comparison needs original and compressed token counts, matching rates and trustworthy request correlation from the front proxy. Do not treat client-supplied savings headers as accounting evidence. Check both streaming and non-streaming paths: another proxy can filter the injected response headers even when it preserves usage events.

For ledger/dashboard-only operation without adding fields to client responses:

inject:
  body: false
  headers: false

Keep the management endpoint private. Its key grants CPA administration, not read-only access to one client's ledger. Protect the database and backups: raw rows include account, session and request identifiers; daily rollups outlive raw-row retention. Existing directories must already have suitably restrictive permissions. External pricing and exchange-rate feeds receive fetch requests, not prompts, but remain trusted inputs to the estimates.

Limitations

  • Native RPC envelopes are capped at 128 MiB before copying across the ABI.
  • Token tariffs only; the feed has no service-tier (flex/priority/batch) prices.
  • Cost injected into bodies is computed from the response's own usage. Many clients ignore usage.cost; use the API for the recorded per-attempt estimates.
  • Streaming responses carry X-CliProxy-Pricing but no cost header (headers are sent before usage is known); the cost is in the final usage event.
  • Quota is passive: a credential appears after it serves a request whose upstream sends quota headers.
  • The write queue is bounded; overflow is counted, never blocking. CPA delivers usage asynchronously and in memory, so records in flight during a crash are lost. A normal shutdown drains the queue.

Building from source

Requires Go (any recent version with GOTOOLCHAIN=auto; go.mod pins go 1.26.0 and toolchain go1.26.8) and a C compiler for cgo.

make check                    # vet, race tests, build for the host arch
make integration CPA_BINARY=/path/to/cli-proxy-api # loopback synthetic fixtures
make build GOARCH=arm64 CC=aarch64-linux-gnu-gcc

Output: dist/<os>/<arch>/cliproxy-costs.{so,dylib,dll}. For glibc compatibility with the CPA image, build Linux inside golang:1.26-bookworm as CI does; CI cross-builds Windows there too (make build GOOS=windows CC=x86_64-w64-mingw32-gcc) and builds macOS on a macOS runner.

Layout

Path Contents
main.go cgo ABI shell: the four exports and the host callback bridge
internal/abi wire structs copied from CPA, envelopes
internal/config YAML parsing, defaults, validation
internal/pricing feed parsing, resolution, normalization, cost (pure)
internal/usagebody usage extraction and cost insertion per format, SSE framing (pure)
internal/intercept response interceptors and stream state
internal/ledger usage.handle → ledger row, fingerprints
internal/quota quota header parsing
internal/catalog feed fetch, decode, snapshot
internal/fx ECB reference rates fetch, parse, snapshot, USD-based rates
internal/store SQLite schema, writer, queries, retention
internal/api routes, endpoints, static assets
internal/plugin lifecycle and dispatch
web/ dashboard (vanilla JS + vendored uPlot, MIT)

License

MIT © Alex Markessinis. Includes code from CLIProxyAPI and uPlot, both MIT; see THIRD_PARTY_NOTICES.md.

Documentation

Overview

Command cliproxy-costs is a CLIProxyAPI native plugin (build with -buildmode=c-shared). It exports the ABI 1 entry point and dispatches every RPC into internal/plugin.

Directories

Path Synopsis
internal
abi
Package abi holds the wire structs and envelopes of the CLIProxyAPI native plugin ABI (ABI 1, JSON schema 6).
Package abi holds the wire structs and envelopes of the CLIProxyAPI native plugin ABI (ABI 1, JSON schema 6).
api
Package api serves the plugin's management and resource routes: the data endpoints under CPA's management API (CPA enforces the management key) and the static dashboard assets under /v0/resource/plugins/, which carry no data.
Package api serves the plugin's management and resource routes: the data endpoints under CPA's management API (CPA enforces the management key) and the static dashboard assets under /v0/resource/plugins/, which carry no data.
catalog
Package catalog fetches the pricing feed through the host, decodes and validates it, persists the last good snapshot and publishes parsed catalogs.
Package catalog fetches the pricing feed through the host, decodes and validates it, persists the last good snapshot and publishes parsed catalogs.
config
Package config parses and validates the plugin's block of the CPA config.
Package config parses and validates the plugin's block of the CPA config.
fx
Package fx fetches the ECB euro reference rates through the host, persists the last good snapshot and converts it to USD-based display rates.
Package fx fetches the ECB euro reference rates through the host, persists the last good snapshot and converts it to USD-based display rates.
intercept
Package intercept implements response.intercept_after and response.intercept_stream_chunk: price the body's own usage and annotate the response.
Package intercept implements response.intercept_after and response.intercept_stream_chunk: price the body's own usage and annotate the response.
ledger
Package ledger converts usage.handle records into ledger rows: it derives client fingerprints, strips secrets, normalizes tokens, prices, and extracts quota observations.
Package ledger converts usage.handle records into ledger rows: it derives client fingerprints, strips secrets, normalizes tokens, prices, and extracts quota observations.
plugin
Package plugin owns the lifecycle: register/reconfigure/quiesce/shutdown, the shared state snapshot, background workers and RPC dispatch.
Package plugin owns the lifecycle: register/reconfigure/quiesce/shutdown, the shared state snapshot, background workers and RPC dispatch.
pricing
Package pricing is the pure pricing core: rate cards, catalog lookup, model resolution, token normalization and cost computation.
Package pricing is the pure pricing core: rate cards, catalog lookup, model resolution, token normalization and cost computation.
quota
Package quota turns allowlisted upstream response headers into per- credential subscription quota snapshots.
Package quota turns allowlisted upstream response headers into per- credential subscription quota snapshots.
store
Package store owns the SQLite ledger: one writer goroutine with batched transactions fed by a bounded queue, a read-only pool for API queries, and retention.
Package store owns the SQLite ledger: one writer goroutine with batched transactions fed by a bounded queue, a read-only pool for API queries, and retention.
usagebody
Package usagebody extracts token usage from downstream response bodies and inserts cost annotations, per downstream format.
Package usagebody extracts token usage from downstream response bodies and inserts cost annotations, per downstream format.
Package web embeds the dashboard assets.
Package web embeds the dashboard assets.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL