tokenaudit 0.2.0__tar.gz → 0.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tokenaudit-0.5.0/CHANGELOG.md +126 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/DECISIONS.md +23 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/PKG-INFO +18 -11
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/README.md +17 -10
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/captured/runner.py +29 -13
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/breakpoint_limit.label.json +6 -1
- tokenaudit-0.5.0/corpus/synthetic/fixtures/cache_writes_reused.label.json +38 -0
- tokenaudit-0.5.0/corpus/synthetic/fixtures/cache_writes_reused.trace.json +512 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/context_bloat.label.json +6 -1
- tokenaudit-0.5.0/corpus/synthetic/fixtures/cross_session_distinct.label.json +37 -0
- tokenaudit-0.5.0/corpus/synthetic/fixtures/cross_session_distinct.trace.json +749 -0
- tokenaudit-0.5.0/corpus/synthetic/fixtures/cross_session_drift.label.json +40 -0
- tokenaudit-0.5.0/corpus/synthetic/fixtures/cross_session_drift.trace.json +986 -0
- tokenaudit-0.5.0/corpus/synthetic/fixtures/cross_session_stable.label.json +37 -0
- tokenaudit-0.5.0/corpus/synthetic/fixtures/cross_session_stable.trace.json +986 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/healthy_cached.label.json +6 -1
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/healthy_incremental.label.json +6 -1
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/healthy_small.label.json +6 -1
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/lookback_overflow.label.json +6 -1
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/manifest.json +128 -0
- tokenaudit-0.5.0/corpus/synthetic/fixtures/misordered_shared_context.label.json +40 -0
- tokenaudit-0.5.0/corpus/synthetic/fixtures/misordered_shared_context.trace.json +239 -0
- tokenaudit-0.5.0/corpus/synthetic/fixtures/misordered_shared_context_provider_log.label.json +40 -0
- tokenaudit-0.5.0/corpus/synthetic/fixtures/misordered_shared_context_provider_log.trace.json +104 -0
- tokenaudit-0.5.0/corpus/synthetic/fixtures/missing_cache_markers.label.json +40 -0
- tokenaudit-0.5.0/corpus/synthetic/fixtures/missing_cache_markers.trace.json +373 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/model_switch.label.json +6 -1
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/openai_healthy_cached.label.json +6 -1
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/prefix_drift.label.json +6 -1
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/prefix_drift_legacy_flat.label.json +6 -1
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/prefix_drift_no_content.label.json +6 -1
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/prefix_drift_openinference.label.json +6 -1
- tokenaudit-0.5.0/corpus/synthetic/fixtures/prefix_drift_provider_log.label.json +40 -0
- tokenaudit-0.5.0/corpus/synthetic/fixtures/prefix_drift_provider_log.trace.json +284 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/retry_storm.label.json +6 -1
- tokenaudit-0.5.0/corpus/synthetic/fixtures/shared_context_first.label.json +38 -0
- tokenaudit-0.5.0/corpus/synthetic/fixtures/shared_context_first.trace.json +275 -0
- tokenaudit-0.5.0/corpus/synthetic/fixtures/shared_context_small.label.json +38 -0
- tokenaudit-0.5.0/corpus/synthetic/fixtures/shared_context_small.trace.json +239 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/sub_minimum.label.json +6 -1
- tokenaudit-0.5.0/corpus/synthetic/fixtures/tool_schema_bloat.label.json +40 -0
- tokenaudit-0.5.0/corpus/synthetic/fixtures/tool_schema_bloat.trace.json +1160 -0
- tokenaudit-0.5.0/corpus/synthetic/fixtures/tool_schema_bloat_provider_log.label.json +40 -0
- tokenaudit-0.5.0/corpus/synthetic/fixtures/tool_schema_bloat_provider_log.trace.json +1412 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/tool_schema_churn.label.json +6 -1
- tokenaudit-0.5.0/corpus/synthetic/fixtures/tool_schema_lean.label.json +39 -0
- tokenaudit-0.5.0/corpus/synthetic/fixtures/tool_schema_lean.trace.json +836 -0
- tokenaudit-0.5.0/corpus/synthetic/fixtures/tool_schema_rotating.label.json +39 -0
- tokenaudit-0.5.0/corpus/synthetic/fixtures/tool_schema_rotating.trace.json +1342 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/ttl_expiry.label.json +6 -1
- tokenaudit-0.5.0/corpus/synthetic/fixtures/unmarked_far_apart.label.json +38 -0
- tokenaudit-0.5.0/corpus/synthetic/fixtures/unmarked_far_apart.trace.json +373 -0
- tokenaudit-0.5.0/corpus/synthetic/fixtures/unread_cache_writes.label.json +39 -0
- tokenaudit-0.5.0/corpus/synthetic/fixtures/unread_cache_writes.trace.json +433 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/generate.py +548 -14
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/demo/README.md +7 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/demo/app.py +5 -6
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/demo/static/index.html +1 -1
- tokenaudit-0.5.0/docs/case-studies/tradingagents-claude.md +134 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/docs/case-studies/tradingagents.md +26 -1
- tokenaudit-0.5.0/eval/results.json +1934 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/eval/results.md +9 -4
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/eval/run_eval.py +14 -11
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/pyproject.toml +1 -1
- tokenaudit-0.5.0/render.yaml +10 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/__init__.py +1 -1
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/__main__.py +32 -5
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/ci/baseline.py +13 -2
- tokenaudit-0.5.0/src/tokenaudit/counterfactual/__init__.py +94 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/detect/__init__.py +9 -1
- tokenaudit-0.5.0/src/tokenaudit/detect/base.py +192 -0
- tokenaudit-0.5.0/src/tokenaudit/detect/corpus_record.json +86 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/detect/engine.py +52 -1
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/detect/rules/breakpoint_limit.py +12 -15
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/detect/rules/context_bloat.py +14 -14
- tokenaudit-0.5.0/src/tokenaudit/detect/rules/cross_session_drift.py +226 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/detect/rules/lookback_overflow.py +15 -15
- tokenaudit-0.5.0/src/tokenaudit/detect/rules/misordered_shared_context.py +180 -0
- tokenaudit-0.5.0/src/tokenaudit/detect/rules/missing_cache_markers.py +141 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/detect/rules/model_switch.py +11 -12
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/detect/rules/prefix_drift.py +15 -18
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/detect/rules/retry_storm.py +13 -14
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/detect/rules/sub_minimum.py +16 -18
- tokenaudit-0.5.0/src/tokenaudit/detect/rules/tool_schema_bloat.py +122 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/detect/rules/tool_schema_churn.py +13 -18
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/detect/rules/ttl_expiry.py +16 -19
- tokenaudit-0.5.0/src/tokenaudit/detect/rules/unread_cache_writes.py +152 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/ingest/__init__.py +20 -12
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/ingest/dialects.py +100 -5
- tokenaudit-0.5.0/src/tokenaudit/ingest/provider_logs.py +267 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/model/records.py +27 -3
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/report/html.py +20 -2
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/tests/test_corpus.py +16 -2
- tokenaudit-0.5.0/tests/test_counterfactual.py +75 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/tests/test_demo.py +9 -0
- tokenaudit-0.5.0/tests/test_detect.py +445 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/tests/test_eval.py +1 -1
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/tests/test_ingest.py +78 -1
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/tests/test_report.py +1 -1
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/tokenaudit-baseline.json +5 -2
- tokenaudit-0.2.0/CHANGELOG.md +0 -58
- tokenaudit-0.2.0/eval/results.json +0 -668
- tokenaudit-0.2.0/src/tokenaudit/counterfactual/__init__.py +0 -1
- tokenaudit-0.2.0/src/tokenaudit/detect/base.py +0 -96
- tokenaudit-0.2.0/src/tokenaudit/detect/corpus_record.json +0 -56
- tokenaudit-0.2.0/tests/test_detect.py +0 -201
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/.dockerignore +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/.github/workflows/ci.yml +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/.github/workflows/release.yml +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/.gitignore +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/.pre-commit-config.yaml +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/LICENSE +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/Makefile +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/action.yml +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/README.md +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/captured/README.md +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/captured/capture_tradingagents.sh +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/captured/redact.py +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/captured/runs/tradingagents/run-01-NVDA-2026-08-14/decision.txt +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/captured/runs/tradingagents/run-01-NVDA-2026-08-14/manifest.json +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/captured/runs/tradingagents/run-01-NVDA-2026-08-14/trace.jsonl +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/captured/runs/tradingagents/run-02-AAPL-2026-08-14/decision.txt +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/captured/runs/tradingagents/run-02-AAPL-2026-08-14/manifest.json +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/captured/runs/tradingagents/run-02-AAPL-2026-08-14/trace.jsonl +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/captured/runs/tradingagents/run-03-NVDA-2026-08-21/decision.txt +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/captured/runs/tradingagents/run-03-NVDA-2026-08-21/manifest.json +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/captured/runs/tradingagents/run-03-NVDA-2026-08-21/trace.jsonl +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/breakpoint_limit.trace.json +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/context_bloat.trace.json +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/healthy_cached.trace.json +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/healthy_incremental.trace.json +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/healthy_small.trace.json +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/lookback_overflow.trace.json +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/model_switch.trace.json +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/openai_healthy_cached.trace.json +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/prefix_drift.trace.json +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/prefix_drift_legacy_flat.trace.json +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/prefix_drift_no_content.trace.json +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/prefix_drift_openinference.trace.json +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/retry_storm.trace.json +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/sub_minimum.trace.json +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/tool_schema_churn.trace.json +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/ttl_expiry.trace.json +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/demo/Dockerfile +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/demo/__init__.py +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/demo/requirements.txt +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/docs/ci.md +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/docs/rulebook.md +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/ci/__init__.py +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/ci/check.py +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/detect/findings.py +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/detect/rules/__init__.py +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/examples/sample.trace.json +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/ingest/containers.py +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/ingest/dedup.py +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/ingest/quality.py +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/ingest/sessionize.py +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/model/__init__.py +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/py.typed +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/report/__init__.py +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/report/corpus_record.py +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/report/health.py +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/rulebook/__init__.py +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/rulebook/anthropic.json +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/rulebook/google.json +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/rulebook/openai.json +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/tests/test_ci.py +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/tests/test_package.py +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/tests/test_redact.py +0 -0
- {tokenaudit-0.2.0 → tokenaudit-0.5.0}/tests/test_rulebook.py +0 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.5.0 — 2026-11-12
|
|
4
|
+
|
|
5
|
+
From the [TradingAgents on Claude case study](docs/case-studies/tradingagents-claude.md):
|
|
6
|
+
|
|
7
|
+
- New detector `unread_cache_writes`: requests that write to the cache when
|
|
8
|
+
nothing later re-sends what they wrote, so the write premium buys nothing.
|
|
9
|
+
HIGH when the session's writes cost more than its reads saved. Defers to
|
|
10
|
+
prefix_drift, ttl_expiry, tool_schema_churn, breakpoint_limit and
|
|
11
|
+
lookback_overflow, which explain a missed read better.
|
|
12
|
+
- `missing_cache_markers` counts tool definitions in the shared prefix (they
|
|
13
|
+
come first in an Anthropic prompt, and different tools share nothing) and
|
|
14
|
+
sizes each repeat from the earlier request's reported tokens instead of
|
|
15
|
+
characters. On the Claude runs its estimate went from 2k-5k to 8k-22k tokens
|
|
16
|
+
per run, in line with what caching actually read back.
|
|
17
|
+
- `import tokenaudit.counterfactual` on its own no longer fails with a
|
|
18
|
+
circular import.
|
|
19
|
+
|
|
20
|
+
## 0.4.0 — 2026-11-12
|
|
21
|
+
|
|
22
|
+
Upgrading adds findings to traces with these patterns, and changes the
|
|
23
|
+
impact of some existing ones: re-baseline `tokenaudit check` after upgrading.
|
|
24
|
+
|
|
25
|
+
New detectors, each with a positive corpus fixture and near-miss negatives:
|
|
26
|
+
|
|
27
|
+
- `misordered_shared_context`: the same reports or documents sent to several
|
|
28
|
+
agents, each behind its own role text, so the prefix cache never reuses
|
|
29
|
+
them.
|
|
30
|
+
- `cross_session_drift`: a date, user or ticker near the start of a long
|
|
31
|
+
prompt, which keeps it from being reused across sessions.
|
|
32
|
+
- `tool_schema_bloat`: tool definitions sent on every request and never
|
|
33
|
+
called by any request carrying the same tool set.
|
|
34
|
+
- `missing_cache_markers`: a provider that only caches on request
|
|
35
|
+
(Anthropic), a session with no markers and no cache activity, and requests
|
|
36
|
+
re-sending a prefix an earlier one sent within the cache lifetime.
|
|
37
|
+
|
|
38
|
+
The first two find the TradingAgents problems the case study diagnosed by
|
|
39
|
+
hand. The captured runs, previously reported as clean, now show both
|
|
40
|
+
findings and a 25% health floor (was 100%).
|
|
41
|
+
|
|
42
|
+
Pricing:
|
|
43
|
+
|
|
44
|
+
- Every finding is priced with its model's read and write rates from the
|
|
45
|
+
rulebook. A request that wrote the cache is charged the write premium
|
|
46
|
+
(1.25x on Anthropic's 5-minute cache) instead of full price, and the
|
|
47
|
+
assumption line names the rates used.
|
|
48
|
+
- `diagnose` and the HTML report show a dollar range when the rulebook has a
|
|
49
|
+
verified input price for the model.
|
|
50
|
+
|
|
51
|
+
Ingest:
|
|
52
|
+
|
|
53
|
+
- Raw provider logs: JSONL or a JSON array of logged request/response pairs
|
|
54
|
+
from the OpenAI Responses, OpenAI Chat Completions and Anthropic Messages
|
|
55
|
+
APIs. Anthropic's exclusive token counts are converted; records without
|
|
56
|
+
timestamps are flagged and time-based detectors abstain.
|
|
57
|
+
- Tool definitions (name and size) and the tools each response called, from
|
|
58
|
+
OpenInference, `gen_ai.tool.definitions` in current OTel GenAI, and raw
|
|
59
|
+
logs.
|
|
60
|
+
- Per-line digests kept in memory for the new detectors; `ingest` output
|
|
61
|
+
leaves them out.
|
|
62
|
+
|
|
63
|
+
Also:
|
|
64
|
+
|
|
65
|
+
- `diagnose` and `check` report a cross-session section when given more
|
|
66
|
+
than one session.
|
|
67
|
+
- Demo: a `render.yaml` blueprint for one-click deploys, and raw provider
|
|
68
|
+
logs are accepted as uploads.
|
|
69
|
+
- The capture runner records Anthropic runs (`--provider anthropic`);
|
|
70
|
+
`experiments/tradingagents-claude/` is ready to run with an API key.
|
|
71
|
+
- The corpus has 30 fixtures (was 16).
|
|
72
|
+
|
|
73
|
+
## 0.2.0 — 2026-11-10
|
|
74
|
+
|
|
75
|
+
- Fix the GitHub Action for external users. It installed the latest PyPI
|
|
76
|
+
release, and 0.1.0 has no `check` command. It now installs the code at the
|
|
77
|
+
ref it is used from, so `@v0.2.0` runs 0.2.0. The `version` input still
|
|
78
|
+
pins a PyPI release.
|
|
79
|
+
- Rulebook re-verified against current provider docs (version 2026.11.10):
|
|
80
|
+
- Anthropic: Opus, Sonnet and Haiku 5.5, Fable and Mythos 5.1/5.2.
|
|
81
|
+
Per-model read multipliers (0.05x and 0.025x) and base input prices.
|
|
82
|
+
- OpenAI: GPT-5.6 sizes, GPT-6 and GPT-6.1, 12h TTL. Pre-5.6 models no
|
|
83
|
+
longer publish a fixed minimum, so it is recorded as unknown.
|
|
84
|
+
- Google: added Gemini 4 Argon and 3.8 Flash; removed 3.7 and 3.5 Flash,
|
|
85
|
+
which are no longer in Google's table.
|
|
86
|
+
- `sub_minimum` abstains when a model has no published minimum.
|
|
87
|
+
- Release workflow: pushing a `vX.Y.Z` tag publishes to PyPI.
|
|
88
|
+
- The sdist no longer includes the experiment traces.
|
|
89
|
+
- **`ingest_text(text, source)`**: ingest trace content already in memory
|
|
90
|
+
(an upload, a stream) — nothing touches disk. `ingest_path` now wraps it.
|
|
91
|
+
- **Hosted demo** (`demo/`, not part of the package): a one-page FastAPI
|
|
92
|
+
app — drop a trace, get the report; two prerendered samples (the
|
|
93
|
+
synthetic prefix-drift fixture and the real captured TradingAgents
|
|
94
|
+
runs). Uploads are parsed in memory and never stored or logged.
|
|
95
|
+
- **CI cost-regression gate**: `tokenaudit check <traces> --baseline
|
|
96
|
+
baseline.json` fails a build (exit 1) on a new HIGH finding or a drop in
|
|
97
|
+
cache read rate / health floor beyond `--max-regression` points; create
|
|
98
|
+
or refresh the baseline with `--update`. Ships as a composite GitHub
|
|
99
|
+
Action (`action.yml`) with a markdown summary written to the job page
|
|
100
|
+
(and to a file via `--markdown`, for PR comments). See `docs/ci.md`.
|
|
101
|
+
|
|
102
|
+
## 0.1.0 — 2026-08-30
|
|
103
|
+
|
|
104
|
+
First usable release.
|
|
105
|
+
|
|
106
|
+
- **Ingest** four real trace dialects into one normalized accounting model:
|
|
107
|
+
OTel GenAI current semconv, legacy flat names (older openllmetry),
|
|
108
|
+
OpenInference, and the OTel Python SDK's `to_json()` JSONL — with
|
|
109
|
+
layered-span deduplication (framework + SDK instrumentors double-report
|
|
110
|
+
the same call), a data-quality gate that refuses to price malformed
|
|
111
|
+
telemetry, and prompt privacy by construction (block hashes and lengths,
|
|
112
|
+
never text).
|
|
113
|
+
- **Nine deterministic detectors** — prefix drift, sub-minimum cache
|
|
114
|
+
blocks, model switch, TTL expiry, breakpoint limit, lookback overflow,
|
|
115
|
+
tool-schema churn, context bloat, retry storm — each with evidence
|
|
116
|
+
pointers, bounded impact ranges with stated assumptions, and abstention
|
|
117
|
+
(with a reason) as a first-class outcome distinct from "checked, clean".
|
|
118
|
+
- **Provider caching rulebook** packaged as sourced, dated JSON (Anthropic,
|
|
119
|
+
OpenAI, Google), with a CI staleness alarm and a rendered comparison
|
|
120
|
+
table (`tokenaudit rulebook`).
|
|
121
|
+
- **Evaluation** against a byte-reproducible labeled corpus, published in
|
|
122
|
+
`eval/results.md`; per-detector corpus records ship inside the package
|
|
123
|
+
and appear on findings.
|
|
124
|
+
- **CLI**: `tokenaudit diagnose` (text, `--json`, self-contained `--html`
|
|
125
|
+
report, `--sample`), `tokenaudit ingest`, `tokenaudit rulebook`.
|
|
126
|
+
- Zero runtime dependencies. Python 3.11+.
|
|
@@ -39,3 +39,26 @@ Three calls made together for the 0.1.0 release, all in service of the same prin
|
|
|
39
39
|
**The cache health score is `realized / achievable` savings** — realized cache savings over realized-plus-what-the-findings-quantify. Both halves are computed the same way, so there are no weights to defend; overlapping findings make the denominator conservative, so the score is presented as a floor and says so.
|
|
40
40
|
|
|
41
41
|
**Findings carry a corpus record, not a confidence number.** A precision float computed from a 16-fixture corpus would be theater. The package ships the raw record instead — "3/4 planted cases found, 0 false alarms in N negatives" — generated by the eval harness and drift-tested, so what appears next to a finding is exactly what was measured.
|
|
42
|
+
|
|
43
|
+
## 007 — Line-level digests, and detectors that look across sessions (2026-11-12)
|
|
44
|
+
|
|
45
|
+
**Decision:** ingest keeps a truncated SHA-256 and length for every line of every message, in memory only, next to the existing whole-message digest. Two detectors use them: `misordered_shared_context` within a session and `cross_session_drift` across sessions.
|
|
46
|
+
|
|
47
|
+
**Why:** the TradingAgents diagnosis was done by hand, because whole-message hashes can't see the same reports inside two different messages. The tool called those runs clean while the case study showed a 4x improvement. Lines are fine enough to find shared content and to pin a date to one line, and coarse enough to stay fast.
|
|
48
|
+
|
|
49
|
+
**Boundaries:** line digests never leave the process (`tokenaudit ingest` drops them), because a hash of a short line is easy to guess. Cross-session findings count toward a CI gate's findings but not toward the health floor, since whether sessions share a cache depends on when they ran; the low impact bound only counts sessions that started within the model's cache lifetime of each other. Each new detector defers to an existing one where they overlap: a short edit near the start of a prompt is prefix drift, and a value that changes on every call within a session is prefix drift too.
|
|
50
|
+
|
|
51
|
+
## 008 — Price against the fix, per model; new detectors yield to older ones (2026-11-12)
|
|
52
|
+
|
|
53
|
+
**Decision:** a finding's impact is what the affected tokens cost minus what they would have cost under the fix, using the model's read and write multipliers from the rulebook. A request that wrote the cache is charged the write premium, not full price. Dollars appear only when the rulebook has a verified input price; tiered prices (Haiku 5.5) stay in tokens.
|
|
54
|
+
|
|
55
|
+
**Why:** a flat discount overstated waste on models with cheaper reads and ignored that a miss on Anthropic often costs more than a plain request, because it writes the cache. Keeping the rates in one module means a rulebook update reprices every detector at once.
|
|
56
|
+
|
|
57
|
+
**Boundaries:** `tool_schema_bloat` prints tool names, which are identifiers a developer chose rather than prompt text. It counts a tool as used if any request with the same tool set called it, so a shared registry where every tool is used by some agent is not flagged; telling agents apart needs more than a trace carries. `missing_cache_markers` only counts block-aligned prefixes repeated within the cache lifetime, since a breakpoint sits at a block's end and a later repeat would have missed anyway. It leaves growing conversations to `context_bloat` and byte-identical re-sends to `retry_storm`, whose fixes fit those cases better.
|
|
58
|
+
|
|
59
|
+
## 009 — Report cache writes that buy nothing; size prefixes from real token counts (2026-11-12)
|
|
60
|
+
|
|
61
|
+
**Decision:** `unread_cache_writes` reports writes that no later request in the session sends again, priced at the write premium, and calls a session HIGH when that premium exceeds what its reads saved. It stays quiet when another detector already explains why the cache wasn't read. Prefix sizes in `missing_cache_markers` come from the earlier request's reported input tokens, scaled by its share of the request's characters.
|
|
62
|
+
|
|
63
|
+
**Why:** the Claude case study turned caching on for every TradingAgents request and paid 7.2% more, and 0.4.0 called those runs clean. Caching is only cheaper when something reads the write back, and "turn on caching" is the fix people reach for first, so the tool has to say when it backfires. Character counts were off by 2x on prompts made of numbers and JSON, which made the missing-markers estimate a fraction of what caching delivered.
|
|
64
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: tokenaudit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Root-cause diagnosis of wasted LLM tokens, from the traces your agents already emit
|
|
5
5
|
Project-URL: Repository, https://github.com/yashmyeole/tokenaudit
|
|
6
6
|
Project-URL: Issues, https://github.com/yashmyeole/tokenaudit/issues
|
|
@@ -29,7 +29,7 @@ Description-Content-Type: text/markdown
|
|
|
29
29
|
|
|
30
30
|
**Root-cause diagnosis of wasted LLM tokens — from the traces your agents already emit.**
|
|
31
31
|
|
|
32
|
-
> Status: alpha (0.
|
|
32
|
+
> Status: alpha (0.5.0). Interfaces may change before 1.0.
|
|
33
33
|
|
|
34
34
|
Every LLM cost tool can tell you *how much* you spent and *who* spent it. Almost none can tell you *why a specific request wasted money*. The biggest lever on an LLM bill — prompt caching, up to a 10× discount on repeated input — fails silently: change one byte of a cached prefix, drop below a model's minimum cacheable size, let a timer expire, and the provider just bills you full price. No error, no warning. There are [documented](https://barndoor.ai/what-happens-to-your-llm-bill-when-prompt-caching-fails/) [cases](https://github.com/TauricResearch/TradingAgents/issues/750) of agents running at ~0% cache hit rate for months, at 30–40% unnecessary cost, because of a single line of prompt construction.
|
|
35
35
|
|
|
@@ -37,21 +37,26 @@ tokenaudit reads the traces agents already produce and, for each expensive reque
|
|
|
37
37
|
|
|
38
38
|
It works: diagnosing one of those documented cases produced a 9-file patch that took TradingAgents' cache hit rate from **7.5% to 32.4% (4.3×)** in a controlled, pre-registered before/after experiment — the [first case study](docs/case-studies/tradingagents.md).
|
|
39
39
|
|
|
40
|
+
On Claude it found the opposite problem: switching on caching for every request made the same agent's input bill 7.2% higher, because most of its prompts are sent once and every cache write costs 1.25×. The [second case study](docs/case-studies/tradingagents-claude.md) shows where the money went and what to cache instead.
|
|
41
|
+
|
|
40
42
|
## Quickstart
|
|
41
43
|
|
|
42
44
|
pip install tokenaudit
|
|
43
45
|
tokenaudit diagnose --sample # diagnose the bundled demo trace
|
|
44
46
|
tokenaudit diagnose mytraces/ --html report.html
|
|
45
47
|
|
|
46
|
-
Zero runtime dependencies; reads OTLP/JSON files
|
|
48
|
+
Zero runtime dependencies; reads OTLP/JSON files, OTel SDK JSONL, and raw
|
|
49
|
+
OpenAI or Anthropic request/response logs. Try it
|
|
47
50
|
on this repo's real captured runs: `tokenaudit diagnose corpus/captured/runs`.
|
|
48
|
-
Prefer a browser? The [demo
|
|
49
|
-
are parsed in memory and never stored.
|
|
51
|
+
Prefer a browser? The [live demo](https://tokenaudit-demo.onrender.com)
|
|
52
|
+
diagnoses a dropped trace; uploads are parsed in memory and never stored. It
|
|
53
|
+
runs on a free instance that sleeps when idle, so the first load can take a
|
|
54
|
+
minute.
|
|
50
55
|
|
|
51
56
|
The plan, in one line each:
|
|
52
57
|
|
|
53
58
|
- Ingest standard traces — OpenTelemetry GenAI in all three of its cache-field naming generations, OpenInference, raw provider SDK logs — into one normalized accounting model.
|
|
54
|
-
- Run a library of deterministic detectors for the silent failure modes: prefix drift, sub-minimum cache blocks, model switches, TTL expiry, breakpoint and lookback limits, tool-schema churn and bloat, context bloat, retry storms.
|
|
59
|
+
- Run a library of deterministic detectors for the silent failure modes: prefix drift, sub-minimum cache blocks, model switches, TTL expiry, breakpoint and lookback limits, tool-schema churn and bloat, context bloat, retry storms, shared content sent behind different openings, per-session values that stop a prompt being reused across sessions, prompts that never ask a cache-on-request provider to cache them, and cache writes nothing reads back.
|
|
55
60
|
- Price each finding against a counterfactual — what the bill would have been under the fix — as a bounded range, never a point guess.
|
|
56
61
|
- Publish per-detector precision and recall against a labeled corpus, so you know how much to trust each finding.
|
|
57
62
|
|
|
@@ -118,11 +123,13 @@ If you know a tool that already does this, please open an issue — I would genu
|
|
|
118
123
|
- [x] `tokenaudit diagnose` CLI, HTML report, cache health score
|
|
119
124
|
- [x] CI cost-regression mode: fail a build that tanks the cache hit rate ([docs/ci.md](docs/ci.md))
|
|
120
125
|
- [x] Demo app: drag a trace in, get the report ([demo/](demo/))
|
|
121
|
-
- [x] Case studies on public agents, with reproduction commands ([TradingAgents: 4.3× cache hit rate](docs/case-studies/tradingagents.md))
|
|
122
|
-
- [
|
|
123
|
-
- [
|
|
124
|
-
- [
|
|
125
|
-
- [
|
|
126
|
+
- [x] Case studies on public agents, with reproduction commands ([TradingAgents: 4.3× cache hit rate](docs/case-studies/tradingagents.md), [TradingAgents on Claude: when caching raised the bill](docs/case-studies/tradingagents-claude.md))
|
|
127
|
+
- [x] Detectors for misordered shared context and cross-session drift: they now find the TradingAgents problems on their own ([case study, section 6](docs/case-studies/tradingagents.md#6-what-tokenaudit-reports-now))
|
|
128
|
+
- [x] Counterfactual pricing with per-model read and write rates and dollar ranges
|
|
129
|
+
- [x] Raw provider SDK log ingest (OpenAI Responses, Chat Completions, Anthropic Messages), tool-schema bloat detector
|
|
130
|
+
- [x] A detector for prompts that never ask Anthropic to cache them
|
|
131
|
+
- [x] Public demo deployment: [tokenaudit-demo.onrender.com](https://tokenaudit-demo.onrender.com)
|
|
132
|
+
- [x] Case study on an Anthropic-model agent ([TradingAgents on Claude](docs/case-studies/tradingagents-claude.md))
|
|
126
133
|
|
|
127
134
|
## Development
|
|
128
135
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Root-cause diagnosis of wasted LLM tokens — from the traces your agents already emit.**
|
|
4
4
|
|
|
5
|
-
> Status: alpha (0.
|
|
5
|
+
> Status: alpha (0.5.0). Interfaces may change before 1.0.
|
|
6
6
|
|
|
7
7
|
Every LLM cost tool can tell you *how much* you spent and *who* spent it. Almost none can tell you *why a specific request wasted money*. The biggest lever on an LLM bill — prompt caching, up to a 10× discount on repeated input — fails silently: change one byte of a cached prefix, drop below a model's minimum cacheable size, let a timer expire, and the provider just bills you full price. No error, no warning. There are [documented](https://barndoor.ai/what-happens-to-your-llm-bill-when-prompt-caching-fails/) [cases](https://github.com/TauricResearch/TradingAgents/issues/750) of agents running at ~0% cache hit rate for months, at 30–40% unnecessary cost, because of a single line of prompt construction.
|
|
8
8
|
|
|
@@ -10,21 +10,26 @@ tokenaudit reads the traces agents already produce and, for each expensive reque
|
|
|
10
10
|
|
|
11
11
|
It works: diagnosing one of those documented cases produced a 9-file patch that took TradingAgents' cache hit rate from **7.5% to 32.4% (4.3×)** in a controlled, pre-registered before/after experiment — the [first case study](docs/case-studies/tradingagents.md).
|
|
12
12
|
|
|
13
|
+
On Claude it found the opposite problem: switching on caching for every request made the same agent's input bill 7.2% higher, because most of its prompts are sent once and every cache write costs 1.25×. The [second case study](docs/case-studies/tradingagents-claude.md) shows where the money went and what to cache instead.
|
|
14
|
+
|
|
13
15
|
## Quickstart
|
|
14
16
|
|
|
15
17
|
pip install tokenaudit
|
|
16
18
|
tokenaudit diagnose --sample # diagnose the bundled demo trace
|
|
17
19
|
tokenaudit diagnose mytraces/ --html report.html
|
|
18
20
|
|
|
19
|
-
Zero runtime dependencies; reads OTLP/JSON files
|
|
21
|
+
Zero runtime dependencies; reads OTLP/JSON files, OTel SDK JSONL, and raw
|
|
22
|
+
OpenAI or Anthropic request/response logs. Try it
|
|
20
23
|
on this repo's real captured runs: `tokenaudit diagnose corpus/captured/runs`.
|
|
21
|
-
Prefer a browser? The [demo
|
|
22
|
-
are parsed in memory and never stored.
|
|
24
|
+
Prefer a browser? The [live demo](https://tokenaudit-demo.onrender.com)
|
|
25
|
+
diagnoses a dropped trace; uploads are parsed in memory and never stored. It
|
|
26
|
+
runs on a free instance that sleeps when idle, so the first load can take a
|
|
27
|
+
minute.
|
|
23
28
|
|
|
24
29
|
The plan, in one line each:
|
|
25
30
|
|
|
26
31
|
- Ingest standard traces — OpenTelemetry GenAI in all three of its cache-field naming generations, OpenInference, raw provider SDK logs — into one normalized accounting model.
|
|
27
|
-
- Run a library of deterministic detectors for the silent failure modes: prefix drift, sub-minimum cache blocks, model switches, TTL expiry, breakpoint and lookback limits, tool-schema churn and bloat, context bloat, retry storms.
|
|
32
|
+
- Run a library of deterministic detectors for the silent failure modes: prefix drift, sub-minimum cache blocks, model switches, TTL expiry, breakpoint and lookback limits, tool-schema churn and bloat, context bloat, retry storms, shared content sent behind different openings, per-session values that stop a prompt being reused across sessions, prompts that never ask a cache-on-request provider to cache them, and cache writes nothing reads back.
|
|
28
33
|
- Price each finding against a counterfactual — what the bill would have been under the fix — as a bounded range, never a point guess.
|
|
29
34
|
- Publish per-detector precision and recall against a labeled corpus, so you know how much to trust each finding.
|
|
30
35
|
|
|
@@ -91,11 +96,13 @@ If you know a tool that already does this, please open an issue — I would genu
|
|
|
91
96
|
- [x] `tokenaudit diagnose` CLI, HTML report, cache health score
|
|
92
97
|
- [x] CI cost-regression mode: fail a build that tanks the cache hit rate ([docs/ci.md](docs/ci.md))
|
|
93
98
|
- [x] Demo app: drag a trace in, get the report ([demo/](demo/))
|
|
94
|
-
- [x] Case studies on public agents, with reproduction commands ([TradingAgents: 4.3× cache hit rate](docs/case-studies/tradingagents.md))
|
|
95
|
-
- [
|
|
96
|
-
- [
|
|
97
|
-
- [
|
|
98
|
-
- [
|
|
99
|
+
- [x] Case studies on public agents, with reproduction commands ([TradingAgents: 4.3× cache hit rate](docs/case-studies/tradingagents.md), [TradingAgents on Claude: when caching raised the bill](docs/case-studies/tradingagents-claude.md))
|
|
100
|
+
- [x] Detectors for misordered shared context and cross-session drift: they now find the TradingAgents problems on their own ([case study, section 6](docs/case-studies/tradingagents.md#6-what-tokenaudit-reports-now))
|
|
101
|
+
- [x] Counterfactual pricing with per-model read and write rates and dollar ranges
|
|
102
|
+
- [x] Raw provider SDK log ingest (OpenAI Responses, Chat Completions, Anthropic Messages), tool-schema bloat detector
|
|
103
|
+
- [x] A detector for prompts that never ask Anthropic to cache them
|
|
104
|
+
- [x] Public demo deployment: [tokenaudit-demo.onrender.com](https://tokenaudit-demo.onrender.com)
|
|
105
|
+
- [x] Case study on an Anthropic-model agent ([TradingAgents on Claude](docs/case-studies/tradingagents-claude.md))
|
|
99
106
|
|
|
100
107
|
## Development
|
|
101
108
|
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"""Capture one real TradingAgents run as OpenTelemetry spans in a JSONL file.
|
|
2
2
|
|
|
3
3
|
Zero-modification capture: TradingAgents' code is untouched. Instrumentation
|
|
4
|
-
wraps the
|
|
5
|
-
posture tokenaudit itself takes. Spans are written
|
|
6
|
-
Python SDK's own ``ReadableSpan.to_json()``
|
|
7
|
-
so the captured dialect is exactly what
|
|
4
|
+
wraps the provider SDK (OpenAI or Anthropic) and LangChain at process level,
|
|
5
|
+
the same zero-integration posture tokenaudit itself takes. Spans are written
|
|
6
|
+
line-by-line using the OTel Python SDK's own ``ReadableSpan.to_json()``
|
|
7
|
+
rendering, with no custom mapping, so the captured dialect is exactly what
|
|
8
|
+
the SDK produces in the wild.
|
|
8
9
|
|
|
9
10
|
This script is executed by ``capture_tradingagents.sh`` inside a throwaway
|
|
10
11
|
virtualenv on the machine that holds the API key. It is not part of the
|
|
@@ -66,16 +67,22 @@ def build_provider(trace_file: Path, run_id: str, ta_commit: str) -> TracerProvi
|
|
|
66
67
|
return provider
|
|
67
68
|
|
|
68
69
|
|
|
69
|
-
def instrument(provider: TracerProvider) -> list[str]:
|
|
70
|
+
def instrument(provider: TracerProvider, llm_provider: str = "openai") -> list[str]:
|
|
70
71
|
"""Attach OpenInference instrumentors; return the ones that activated."""
|
|
71
72
|
active: list[str] = []
|
|
72
73
|
try:
|
|
73
|
-
|
|
74
|
+
if llm_provider == "anthropic":
|
|
75
|
+
from openinference.instrumentation.anthropic import AnthropicInstrumentor
|
|
74
76
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
+
AnthropicInstrumentor().instrument(tracer_provider=provider)
|
|
78
|
+
active.append("openinference-instrumentation-anthropic")
|
|
79
|
+
else:
|
|
80
|
+
from openinference.instrumentation.openai import OpenAIInstrumentor
|
|
81
|
+
|
|
82
|
+
OpenAIInstrumentor().instrument(tracer_provider=provider)
|
|
83
|
+
active.append("openinference-instrumentation-openai")
|
|
77
84
|
except Exception as exc:
|
|
78
|
-
print(f"warning:
|
|
85
|
+
print(f"warning: {llm_provider} SDK instrumentor not active: {exc}", file=sys.stderr)
|
|
79
86
|
try:
|
|
80
87
|
from openinference.instrumentation.langchain import LangChainInstrumentor
|
|
81
88
|
|
|
@@ -88,12 +95,14 @@ def instrument(provider: TracerProvider) -> list[str]:
|
|
|
88
95
|
return active
|
|
89
96
|
|
|
90
97
|
|
|
91
|
-
def run_analysis(
|
|
98
|
+
def run_analysis(
|
|
99
|
+
ticker: str, date: str, deep: str, quick: str, rounds: int, llm_provider: str = "openai"
|
|
100
|
+
) -> str:
|
|
92
101
|
from tradingagents.default_config import DEFAULT_CONFIG
|
|
93
102
|
from tradingagents.graph.trading_graph import TradingAgentsGraph
|
|
94
103
|
|
|
95
104
|
config = dict(DEFAULT_CONFIG)
|
|
96
|
-
config["llm_provider"] =
|
|
105
|
+
config["llm_provider"] = llm_provider
|
|
97
106
|
config["deep_think_llm"] = deep
|
|
98
107
|
config["quick_think_llm"] = quick
|
|
99
108
|
config["max_debate_rounds"] = rounds
|
|
@@ -119,6 +128,7 @@ def main() -> None:
|
|
|
119
128
|
parser.add_argument("--deep", default="gpt-5.4-mini")
|
|
120
129
|
parser.add_argument("--quick", default="gpt-5.4-mini")
|
|
121
130
|
parser.add_argument("--rounds", type=int, default=1)
|
|
131
|
+
parser.add_argument("--provider", choices=["openai", "anthropic"], default="openai")
|
|
122
132
|
parser.add_argument("--ta-commit", default="unknown")
|
|
123
133
|
parser.add_argument("--smoke", action="store_true", help="plumbing test, no API calls")
|
|
124
134
|
args = parser.parse_args()
|
|
@@ -134,6 +144,7 @@ def main() -> None:
|
|
|
134
144
|
"smoke": args.smoke,
|
|
135
145
|
"ticker": args.ticker,
|
|
136
146
|
"trade_date": args.date,
|
|
147
|
+
"llm_provider": args.provider,
|
|
137
148
|
"models": {"deep_think": args.deep, "quick_think": args.quick},
|
|
138
149
|
"max_debate_rounds": args.rounds,
|
|
139
150
|
"tradingagents_commit": args.ta_commit,
|
|
@@ -146,8 +157,11 @@ def main() -> None:
|
|
|
146
157
|
"tradingagents",
|
|
147
158
|
"opentelemetry-sdk",
|
|
148
159
|
"openinference-instrumentation-openai",
|
|
160
|
+
"openinference-instrumentation-anthropic",
|
|
149
161
|
"openinference-instrumentation-langchain",
|
|
150
162
|
"openai",
|
|
163
|
+
"anthropic",
|
|
164
|
+
"langchain-anthropic",
|
|
151
165
|
"langchain-core",
|
|
152
166
|
)
|
|
153
167
|
},
|
|
@@ -164,8 +178,10 @@ def main() -> None:
|
|
|
164
178
|
manifest["instrumentors"] = []
|
|
165
179
|
decision = "smoke"
|
|
166
180
|
else:
|
|
167
|
-
manifest["instrumentors"] = instrument(provider)
|
|
168
|
-
decision = run_analysis(
|
|
181
|
+
manifest["instrumentors"] = instrument(provider, args.provider)
|
|
182
|
+
decision = run_analysis(
|
|
183
|
+
args.ticker, args.date, args.deep, args.quick, args.rounds, args.provider
|
|
184
|
+
)
|
|
169
185
|
(args.out / "decision.txt").write_text(decision + "\n", encoding="utf-8")
|
|
170
186
|
|
|
171
187
|
provider.force_flush()
|
|
@@ -11,13 +11,18 @@
|
|
|
11
11
|
],
|
|
12
12
|
"must_not_contain": [
|
|
13
13
|
"context_bloat",
|
|
14
|
+
"cross_session_drift",
|
|
14
15
|
"lookback_overflow",
|
|
16
|
+
"misordered_shared_context",
|
|
17
|
+
"missing_cache_markers",
|
|
15
18
|
"model_switch",
|
|
16
19
|
"prefix_drift",
|
|
17
20
|
"retry_storm",
|
|
18
21
|
"sub_minimum",
|
|
22
|
+
"tool_schema_bloat",
|
|
19
23
|
"tool_schema_churn",
|
|
20
|
-
"ttl_expiry"
|
|
24
|
+
"ttl_expiry",
|
|
25
|
+
"unread_cache_writes"
|
|
21
26
|
],
|
|
22
27
|
"evidence": {
|
|
23
28
|
"marked_blocks": 6,
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"fixture": "cache_writes_reused",
|
|
3
|
+
"schema": "otel-genai-current",
|
|
4
|
+
"provider_shape": "anthropic",
|
|
5
|
+
"models": [
|
|
6
|
+
"claude-sonnet-4-5"
|
|
7
|
+
],
|
|
8
|
+
"content_captured": true,
|
|
9
|
+
"contains": [],
|
|
10
|
+
"must_not_contain": [
|
|
11
|
+
"breakpoint_limit",
|
|
12
|
+
"context_bloat",
|
|
13
|
+
"cross_session_drift",
|
|
14
|
+
"lookback_overflow",
|
|
15
|
+
"misordered_shared_context",
|
|
16
|
+
"missing_cache_markers",
|
|
17
|
+
"model_switch",
|
|
18
|
+
"prefix_drift",
|
|
19
|
+
"retry_storm",
|
|
20
|
+
"sub_minimum",
|
|
21
|
+
"tool_schema_bloat",
|
|
22
|
+
"tool_schema_churn",
|
|
23
|
+
"ttl_expiry",
|
|
24
|
+
"unread_cache_writes"
|
|
25
|
+
],
|
|
26
|
+
"evidence": {
|
|
27
|
+
"calls": 6,
|
|
28
|
+
"written_never_read": 0
|
|
29
|
+
},
|
|
30
|
+
"notes": "Three agents, each called twice, with only the stable system prompt marked. Near-miss: every write is read back by the agent's next call, so a correct tool reports nothing here.",
|
|
31
|
+
"totals": {
|
|
32
|
+
"calls": 6,
|
|
33
|
+
"input_tokens": 12114,
|
|
34
|
+
"cache_read_tokens": 6021,
|
|
35
|
+
"cache_creation_tokens": 6021,
|
|
36
|
+
"output_tokens": 1200
|
|
37
|
+
}
|
|
38
|
+
}
|