agent-budget-semantics 0.1.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 (46) hide show
  1. agent_budget_semantics-0.1.0/.gitignore +16 -0
  2. agent_budget_semantics-0.1.0/DIMENSIONS.md +125 -0
  3. agent_budget_semantics-0.1.0/LICENSE +21 -0
  4. agent_budget_semantics-0.1.0/PKG-INFO +320 -0
  5. agent_budget_semantics-0.1.0/README.md +251 -0
  6. agent_budget_semantics-0.1.0/cli.py +163 -0
  7. agent_budget_semantics-0.1.0/cost_divergence.py +228 -0
  8. agent_budget_semantics-0.1.0/harness.py +236 -0
  9. agent_budget_semantics-0.1.0/mock-llm/__init__.py +0 -0
  10. agent_budget_semantics-0.1.0/mock-llm/server.py +187 -0
  11. agent_budget_semantics-0.1.0/otel_comparison.py +213 -0
  12. agent_budget_semantics-0.1.0/otel_span_capture.py +265 -0
  13. agent_budget_semantics-0.1.0/pyproject.toml +120 -0
  14. agent_budget_semantics-0.1.0/report_generator.py +286 -0
  15. agent_budget_semantics-0.1.0/runners/__init__.py +19 -0
  16. agent_budget_semantics-0.1.0/runners/anthropic_runner.py +206 -0
  17. agent_budget_semantics-0.1.0/runners/base.py +34 -0
  18. agent_budget_semantics-0.1.0/runners/crewai_runner.py +190 -0
  19. agent_budget_semantics-0.1.0/runners/langchain_runner.py +195 -0
  20. agent_budget_semantics-0.1.0/runners/runner_adk.py +120 -0
  21. agent_budget_semantics-0.1.0/runners/runner_agno.py +107 -0
  22. agent_budget_semantics-0.1.0/runners/runner_anthropic.py +126 -0
  23. agent_budget_semantics-0.1.0/runners/runner_autogen.py +111 -0
  24. agent_budget_semantics-0.1.0/runners/runner_crewai.py +118 -0
  25. agent_budget_semantics-0.1.0/runners/runner_langchain.py +111 -0
  26. agent_budget_semantics-0.1.0/runners/runner_langgraph.py +106 -0
  27. agent_budget_semantics-0.1.0/runners/runner_llamaindex.py +105 -0
  28. agent_budget_semantics-0.1.0/runners/runner_openai_agents.py +109 -0
  29. agent_budget_semantics-0.1.0/runners/runner_semantic_kernel.py +111 -0
  30. agent_budget_semantics-0.1.0/runners/runner_swarm.py +103 -0
  31. agent_budget_semantics-0.1.0/runners/semantic_kernel_runner.py +196 -0
  32. agent_budget_semantics-0.1.0/scenarios/S10-nested-delegation-chain.yaml +167 -0
  33. agent_budget_semantics-0.1.0/scenarios/S11-timeout-cancellation.yaml +137 -0
  34. agent_budget_semantics-0.1.0/scenarios/S12-dynamic-budget-modification.yaml +147 -0
  35. agent_budget_semantics-0.1.0/scenarios/S3-multi-agent-delegation.yaml +135 -0
  36. agent_budget_semantics-0.1.0/scenarios/S4-parallel-tools.yaml +90 -0
  37. agent_budget_semantics-0.1.0/scenarios/S5-error-retry.yaml +112 -0
  38. agent_budget_semantics-0.1.0/scenarios/S6-token-budget.yaml +132 -0
  39. agent_budget_semantics-0.1.0/scenarios/S7-streaming-chunks.yaml +111 -0
  40. agent_budget_semantics-0.1.0/scenarios/S8-tool-output-explosion.yaml +136 -0
  41. agent_budget_semantics-0.1.0/scenarios/S9-system-prompt-attribution.yaml +137 -0
  42. agent_budget_semantics-0.1.0/scenarios/__init__.py +0 -0
  43. agent_budget_semantics-0.1.0/scenarios/s1_simple_tool_loop.yaml +56 -0
  44. agent_budget_semantics-0.1.0/scenarios/s2_budget_exhaustion.yaml +147 -0
  45. agent_budget_semantics-0.1.0/tests/__init__.py +0 -0
  46. agent_budget_semantics-0.1.0/tests/test_mock_llm.py +123 -0
@@ -0,0 +1,16 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .eggs/
8
+ *.egg
9
+ .venv/
10
+ venv/
11
+ env/
12
+ .pytest_cache/
13
+ .mypy_cache/
14
+ results/*.json
15
+ .DS_Store
16
+ .env
@@ -0,0 +1,125 @@
1
+ # Budget Semantics Divergence Dimensions
2
+
3
+ 24 dimensions of divergence discovered across 11 AI agent frameworks.
4
+
5
+ ## Core Iteration Semantics (D1-D4)
6
+
7
+ | Dim | Name | Question | Scenarios |
8
+ |-----|------|----------|-----------|
9
+ | D1 | Iteration unit | What counts as one iteration? | S1, S2 |
10
+ | D2 | Token accounting | Which tokens are counted? | S2, S6 |
11
+ | D3 | Enforcement point | Pre-call or post-call budget check? | S2 |
12
+ | D4 | Exhaustion behavior | What happens when budget runs out? | S2 |
13
+
14
+ ## Multi-Agent (D5-D6)
15
+
16
+ | Dim | Name | Question | Scenarios |
17
+ |-----|------|----------|-----------|
18
+ | D5 | Budget delegation | How is budget shared with sub-agents? | S3, S10 |
19
+ | D6 | Delegation cost | Does delegating itself consume budget? | S3, S10 |
20
+
21
+ ## Parallel Execution (D7-D8)
22
+
23
+ | Dim | Name | Question | Scenarios |
24
+ |-----|------|----------|-----------|
25
+ | D7 | Parallel tool counting | N parallel tools = how many units? | S4 |
26
+ | D8 | Partial batch execution | If budget=2 and batch=3, run 2 or 0? | S4 |
27
+
28
+ ## Error Handling (D9-D10)
29
+
30
+ | Dim | Name | Question | Scenarios |
31
+ |-----|------|----------|-----------|
32
+ | D9 | Error/retry counting | Failed + retry = 1 or 2 units? | S5 |
33
+ | D10 | Error propagation | How is the error communicated to LLM? | S5 |
34
+
35
+ ## Token Budget (D11-D12)
36
+
37
+ | Dim | Name | Question | Scenarios |
38
+ |-----|------|----------|-----------|
39
+ | D11 | Token budget enforcement | Cumulative token tracking? | S6 |
40
+ | D12 | Token counting method | Total, completion-only, or prompt-only? | S6 |
41
+
42
+ ## Streaming (D13-D14)
43
+
44
+ | Dim | Name | Question | Scenarios |
45
+ |-----|------|----------|-----------|
46
+ | D13 | Streaming chunk counting | Streamed response = 1 or N budget units? | S7 |
47
+ | D14 | Streaming token attribution | Single usage record or incremental? | S7 |
48
+
49
+ ## Context Growth (D15-D16)
50
+
51
+ | Dim | Name | Question | Scenarios |
52
+ |-----|------|----------|-----------|
53
+ | D15 | Tool output token counting | Large tool output counts in budget? | S8 |
54
+ | D16 | Context growth attribution | Growing prompt attributed to which span? | S8 |
55
+
56
+ ## System Prompt (D17-D18)
57
+
58
+ | Dim | Name | Question | Scenarios |
59
+ |-----|------|----------|-----------|
60
+ | D17 | System prompt attribution | Counted once, every turn, or never? | S9 |
61
+ | D18 | System prompt telemetry | How represented in OTel spans? | S9 |
62
+
63
+ ## Deep Delegation (D19-D20)
64
+
65
+ | Dim | Name | Question | Scenarios |
66
+ |-----|------|----------|-----------|
67
+ | D19 | Nested inheritance | Budget across 3+ delegation levels? | S10 |
68
+ | D20 | Delegation cost visibility | Parent see child's consumption? | S10 |
69
+
70
+ ## Fault Tolerance (D21-D22)
71
+
72
+ | Dim | Name | Question | Scenarios |
73
+ |-----|------|----------|-----------|
74
+ | D21 | Timeout budget impact | Timed-out call consumes budget? | S11 |
75
+ | D22 | Timeout token attribution | Tokens from failed request counted? | S11 |
76
+
77
+ ## Dynamic Budget (D23-D24)
78
+
79
+ | Dim | Name | Question | Scenarios |
80
+ |-----|------|----------|-----------|
81
+ | D23 | Dynamic budget support | Can budget change mid-execution? | S12 |
82
+ | D24 | Budget modification telemetry | How to represent changes in OTel? | S12 |
83
+
84
+ ## Framework Coverage Matrix
85
+
86
+ | Framework | D1 | D5 | D7 | D9 | D11 | D13 | D15 | D17 | D19 | D21 | D23 |
87
+ |-----------|----|----|----|----|-----|-----|-----|-----|-----|-----|-----|
88
+ | AutoGen | msg | shared | N | counts | callback | transparent | configurable | every-turn | shared-pool | counts | callback |
89
+ | OpenAI Agents | llm | shared | 1 | counts | none | transparent | no | every-turn | shared | counts | no |
90
+ | LangChain | cycle | n/a | 1 | configurable | none | transparent | no | every-turn | n/a | configurable | no |
91
+ | LangGraph | node | subtract | 1 | counts | none | transparent | indirect | every-turn | subtract | counts | no |
92
+ | CrewAI | cycle | independent | n/a | free | none | n/a | no | every-turn | independent | free | no |
93
+ | ADK | loop | remaining | 1 | counts | configurable | transparent | configurable | once | remaining | counts | no |
94
+ | Semantic Kernel | invoke | n/a | 1 | free | none | transparent | no | every-turn | n/a | counts | mutable |
95
+ | Anthropic | client | n/a | client | client | per-call | transparent | indirect | per-call | n/a | client | n/a |
96
+ | Swarm | msg | shared | 2N | counts | none | transparent | no | every-turn | shared | counts | mutable |
97
+ | LlamaIndex | step | n/a | separate | counts | none | transparent | no | every-turn | n/a | counts | no |
98
+ | Agno | cycle | team-pool | 1 | counts | output-only | transparent | no | every-turn | team-pool | counts | no |
99
+
100
+ ## Key Finding
101
+
102
+ For `gen_ai.agent.iteration_budget.consumed` with the SAME execution
103
+ (4 LLM calls, 3 tool calls):
104
+
105
+ ```
106
+ consumed = [3, 4, 7, 10]
107
+ ```
108
+
109
+ depending on which framework. 4 different values. Not edge cases or
110
+ implementation bugs. Fundamental design disagreements about what "one iteration"
111
+ means.
112
+
113
+ ## Impact on OTel Semantic Conventions
114
+
115
+ Without mandatory counting semantics metadata, the budget attributes in
116
+ PR #439 are:
117
+
118
+ 1. Not comparable across frameworks (the primary use case for OTel)
119
+ 2. Meaningless for multi-framework dashboards
120
+ 3. Incorrect for cost attribution and chargeback
121
+ 4. Unstable for alert thresholds (same execution, different consumed values)
122
+ 5. Misleading for capacity planning
123
+
124
+ Proposed fix: mandatory `gen_ai.agent.iteration_budget.counting_method` enum
125
+ that classifies the framework's counting approach.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Elankumaran Srinivasan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,320 @@
1
+ Metadata-Version: 2.5
2
+ Name: agent-budget-semantics
3
+ Version: 0.1.0
4
+ Summary: Differential testing of budget enforcement semantics across AI agent frameworks
5
+ Project-URL: Homepage, https://github.com/elang2/agent-budget-semantics
6
+ Project-URL: Repository, https://github.com/elang2/agent-budget-semantics
7
+ Project-URL: Issues, https://github.com/elang2/agent-budget-semantics/issues
8
+ Project-URL: Documentation, https://github.com/elang2/agent-budget-semantics#readme
9
+ Author: Elankumaran Srinivasan
10
+ License-Expression: Apache-2.0
11
+ License-File: LICENSE
12
+ Keywords: agent,anthropic,autogen,budget,conformance,crewai,differential-testing,genai,google-adk,langchain,langgraph,llm,openai-agents,opentelemetry,semantic-kernel,telemetry
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: Apache Software License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
21
+ Classifier: Topic :: Software Development :: Testing
22
+ Requires-Python: >=3.11
23
+ Requires-Dist: httpx>=0.27
24
+ Requires-Dist: pyyaml>=6.0
25
+ Provides-Extra: adk
26
+ Requires-Dist: google-adk>=1.0; extra == 'adk'
27
+ Provides-Extra: agno
28
+ Requires-Dist: agno>=1.0; extra == 'agno'
29
+ Provides-Extra: all
30
+ Requires-Dist: agno>=1.0; extra == 'all'
31
+ Requires-Dist: anthropic>=0.34; extra == 'all'
32
+ Requires-Dist: autogen-agentchat>=0.4; extra == 'all'
33
+ Requires-Dist: autogen-ext>=0.4; extra == 'all'
34
+ Requires-Dist: crewai>=0.100; extra == 'all'
35
+ Requires-Dist: google-adk>=1.0; extra == 'all'
36
+ Requires-Dist: langchain-openai>=0.3; extra == 'all'
37
+ Requires-Dist: langchain>=0.3; extra == 'all'
38
+ Requires-Dist: langgraph>=0.3; extra == 'all'
39
+ Requires-Dist: llama-index-core>=0.11; extra == 'all'
40
+ Requires-Dist: llama-index-llms-openai>=0.3; extra == 'all'
41
+ Requires-Dist: openai-agents>=0.1; extra == 'all'
42
+ Requires-Dist: openai-swarm>=0.1; extra == 'all'
43
+ Requires-Dist: semantic-kernel>=1.0; extra == 'all'
44
+ Provides-Extra: anthropic
45
+ Requires-Dist: anthropic>=0.34; extra == 'anthropic'
46
+ Provides-Extra: autogen
47
+ Requires-Dist: autogen-agentchat>=0.4; extra == 'autogen'
48
+ Requires-Dist: autogen-ext>=0.4; extra == 'autogen'
49
+ Provides-Extra: crewai
50
+ Requires-Dist: crewai>=0.100; extra == 'crewai'
51
+ Provides-Extra: dev
52
+ Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
53
+ Requires-Dist: pytest>=8.0; extra == 'dev'
54
+ Provides-Extra: langchain
55
+ Requires-Dist: langchain-openai>=0.3; extra == 'langchain'
56
+ Requires-Dist: langchain>=0.3; extra == 'langchain'
57
+ Provides-Extra: langgraph
58
+ Requires-Dist: langgraph>=0.3; extra == 'langgraph'
59
+ Provides-Extra: llamaindex
60
+ Requires-Dist: llama-index-core>=0.11; extra == 'llamaindex'
61
+ Requires-Dist: llama-index-llms-openai>=0.3; extra == 'llamaindex'
62
+ Provides-Extra: openai-agents
63
+ Requires-Dist: openai-agents>=0.1; extra == 'openai-agents'
64
+ Provides-Extra: semantic-kernel
65
+ Requires-Dist: semantic-kernel>=1.0; extra == 'semantic-kernel'
66
+ Provides-Extra: swarm
67
+ Requires-Dist: openai-swarm>=0.1; extra == 'swarm'
68
+ Description-Content-Type: text/markdown
69
+
70
+ # agent-budget-semantics
71
+
72
+ Differential testing of budget enforcement semantics across 11 AI agent frameworks.
73
+
74
+ ## The Problem
75
+
76
+ ```
77
+ gen_ai.agent.iteration_budget.consumed = [3, 4, 7, 10]
78
+ ```
79
+
80
+ Same work. Same LLM calls. Same tokens consumed. Four different telemetry values depending on which framework is instrumented. Setting `budget=3` means something fundamentally different across frameworks.
81
+
82
+ ## The Evidence
83
+
84
+ | Framework | `budget=3` means | Parallel 3 tools | Error retry | Final answer |
85
+ |-----------|-----------------|------------------|-------------|--------------|
86
+ | AutoGen | 3 messages (LLM + tool mixed) | 3 budget units | Counts | Counts |
87
+ | OpenAI Agents | 3 LLM invocations | 1 budget unit | Counts | Counts |
88
+ | LangChain | 3 tool-call cycles | 1 budget unit | Configurable | Free |
89
+ | LangGraph | 3 node executions | 1 budget unit | Counts | Counts |
90
+ | CrewAI | 3 tool-use cycles | N/A | Free | Free extra call |
91
+ | Google ADK | 3 full agent loops | 1 budget unit | Counts | Part of last |
92
+ | Semantic Kernel | 3 auto-invoke attempts | 1 budget unit | Free | Not counted |
93
+ | Anthropic | Client-defined | Client decides | Client decides | Client decides |
94
+ | Swarm | Messages in history | 2N budget units | Counts | Counts |
95
+ | LlamaIndex | 3 ReAct steps | Separate budget | Counts | Free extra |
96
+ | Agno | 3 tool-use cycles | 1 budget unit | Counts | Part of flow |
97
+
98
+ ## Install
99
+
100
+ ```bash
101
+ pip install agent-budget-semantics
102
+ ```
103
+
104
+ Or with Docker (no dependencies):
105
+
106
+ ```bash
107
+ docker run --rm ghcr.io/elang2/agent-budget-semantics compare
108
+ docker run --rm ghcr.io/elang2/agent-budget-semantics cost
109
+ docker run --rm ghcr.io/elang2/agent-budget-semantics spans
110
+ ```
111
+
112
+ ## Quick Start
113
+
114
+ ```bash
115
+ # Show the iteration divergence matrix
116
+ agent-budget-semantics compare
117
+
118
+ # Show cost divergence ($97K/year spread at scale)
119
+ agent-budget-semantics cost --daily-runs 1000
120
+
121
+ # Show OTel telemetry divergence (what your dashboard would show)
122
+ agent-budget-semantics spans
123
+
124
+ # Generate full report suite (markdown + JSON)
125
+ agent-budget-semantics report --output reports/
126
+
127
+ # Run differential tests against a specific framework
128
+ pip install "agent-budget-semantics[autogen]"
129
+ agent-budget-semantics run --scenario scenarios/s2_budget_exhaustion.yaml --frameworks autogen
130
+
131
+ # Run all frameworks
132
+ pip install "agent-budget-semantics[all]"
133
+ agent-budget-semantics run --all
134
+ ```
135
+
136
+ ## What It Produces
137
+
138
+ ### Iteration divergence (the headline finding)
139
+
140
+ ```
141
+ Framework consumed utilization Counting method
142
+ --------------------------------------------------------------------------------
143
+ autogen 7 233% Each message (LLM response OR tool result)
144
+ openai_agents 4 133% Each full LLM invocation
145
+ langchain 3 100% Each tool-use cycle
146
+ langgraph 7 233% Each graph node execution
147
+ swarm 10 333% Messages added to history
148
+ ```
149
+
150
+ ### Cost divergence (makes it tangible)
151
+
152
+ ```
153
+ Monthly Cost Projection (1000 runs/day)
154
+ ----------------------------------------------------------------------
155
+ langchain $5,850/mo baseline
156
+ openai_agents $6,750/mo +$900 (+15%)
157
+ autogen $10,350/mo +$4,500 (+77%)
158
+ swarm $13,950/mo +$8,100 (+138%)
159
+
160
+ Annual spread: $97,200 — from iteration counting alone.
161
+ ```
162
+
163
+ ### OTel span structure (what your dashboard shows)
164
+
165
+ ```
166
+ Framework Spans consumed util% Structure
167
+ ------------------------------------------------------------------------------------------
168
+ autogen 8 7 233% root → 4 llm → 3 tool
169
+ langchain 6 3 100% root → 4 llm → 1 batch
170
+ swarm 8 10 333% root → 4 llm → 3 tool
171
+ ```
172
+
173
+ ## 12 Scenarios, 24 Dimensions
174
+
175
+ | Scenario | Tests | Dimensions |
176
+ |----------|-------|-----------|
177
+ | S1: Simple tool loop | Baseline behavior | D1-D4 |
178
+ | S2: Budget exhaustion | Enforcement boundaries | D1-D4 |
179
+ | S3: Multi-agent delegation | Budget sharing | D5-D6 |
180
+ | S4: Parallel tools | Batch counting | D7-D8 |
181
+ | S5: Error/retry | Retry budget impact | D9-D10 |
182
+ | S6: Token budget | Cumulative token tracking | D11-D12 |
183
+ | S7: Streaming | Chunk counting | D13-D14 |
184
+ | S8: Tool output explosion | Large response attribution | D15-D16 |
185
+ | S9: System prompt | Repeated prompt tokens | D17-D18 |
186
+ | S10: Nested delegation | 3-level inheritance | D19-D20 |
187
+ | S11: Timeout/cancellation | Failed call budget impact | D21-D22 |
188
+ | S12: Dynamic budget | Mid-run modification | D23-D24 |
189
+
190
+ See [DIMENSIONS.md](DIMENSIONS.md) for the full taxonomy with per-framework behavior.
191
+
192
+ ## Use in CI
193
+
194
+ Drop into `.github/workflows/budget-conformance.yml`:
195
+
196
+ ```yaml
197
+ name: Budget Semantics Check
198
+ on: [push, pull_request]
199
+
200
+ jobs:
201
+ check:
202
+ runs-on: ubuntu-latest
203
+ steps:
204
+ - uses: actions/checkout@v4
205
+
206
+ - uses: actions/setup-python@v5
207
+ with:
208
+ python-version: '3.12'
209
+
210
+ - name: Install
211
+ run: pip install agent-budget-semantics
212
+
213
+ - name: Run comparison
214
+ run: |
215
+ agent-budget-semantics compare
216
+ agent-budget-semantics cost
217
+ agent-budget-semantics report --output budget-report/
218
+
219
+ - name: Upload report
220
+ uses: actions/upload-artifact@v4
221
+ with:
222
+ name: budget-divergence-report
223
+ path: budget-report/
224
+ ```
225
+
226
+ Or with Docker (no Python setup needed):
227
+
228
+ ```yaml
229
+ jobs:
230
+ check:
231
+ runs-on: ubuntu-latest
232
+ container:
233
+ image: ghcr.io/elang2/agent-budget-semantics:latest
234
+ steps:
235
+ - run: agent-budget-semantics compare
236
+ - run: agent-budget-semantics cost --daily-runs 500
237
+ ```
238
+
239
+ ## How It Works
240
+
241
+ ```
242
+ ┌─────────────┐ ┌──────────────┐ ┌───────────────┐
243
+ │ Scenario │────▶│ Mock LLM │◀────│ Framework │
244
+ │ (YAML) │ │ (ledger) │ │ Runner │
245
+ └─────────────┘ └──────┬───────┘ └───────────────┘
246
+
247
+
248
+ Ground Truth
249
+ (actual calls,
250
+ actual tokens)
251
+ ```
252
+
253
+ A deterministic mock LLM with a request ledger serves as ground truth. Scripted scenarios force tool-calling loops of known depth. Each framework runner executes the same scenario against the same mock. The harness compares what each framework reports vs. what actually happened.
254
+
255
+ No real LLM API keys needed. No flaky network calls. Fully reproducible.
256
+
257
+ ## Three Architectural Models
258
+
259
+ Testing revealed three fundamentally different approaches to budget enforcement:
260
+
261
+ 1. **Client-side only** (Anthropic) — No server-side budget concept. The client library decides when to stop. The API has no awareness of iteration limits.
262
+
263
+ 2. **Framework-enforced** (9 frameworks) — The framework wraps the LLM API and applies its own budget logic. Each framework counts differently, producing the 4-value divergence.
264
+
265
+ 3. **Server-side opaque** (AWS Bedrock) — The server enforces budget internally. The client cannot observe or control the counting mechanism.
266
+
267
+ ## Relevance to OTel GenAI Conventions
268
+
269
+ This project provides empirical evidence for the budget governance discussion in the OpenTelemetry semantic conventions. Without mandatory counting semantics metadata, `gen_ai.agent.iteration_budget.consumed` is not comparable across frameworks.
270
+
271
+ Proposed fix: mandatory `counting_method` enum that classifies the framework's approach:
272
+
273
+ ```
274
+ gen_ai.agent.iteration_budget.counting_method
275
+ Values: llm_calls | tool_cycles | graph_nodes | messages
276
+ ```
277
+
278
+ Related PRs/Issues:
279
+ - open-telemetry/semantic-conventions #439 (budget governance attributes)
280
+ - open-telemetry/semantic-conventions #451 (turn count)
281
+ - open-telemetry/semantic-conventions #447 (agent delegation)
282
+ - open-telemetry/semantic-conventions #4025 (retry counting)
283
+
284
+ ## Project Structure
285
+
286
+ ```
287
+ cli.py CLI entry point
288
+ harness.py Test orchestrator
289
+ otel_comparison.py Iteration divergence analysis
290
+ otel_span_capture.py OTel telemetry simulation
291
+ cost_divergence.py Cost impact calculator
292
+ report_generator.py Markdown/JSON report suite
293
+ DIMENSIONS.md 24-dimension taxonomy
294
+
295
+ mock-llm/ Deterministic mock LLM server
296
+ server.py OpenAI-compatible API with request ledger
297
+
298
+ runners/ Per-framework adapters (11 frameworks)
299
+ runner_autogen.py
300
+ runner_openai_agents.py
301
+ runner_langchain.py
302
+ runner_langgraph.py
303
+ runner_crewai.py
304
+ runner_adk.py
305
+ runner_semantic_kernel.py
306
+ runner_anthropic.py
307
+ runner_swarm.py
308
+ runner_llamaindex.py
309
+ runner_agno.py
310
+
311
+ scenarios/ YAML-defined test scenarios (12 scenarios)
312
+ S1-S12 Covering 24 divergence dimensions
313
+
314
+ tests/ Unit tests
315
+ reports/ Generated report artifacts
316
+ ```
317
+
318
+ ## License
319
+
320
+ Apache-2.0