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.
Files changed (170) hide show
  1. tokenaudit-0.5.0/CHANGELOG.md +126 -0
  2. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/DECISIONS.md +23 -0
  3. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/PKG-INFO +18 -11
  4. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/README.md +17 -10
  5. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/captured/runner.py +29 -13
  6. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/breakpoint_limit.label.json +6 -1
  7. tokenaudit-0.5.0/corpus/synthetic/fixtures/cache_writes_reused.label.json +38 -0
  8. tokenaudit-0.5.0/corpus/synthetic/fixtures/cache_writes_reused.trace.json +512 -0
  9. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/context_bloat.label.json +6 -1
  10. tokenaudit-0.5.0/corpus/synthetic/fixtures/cross_session_distinct.label.json +37 -0
  11. tokenaudit-0.5.0/corpus/synthetic/fixtures/cross_session_distinct.trace.json +749 -0
  12. tokenaudit-0.5.0/corpus/synthetic/fixtures/cross_session_drift.label.json +40 -0
  13. tokenaudit-0.5.0/corpus/synthetic/fixtures/cross_session_drift.trace.json +986 -0
  14. tokenaudit-0.5.0/corpus/synthetic/fixtures/cross_session_stable.label.json +37 -0
  15. tokenaudit-0.5.0/corpus/synthetic/fixtures/cross_session_stable.trace.json +986 -0
  16. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/healthy_cached.label.json +6 -1
  17. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/healthy_incremental.label.json +6 -1
  18. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/healthy_small.label.json +6 -1
  19. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/lookback_overflow.label.json +6 -1
  20. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/manifest.json +128 -0
  21. tokenaudit-0.5.0/corpus/synthetic/fixtures/misordered_shared_context.label.json +40 -0
  22. tokenaudit-0.5.0/corpus/synthetic/fixtures/misordered_shared_context.trace.json +239 -0
  23. tokenaudit-0.5.0/corpus/synthetic/fixtures/misordered_shared_context_provider_log.label.json +40 -0
  24. tokenaudit-0.5.0/corpus/synthetic/fixtures/misordered_shared_context_provider_log.trace.json +104 -0
  25. tokenaudit-0.5.0/corpus/synthetic/fixtures/missing_cache_markers.label.json +40 -0
  26. tokenaudit-0.5.0/corpus/synthetic/fixtures/missing_cache_markers.trace.json +373 -0
  27. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/model_switch.label.json +6 -1
  28. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/openai_healthy_cached.label.json +6 -1
  29. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/prefix_drift.label.json +6 -1
  30. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/prefix_drift_legacy_flat.label.json +6 -1
  31. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/prefix_drift_no_content.label.json +6 -1
  32. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/prefix_drift_openinference.label.json +6 -1
  33. tokenaudit-0.5.0/corpus/synthetic/fixtures/prefix_drift_provider_log.label.json +40 -0
  34. tokenaudit-0.5.0/corpus/synthetic/fixtures/prefix_drift_provider_log.trace.json +284 -0
  35. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/retry_storm.label.json +6 -1
  36. tokenaudit-0.5.0/corpus/synthetic/fixtures/shared_context_first.label.json +38 -0
  37. tokenaudit-0.5.0/corpus/synthetic/fixtures/shared_context_first.trace.json +275 -0
  38. tokenaudit-0.5.0/corpus/synthetic/fixtures/shared_context_small.label.json +38 -0
  39. tokenaudit-0.5.0/corpus/synthetic/fixtures/shared_context_small.trace.json +239 -0
  40. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/sub_minimum.label.json +6 -1
  41. tokenaudit-0.5.0/corpus/synthetic/fixtures/tool_schema_bloat.label.json +40 -0
  42. tokenaudit-0.5.0/corpus/synthetic/fixtures/tool_schema_bloat.trace.json +1160 -0
  43. tokenaudit-0.5.0/corpus/synthetic/fixtures/tool_schema_bloat_provider_log.label.json +40 -0
  44. tokenaudit-0.5.0/corpus/synthetic/fixtures/tool_schema_bloat_provider_log.trace.json +1412 -0
  45. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/tool_schema_churn.label.json +6 -1
  46. tokenaudit-0.5.0/corpus/synthetic/fixtures/tool_schema_lean.label.json +39 -0
  47. tokenaudit-0.5.0/corpus/synthetic/fixtures/tool_schema_lean.trace.json +836 -0
  48. tokenaudit-0.5.0/corpus/synthetic/fixtures/tool_schema_rotating.label.json +39 -0
  49. tokenaudit-0.5.0/corpus/synthetic/fixtures/tool_schema_rotating.trace.json +1342 -0
  50. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/ttl_expiry.label.json +6 -1
  51. tokenaudit-0.5.0/corpus/synthetic/fixtures/unmarked_far_apart.label.json +38 -0
  52. tokenaudit-0.5.0/corpus/synthetic/fixtures/unmarked_far_apart.trace.json +373 -0
  53. tokenaudit-0.5.0/corpus/synthetic/fixtures/unread_cache_writes.label.json +39 -0
  54. tokenaudit-0.5.0/corpus/synthetic/fixtures/unread_cache_writes.trace.json +433 -0
  55. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/generate.py +548 -14
  56. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/demo/README.md +7 -0
  57. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/demo/app.py +5 -6
  58. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/demo/static/index.html +1 -1
  59. tokenaudit-0.5.0/docs/case-studies/tradingagents-claude.md +134 -0
  60. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/docs/case-studies/tradingagents.md +26 -1
  61. tokenaudit-0.5.0/eval/results.json +1934 -0
  62. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/eval/results.md +9 -4
  63. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/eval/run_eval.py +14 -11
  64. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/pyproject.toml +1 -1
  65. tokenaudit-0.5.0/render.yaml +10 -0
  66. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/__init__.py +1 -1
  67. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/__main__.py +32 -5
  68. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/ci/baseline.py +13 -2
  69. tokenaudit-0.5.0/src/tokenaudit/counterfactual/__init__.py +94 -0
  70. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/detect/__init__.py +9 -1
  71. tokenaudit-0.5.0/src/tokenaudit/detect/base.py +192 -0
  72. tokenaudit-0.5.0/src/tokenaudit/detect/corpus_record.json +86 -0
  73. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/detect/engine.py +52 -1
  74. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/detect/rules/breakpoint_limit.py +12 -15
  75. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/detect/rules/context_bloat.py +14 -14
  76. tokenaudit-0.5.0/src/tokenaudit/detect/rules/cross_session_drift.py +226 -0
  77. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/detect/rules/lookback_overflow.py +15 -15
  78. tokenaudit-0.5.0/src/tokenaudit/detect/rules/misordered_shared_context.py +180 -0
  79. tokenaudit-0.5.0/src/tokenaudit/detect/rules/missing_cache_markers.py +141 -0
  80. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/detect/rules/model_switch.py +11 -12
  81. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/detect/rules/prefix_drift.py +15 -18
  82. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/detect/rules/retry_storm.py +13 -14
  83. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/detect/rules/sub_minimum.py +16 -18
  84. tokenaudit-0.5.0/src/tokenaudit/detect/rules/tool_schema_bloat.py +122 -0
  85. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/detect/rules/tool_schema_churn.py +13 -18
  86. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/detect/rules/ttl_expiry.py +16 -19
  87. tokenaudit-0.5.0/src/tokenaudit/detect/rules/unread_cache_writes.py +152 -0
  88. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/ingest/__init__.py +20 -12
  89. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/ingest/dialects.py +100 -5
  90. tokenaudit-0.5.0/src/tokenaudit/ingest/provider_logs.py +267 -0
  91. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/model/records.py +27 -3
  92. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/report/html.py +20 -2
  93. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/tests/test_corpus.py +16 -2
  94. tokenaudit-0.5.0/tests/test_counterfactual.py +75 -0
  95. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/tests/test_demo.py +9 -0
  96. tokenaudit-0.5.0/tests/test_detect.py +445 -0
  97. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/tests/test_eval.py +1 -1
  98. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/tests/test_ingest.py +78 -1
  99. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/tests/test_report.py +1 -1
  100. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/tokenaudit-baseline.json +5 -2
  101. tokenaudit-0.2.0/CHANGELOG.md +0 -58
  102. tokenaudit-0.2.0/eval/results.json +0 -668
  103. tokenaudit-0.2.0/src/tokenaudit/counterfactual/__init__.py +0 -1
  104. tokenaudit-0.2.0/src/tokenaudit/detect/base.py +0 -96
  105. tokenaudit-0.2.0/src/tokenaudit/detect/corpus_record.json +0 -56
  106. tokenaudit-0.2.0/tests/test_detect.py +0 -201
  107. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/.dockerignore +0 -0
  108. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/.github/workflows/ci.yml +0 -0
  109. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/.github/workflows/release.yml +0 -0
  110. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/.gitignore +0 -0
  111. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/.pre-commit-config.yaml +0 -0
  112. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/LICENSE +0 -0
  113. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/Makefile +0 -0
  114. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/action.yml +0 -0
  115. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/README.md +0 -0
  116. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/captured/README.md +0 -0
  117. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/captured/capture_tradingagents.sh +0 -0
  118. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/captured/redact.py +0 -0
  119. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/captured/runs/tradingagents/run-01-NVDA-2026-08-14/decision.txt +0 -0
  120. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/captured/runs/tradingagents/run-01-NVDA-2026-08-14/manifest.json +0 -0
  121. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/captured/runs/tradingagents/run-01-NVDA-2026-08-14/trace.jsonl +0 -0
  122. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/captured/runs/tradingagents/run-02-AAPL-2026-08-14/decision.txt +0 -0
  123. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/captured/runs/tradingagents/run-02-AAPL-2026-08-14/manifest.json +0 -0
  124. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/captured/runs/tradingagents/run-02-AAPL-2026-08-14/trace.jsonl +0 -0
  125. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/captured/runs/tradingagents/run-03-NVDA-2026-08-21/decision.txt +0 -0
  126. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/captured/runs/tradingagents/run-03-NVDA-2026-08-21/manifest.json +0 -0
  127. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/captured/runs/tradingagents/run-03-NVDA-2026-08-21/trace.jsonl +0 -0
  128. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/breakpoint_limit.trace.json +0 -0
  129. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/context_bloat.trace.json +0 -0
  130. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/healthy_cached.trace.json +0 -0
  131. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/healthy_incremental.trace.json +0 -0
  132. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/healthy_small.trace.json +0 -0
  133. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/lookback_overflow.trace.json +0 -0
  134. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/model_switch.trace.json +0 -0
  135. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/openai_healthy_cached.trace.json +0 -0
  136. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/prefix_drift.trace.json +0 -0
  137. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/prefix_drift_legacy_flat.trace.json +0 -0
  138. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/prefix_drift_no_content.trace.json +0 -0
  139. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/prefix_drift_openinference.trace.json +0 -0
  140. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/retry_storm.trace.json +0 -0
  141. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/sub_minimum.trace.json +0 -0
  142. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/tool_schema_churn.trace.json +0 -0
  143. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/corpus/synthetic/fixtures/ttl_expiry.trace.json +0 -0
  144. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/demo/Dockerfile +0 -0
  145. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/demo/__init__.py +0 -0
  146. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/demo/requirements.txt +0 -0
  147. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/docs/ci.md +0 -0
  148. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/docs/rulebook.md +0 -0
  149. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/ci/__init__.py +0 -0
  150. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/ci/check.py +0 -0
  151. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/detect/findings.py +0 -0
  152. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/detect/rules/__init__.py +0 -0
  153. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/examples/sample.trace.json +0 -0
  154. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/ingest/containers.py +0 -0
  155. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/ingest/dedup.py +0 -0
  156. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/ingest/quality.py +0 -0
  157. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/ingest/sessionize.py +0 -0
  158. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/model/__init__.py +0 -0
  159. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/py.typed +0 -0
  160. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/report/__init__.py +0 -0
  161. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/report/corpus_record.py +0 -0
  162. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/report/health.py +0 -0
  163. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/rulebook/__init__.py +0 -0
  164. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/rulebook/anthropic.json +0 -0
  165. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/rulebook/google.json +0 -0
  166. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/src/tokenaudit/rulebook/openai.json +0 -0
  167. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/tests/test_ci.py +0 -0
  168. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/tests/test_package.py +0 -0
  169. {tokenaudit-0.2.0 → tokenaudit-0.5.0}/tests/test_redact.py +0 -0
  170. {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.2.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.2.0). Interfaces may change before 1.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 and OTel SDK JSONL. Try it
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 app](demo/) diagnoses a dropped trace; uploads
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
- - [ ] Detectors for misordered shared context and cross-run prefix drift
123
- - [ ] Counterfactual pricing with per-model read rates and dollar ranges
124
- - [ ] Raw provider SDK log ingest, tool-schema bloat detector
125
- - [ ] Public demo deployment, case study on an Anthropic-model agent
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.2.0). Interfaces may change before 1.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 and OTel SDK JSONL. Try it
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 app](demo/) diagnoses a dropped trace; uploads
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
- - [ ] Detectors for misordered shared context and cross-run prefix drift
96
- - [ ] Counterfactual pricing with per-model read rates and dollar ranges
97
- - [ ] Raw provider SDK log ingest, tool-schema bloat detector
98
- - [ ] Public demo deployment, case study on an Anthropic-model agent
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 OpenAI SDK and LangChain at process level — the same zero-integration
5
- posture tokenaudit itself takes. Spans are written line-by-line using the OTel
6
- Python SDK's own ``ReadableSpan.to_json()`` rendering, with no custom mapping,
7
- so the captured dialect is exactly what the SDK produces in the wild.
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
- from openinference.instrumentation.openai import OpenAIInstrumentor
74
+ if llm_provider == "anthropic":
75
+ from openinference.instrumentation.anthropic import AnthropicInstrumentor
74
76
 
75
- OpenAIInstrumentor().instrument(tracer_provider=provider)
76
- active.append("openinference-instrumentation-openai")
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: OpenAI instrumentor not active: {exc}", file=sys.stderr)
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(ticker: str, date: str, deep: str, quick: str, rounds: int) -> str:
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"] = "openai"
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(args.ticker, args.date, args.deep, args.quick, args.rounds)
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
+ }