Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ jobs:
run: go test ./internal/cli -run '^TestReleaseBinary' -count=1 -v
- name: Pinned SQL export through the packaged importer
if: matrix.os == 'linux' && matrix.arch == 'amd64'
timeout-minutes: 10
timeout-minutes: 15
env:
FLEETDIFF_BIN: ${{ github.workspace }}/unpacked/fleetdiff
FLEETDIFF_LINUX_BIN: ${{ github.workspace }}/unpacked/fleetdiff
Expand Down
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,28 @@ Notable user-visible changes are recorded here, newest first.

## [Unreleased]

## [0.14.0] - Unreleased

Release preparation; no tag or publication yet. See [draft release notes](docs/releases/v0.14.0.md).

- Add explicit agent attribution from LiteLLM `agent_id` or prefixed request
tags on LiteLLM and Langfuse, with exclusive known/unknown/ambiguous totals,
repeated/empty-tag coverage and an exact 1,024-agent bound.
- Add opt-in request-tag export through the shared psql/native projection,
and an agent table with exact recorded USD, coverage, period shares and
`--rank-by recorded-spend-change`. Default exports and reports are unchanged.
- **Breaking change:** routing `--show-labels` now also requires
`--acknowledge-sensitive-labels`. Replace `--routing-audit --show-labels` with
`--routing-audit --show-labels --acknowledge-sensitive-labels` on existing
`fleetdiff investigate` commands. Raw agent labels require the same two flags.
User/session/end-user/key rankings remain aliased.
See the [agent attribution contract](docs/AGENT_ATTRIBUTION.md).
- Include escaped selected agent labels in the answer sentence, distinguish
identical Langfuse labels with project aliases, and keep raw project names private.
- Keep agent text compact: omit tag-only coverage for agent IDs, show cost
coverage columns only for incomplete displayed costs, and name unknown and
ambiguous identity explicitly. Agent JSON and default reports are unchanged.

## [0.13.1] - 2026-10-27

See the [release notes](docs/releases/v0.13.1.md).
Expand Down
18 changes: 11 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,17 @@ fleetdiff reads LiteLLM databases and exports or Langfuse exports to show what c
<a id="1-install-v0120"></a>
<a id="1-install-v0121"></a>
<a id="1-install-v0130"></a>
<a id="1-install-v0131"></a>

### 1. Install v0.13.1
### 1. Install v0.14.0

These v0.14.0 instructions require its published release assets. Until
publication, use [v0.13.1](https://github.com/llm-measurement/fleetdiff/releases/tag/v0.13.1).

Linux or macOS; needs curl, gh, shasum and tar. The installer checks the release before unpacking it.

```sh
bash -o pipefail -c 'curl --fail --location --proto "=https" --tlsv1.2 https://raw.githubusercontent.com/llm-measurement/fleetdiff/0d7db95c5bb3013f8c564c91fbb07b6235a2664e/scripts/install.sh | sh -s -- v0.13.1 ./fleetdiff-install'
bash -o pipefail -c 'curl --fail --location --proto "=https" --tlsv1.2 https://raw.githubusercontent.com/llm-measurement/fleetdiff/0d7db95c5bb3013f8c564c91fbb07b6235a2664e/scripts/install.sh | sh -s -- v0.14.0 ./fleetdiff-install'
./fleetdiff-install/fleetdiff --version
```

Expand Down Expand Up @@ -85,7 +89,7 @@ Install fleetdiff using [step 1](#1-install-v0120), then run the synthetic examp

```sh
curl --fail --location --proto '=https' --tlsv1.2 \
https://raw.githubusercontent.com/llm-measurement/fleetdiff/v0.13.1/examples/litellm-spend/synthetic.csv \
https://raw.githubusercontent.com/llm-measurement/fleetdiff/v0.14.0/examples/litellm-spend/synthetic.csv \
--output synthetic.csv
./fleetdiff-install/fleetdiff investigate --litellm-spend synthetic.csv \
--before-period 2026-10-07 --after-period 2026-10-08
Expand Down Expand Up @@ -118,16 +122,16 @@ The summary-file path also shows session concentration:

## Install

The verified v0.13.1 installer selects the platform binary for your machine.
The installer verifies the v0.14.0 platform binary for your machine after publication.
For public release downloads, `gh` need not be signed in.
For manual downloads, use the [v0.13.1 archives](https://github.com/llm-measurement/fleetdiff/releases/tag/v0.13.1)
For manual downloads, use the [v0.14.0 archives](https://github.com/llm-measurement/fleetdiff/releases/tag/v0.14.0)
and [verification instructions](docs/OPERATIONS.md#install-and-verify).
An administrator can verify and transfer artifacts through an approved internal mirror.

With a security-patched Go 1.26 toolchain, install the same version from its module:

```sh
go install github.com/llm-measurement/fleetdiff/cmd/fleetdiff@v0.13.1
go install github.com/llm-measurement/fleetdiff/cmd/fleetdiff@v0.14.0
```

The executable goes to `GOBIN`, or `$(go env GOPATH)/bin` when unset.
Expand Down Expand Up @@ -174,7 +178,7 @@ Guides: [LiteLLM spend](https://llm-measurement.github.io/litellm-spend.html) |
- <a id="questions-it-answers"></a><a id="user-and-session-contributors"></a> [Questions and contributor rankings](docs/WORKFLOWS.md#questions-it-answers).
- <a id="how-it-works"></a><a id="use-your-own-data"></a><a id="reading-the-results"></a> [How summary comparison works](docs/WORKFLOWS.md#how-it-works), [comparison contract](docs/COMPARISON.md) and [investigation contract](docs/INVESTIGATION.md).
- <a id="built-to-be-checked"></a> [Operations](docs/OPERATIONS.md), [resource measurements](docs/BENCHMARKS.md) and [checks](docs/WORKFLOWS.md#built-to-be-checked).
- <a id="docs"></a> [FAQ](docs/FAQ.md), [security](SECURITY.md), [release notes](docs/releases/v0.13.1.md) and [release procedure](docs/RELEASING.md).
- <a id="docs"></a> [FAQ](docs/FAQ.md), [security](SECURITY.md), [release notes](docs/releases/v0.14.0.md) and [release procedure](docs/RELEASING.md).

Questions or feedback: [open an issue](https://github.com/llm-measurement/fleetdiff/issues).
See [Contributing](CONTRIBUTING.md) for checks and signed, signed-off commits.
Expand Down
1 change: 1 addition & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## Supported Versions

Security fixes target the latest patch in the `0.13.x` release line and `main`.
Once v0.14.0 is published, fixes target the `0.14.x` release line and `main`.
Use the newest patch and record its version and revision when running trials.
Older patches and development snapshots do not receive routine backports.
Dependencies and the Go toolchain are reviewed on an ongoing basis.
Expand Down
93 changes: 93 additions & 0 deletions docs/AGENT_ATTRIBUTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Agent Spend Attribution

Find which recorded agents drove a token or spend change between two periods
of LiteLLM spend logs or Langfuse exports.

The included synthetic fixture goes from **300 to 620 tokens** and **USD 3 to
6.20**. Alpha adds **200 of the 320-token net increase (62.5%)**. After the
change, known agents account for 500 tokens/USD5, unknown identity for
50 tokens/USD 0.50, and ambiguous identity for 70 tokens/USD 0.70.

## Run The Fixture

These options are prepared for v0.14.0, not available in v0.13.1. Build this
checkout while release publication is pending:

```sh
go build -o bin/fleetdiff ./cmd/fleetdiff
bin/fleetdiff investigate --litellm-spend examples/litellm-spend/agent-tags.csv \
--before-period 2026-10-07 --after-period 2026-10-08 \
--group-by agent --agent-source request-tag --agent-tag-prefix 'agent:' \
--rank-by recorded-spend-change
```

The answer includes:

```text
agent-1 accounts for 62.5% of the net recorded increase (+200 of +320 tokens).
```

The table shows before/after tokens, recorded USD, USD change and each period's
spend share. Unknown and ambiguous changes appear separately below the ranking.
The four cost-coverage columns appear only when a displayed row has missing,
invalid or zero-origin-unknown cost; otherwise one line confirms usable recorded
USD in both periods. Known agents outside `--top` remain in a separate total.
Agents whose USD change is unavailable are always listed, even with `--top`.

## Choose The Source

- LiteLLM recorded IDs: `--group-by agent --agent-source agent_id`.
- LiteLLM or Langfuse tags: `--group-by agent --agent-source request-tag
--agent-tag-prefix 'agent:'`.

The default ranking is signed recorded-token change. Add
`--rank-by recorded-spend-change` for signed recorded-USD change, descending;
first appearance breaks ties.

For a LiteLLM database, replace the file option with `--litellm-db`. Tag
attribution enables the required tag projection automatically. Standalone
native export needs `--request-tags`; psql needs `-v request_tags=true`.
Exporting tags retains all tag values, not just the selected prefix. Review
the [export limits](NATIVE_EXPORT.md#limits-and-failures) and
[optional column grant](NATIVE_EXPORT.md#administrator-setup) before sharing an
export. These transport options do not disclose unrelated tags in reports.

## Include Selected Labels

Add both `--show-labels --acknowledge-sensitive-labels`; either alone fails.
The selected label is escaped in the answer and table, for example:

```text
agent-1 "alpha" accounts for 62.5% of the net recorded increase (+200 of +320 tokens).
```

Langfuse identities stay project-scoped. Identical labels are distinguished as
`agent-1 "alpha" (project-1)` and `agent-2 "alpha" (project-2)`, never with raw
project names. Project aliases describe this report's encounter order, not a
cross-report project ID. Unrelated tags and user/session/end-user/key identities
remain hidden; the text warns before sharing disclosed labels.

## Limits

- The selector is explicit: no field fallback, agent discovery, friendly-name
lookup or request/observation join. Langfuse counts GENERATION observations.
- Prefixes are exact and case-sensitive. Zero distinct nonblank suffixes means
unknown, one means known, two or more means ambiguous. Repeated identical tags
are deduplicated; repetitions and empty suffixes have separate coverage.
Each row contributes once, and all identity buckets retain their accounting.
- LiteLLM arrays with any non-string element remain unknown with
`non_string_tag_rows` coverage; no string label is salvaged. Langfuse retains
string-only arrays. Missing source columns remain column-absent unknown.
- Malformed JSON, non-array tags, invalid UTF-8 and source fields over 8 KiB fail
without dumping rows. More than 1,024 project-scoped known agents fails and
asks for a narrower prefix or export; identities are not silently dropped.
- Shares use usable recorded USD from all analyzed rows, including unknown,
ambiguous and undisplayed agents. Missing/invalid costs stay unquantified;
zero-cost origin stays unknown. A period with no usable cost is unavailable,
not zero. Costs retain source decimals; JSON shares use 12 decimal places and
text percentages use two. Recorded USD is source accounting, not an invoice.
- The optional JSON section remains `fleetdiff-agent-attribution/v1`. Its
fields, base accounting fingerprint, default export projection and reports
without agent mode are unchanged. Project display aliases are text-only.
Unsupported LiteLLM call types and partial Langfuse exports retain their
existing boundaries.
6 changes: 3 additions & 3 deletions docs/LANGFUSE.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ It reads local files only, not Langfuse services or provider APIs.

## Try the synthetic example

Install using the [README](../README.md#1-install-v0131), then get the example
files (the installer does not include a source checkout):
After v0.14.0 is published, install using the [README](../README.md#1-install-v0140),
then get the example files (the installer does not include a source checkout):

```sh
git clone --depth 1 --branch v0.13.1 https://github.com/llm-measurement/fleetdiff
git clone --depth 1 --branch v0.14.0 https://github.com/llm-measurement/fleetdiff
```

From the directory containing `fleetdiff-install` and this checkout, compare
Expand Down
20 changes: 18 additions & 2 deletions docs/NATIVE_EXPORT.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,14 @@ GRANT SELECT (
ALTER ROLE fleetdiff_reader SET default_transaction_read_only = on;
```

Only for opt-in request-tag exports, also grant the new column below.
This exports every request tag on a row, not only agent tags; keep the file private.

```sql
GRANT SELECT (request_tags)
ON TABLE public."LiteLLM_SpendLogs" TO fleetdiff_reader;
```

Only when daily counters are needed, also grant:

```sql
Expand Down Expand Up @@ -201,14 +209,22 @@ fleetdiff issues no database writes or schema migrations.

The v0.12.0 shared SQL orders spend rows by `"startTime", request_id COLLATE
"C"` and daily rows by `date COLLATE "C"`. For the same database snapshot, bounds,
format and detail options, native and v0.12.0 `psql` exports must be
byte-for-byte identical. Sequential exports from a changing live database are
format and detail options, native and matching `psql` exports must be
byte-for-byte identical. Default projections retain the v0.12.0 recipe's bytes;
opt-in request tags require the matching recipe from the same build.
Sequential exports from a changing live database are
not the same snapshot. This ordering is new relative to the v0.11.0 recipe:
it can change bytes, first-seen aliases and tied report order, not recorded
totals or identity hashing. Database sorting can also affect export time.

## Limits And Failures

- `--request-tags` includes every request tag on a row, not only agent tags.
This option targets the next minor release; use the matching source checkout
until it is released.
It is opt-in; default exports omit the column. The manual recipe uses
`-v request_tags=true` for the same JSON-array-text projection.

| Native export bound | Value |
| --- | --- |
| Spend rows | 10,000,000 |
Expand Down
19 changes: 10 additions & 9 deletions docs/OPERATIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ private local exports before analysis.

The initial supported targets are Linux and macOS on AMD64 and ARM64. Native
Windows binaries are not provided. Download a binary from the
[release page](https://github.com/llm-measurement/fleetdiff/releases/tag/v0.13.1).
Go is not needed to run it. The commands below use `curl` for downloading and
[release page](https://github.com/llm-measurement/fleetdiff/releases/tag/v0.14.0).
These v0.14.0 download commands require its published release assets; use the
v0.13.1 release until publication. Go is not needed to run it. The commands below use `curl` for downloading and
the [GitHub CLI](https://cli.github.com/) for provenance verification.

The commands select Linux or macOS and AMD64 or ARM64 from the current shell's
Expand All @@ -22,7 +23,7 @@ subshell stops on any download or verification failure:
```sh
(
set -eu
version=v0.13.1
version=v0.14.0
case "$(uname -s)" in
Linux) os=linux ;;
Darwin) os=darwin ;;
Expand Down Expand Up @@ -56,7 +57,7 @@ From a reviewed repository checkout, the same download and verification steps
are available as a script (no Go compiler needed):

```sh
sh scripts/install.sh v0.13.1 ./fleetdiff-install
sh scripts/install.sh v0.14.0 ./fleetdiff-install
./fleetdiff-install/fleetdiff --version
```

Expand All @@ -67,14 +68,14 @@ curl --fail --location --proto '=https' --tlsv1.2 \
https://raw.githubusercontent.com/llm-measurement/fleetdiff/0d7db95c5bb3013f8c564c91fbb07b6235a2664e/scripts/install.sh \
--output fleetdiff-install.sh
less fleetdiff-install.sh
sh fleetdiff-install.sh v0.13.1 ./fleetdiff-install
sh fleetdiff-install.sh v0.14.0 ./fleetdiff-install
./fleetdiff-install/fleetdiff --version
```

The immutable script pin is a reviewed installer; the explicit `v0.13.1`
The immutable script pin is a reviewed installer; the explicit `v0.14.0`
argument selects the release independently of that script's older default.

The v0.13.1 binary includes [native LiteLLM export](NATIVE_EXPORT.md),
The v0.14.0 binary includes [native LiteLLM export](NATIVE_EXPORT.md),
[model-mix what-if repricing](WHAT_IF.md), the
[LiteLLM spend importer](../examples/litellm-spend/README.md), optional
[daily row coverage](ROW_COVERAGE.md), [Langfuse local exports](LANGFUSE.md)
Expand Down Expand Up @@ -124,7 +125,7 @@ Install a published version with a currently patched Go 1.26 or later toolchain:
go install github.com/llm-measurement/fleetdiff/cmd/fleetdiff@latest
```

Use `@v0.13.1` instead of `@latest` to pin that release. The binary is installed
Use `@v0.14.0` instead of `@latest` to pin that release. The binary is installed
under `GOBIN`, or `$(go env GOPATH)/bin` when unset; put that directory on `PATH`.
This path uses Go module verification rather than the archive attestation above,
and does not include unreleased checkout features.
Expand All @@ -145,7 +146,7 @@ Check `--version` on the binary you actually run:
| Build | Version | Revision |
|---|---|---|
| Unstamped checkout build above | Go's embedded module/VCS version, or `dev` when unavailable | `unknown` |
| Tagged `go install ...@v0.13.1` | `v0.13.1`, from Go's module metadata | `unknown` |
| Tagged `go install ...@v0.14.0` | `v0.14.0`, from Go's module metadata | `unknown` |
| Verified release archive | Stamped release version | Stamped source revision |

A checkout may report a pseudo-version and `+dirty`, depending on the Go toolchain
Expand Down
13 changes: 10 additions & 3 deletions docs/RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ verify that every advertised version is downloadable. Do not replace immutable t
or artifacts to correct a failed publication; fix forward with a new version if
any release content must change.

## After Publishing v0.13.1
## After Publishing v0.14.0

From an anonymous clone in a fresh directory outside any synced workspace:

Expand All @@ -106,7 +106,7 @@ From an anonymous clone in a fresh directory outside any synced workspace:
[OPERATIONS](OPERATIONS.md#install-and-verify). Confirm the signed tag and
recorded build revision match the approved commit.
2. Run the README's verified installer into a new directory. Confirm the native
executable reports `v0.13.1`, then run the packaged-binary tests above.
executable reports `v0.14.0`, then run the packaged-binary tests above.
3. Set `FLEETDIFF_BIN` to that freshly installed executable and run the exact
[pinned database test](../examples/litellm-spend/TESTING.md#pinned-database-integration).
It exercises the real pinned writer, a SELECT-only SQL export and the released
Expand Down Expand Up @@ -141,7 +141,7 @@ From an anonymous clone in a fresh directory outside any synced workspace:
subset, and absent agents read as unknown. Keep the v0.8.0 import-refactor
output goldens and legacy identity-hash tests unchanged; reports without
these conditions must remain byte-identical.
11. Run the routing guide with default aliases and with `--show-labels`. Verify
11. Run the routing guide with default aliases and with `--show-labels --acknowledge-sensitive-labels`. Verify
public model names, explicit configuration-label warnings, missing serving
identity, classifier overhead and raw-identity suppression. Exercise a
pinned-writer `openai/<public-id>` export: price it only when the row provider
Expand All @@ -154,6 +154,13 @@ From an anonymous clone in a fresh directory outside any synced workspace:
Exercise the README's database investigation, private cleanup and retained
exports. Record the exact executable and distinguish native-driver tests
from the older writer/psql tests; skipped database checks do not pass this gate.
13. Run the agent-tag fixture from AGENT_ATTRIBUTION with the installed binary.
Check the 620-token/USD6.20 totals, alpha's +200 of +320-token answer,
unknown/ambiguous buckets, unavailable-cost visibility and two-flag escaped
label disclosure. Check project aliases on a two-project Langfuse export.
Retain the 18 default text/JSON byte comparisons against v0.13.1 separately
from these opt-in agent checks. Publish site pins and guide text only after
these released-artifact gates pass; do not treat source tests as that proof.

The release workflow tests every native platform before publication. The local
outsider run proves the post-publication download and database path on its own
Expand Down
Loading
Loading