class1 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 (177) hide show
  1. class1-0.1.0/PKG-INFO +135 -0
  2. class1-0.1.0/README.md +119 -0
  3. class1-0.1.0/blue_book/actuals.py +126 -0
  4. class1-0.1.0/blue_book/actuals_history.py +503 -0
  5. class1-0.1.0/blue_book/allocation.py +31 -0
  6. class1-0.1.0/blue_book/calibration_data.py +544 -0
  7. class1-0.1.0/blue_book/estimate_store.py +101 -0
  8. class1-0.1.0/blue_book/focus.py +143 -0
  9. class1-0.1.0/blue_book/footprint_actuals.py +93 -0
  10. class1-0.1.0/blue_book/ingest.py +61 -0
  11. class1-0.1.0/blue_book/ingest_core.py +184 -0
  12. class1-0.1.0/blue_book/mine.py +171 -0
  13. class1-0.1.0/blue_book/opendata.py +180 -0
  14. class1-0.1.0/blue_book/otel_receiver.py +260 -0
  15. class1-0.1.0/blue_book/search_first.py +28 -0
  16. class1-0.1.0/blue_book/usage_openai.py +57 -0
  17. class1-0.1.0/class1.egg-info/PKG-INFO +135 -0
  18. class1-0.1.0/class1.egg-info/SOURCES.txt +175 -0
  19. class1-0.1.0/class1.egg-info/dependency_links.txt +1 -0
  20. class1-0.1.0/class1.egg-info/entry_points.txt +2 -0
  21. class1-0.1.0/class1.egg-info/requires.txt +10 -0
  22. class1-0.1.0/class1.egg-info/top_level.txt +4 -0
  23. class1-0.1.0/cost_engine/__init__.py +97 -0
  24. class1-0.1.0/cost_engine/aliases.py +53 -0
  25. class1-0.1.0/cost_engine/autopoiesis.py +109 -0
  26. class1-0.1.0/cost_engine/basis.py +118 -0
  27. class1-0.1.0/cost_engine/basis_of_estimate.py +65 -0
  28. class1-0.1.0/cost_engine/budget.py +101 -0
  29. class1-0.1.0/cost_engine/calibration.py +136 -0
  30. class1-0.1.0/cost_engine/calibrator.py +91 -0
  31. class1-0.1.0/cost_engine/capability.py +130 -0
  32. class1-0.1.0/cost_engine/capability_data.py +43 -0
  33. class1-0.1.0/cost_engine/capex.py +90 -0
  34. class1-0.1.0/cost_engine/classification.py +45 -0
  35. class1-0.1.0/cost_engine/cloud_cost.py +78 -0
  36. class1-0.1.0/cost_engine/commitment.py +41 -0
  37. class1-0.1.0/cost_engine/contingency.py +26 -0
  38. class1-0.1.0/cost_engine/distributions.py +28 -0
  39. class1-0.1.0/cost_engine/energy.py +92 -0
  40. class1-0.1.0/cost_engine/escalation.py +40 -0
  41. class1-0.1.0/cost_engine/estimate_decay.py +38 -0
  42. class1-0.1.0/cost_engine/evidence.py +217 -0
  43. class1-0.1.0/cost_engine/grades_real.py +123 -0
  44. class1-0.1.0/cost_engine/mcp_overhead.py +80 -0
  45. class1-0.1.0/cost_engine/monte_carlo.py +107 -0
  46. class1-0.1.0/cost_engine/prices.py +89 -0
  47. class1-0.1.0/cost_engine/pricing_loader.py +35 -0
  48. class1-0.1.0/cost_engine/recommend.py +65 -0
  49. class1-0.1.0/cost_engine/report.py +69 -0
  50. class1-0.1.0/cost_engine/scenario.py +106 -0
  51. class1-0.1.0/cost_engine/self_cost.py +27 -0
  52. class1-0.1.0/cost_engine/structured_price.py +162 -0
  53. class1-0.1.0/pyproject.toml +36 -0
  54. class1-0.1.0/setup.cfg +4 -0
  55. class1-0.1.0/snapshots/__init__.py +0 -0
  56. class1-0.1.0/snapshots/actuals_index.json +738 -0
  57. class1-0.1.0/snapshots/actuarial_table_real.json +2132 -0
  58. class1-0.1.0/snapshots/autobuild_runs.json +176 -0
  59. class1-0.1.0/snapshots/capability.json +280 -0
  60. class1-0.1.0/snapshots/cloud_price_index.json +219694 -0
  61. class1-0.1.0/snapshots/estimates.json +54 -0
  62. class1-0.1.0/snapshots/footprint_basis.json +33 -0
  63. class1-0.1.0/snapshots/grid_intensity.json +31 -0
  64. class1-0.1.0/snapshots/price_index.json +45810 -0
  65. class1-0.1.0/snapshots/pricing.json +49088 -0
  66. class1-0.1.0/snapshots/pricing_structure.json +10193 -0
  67. class1-0.1.0/snapshots/spec_sheet.json +419463 -0
  68. class1-0.1.0/snapshots/water_basis.json +17 -0
  69. class1-0.1.0/takeoff/__init__.py +14 -0
  70. class1-0.1.0/takeoff/estimate_pr.py +423 -0
  71. class1-0.1.0/takeoff/license.py +91 -0
  72. class1-0.1.0/takeoff/pilot.py +68 -0
  73. class1-0.1.0/takeoff/policy.py +96 -0
  74. class1-0.1.0/takeoff/post_pr.py +67 -0
  75. class1-0.1.0/takeoff/scan.py +354 -0
  76. class1-0.1.0/takeoff/scan_treesitter.py +173 -0
  77. class1-0.1.0/takeoff/translate.py +92 -0
  78. class1-0.1.0/tests/test_actuals.py +51 -0
  79. class1-0.1.0/tests/test_actuals_history.py +191 -0
  80. class1-0.1.0/tests/test_actuarial.py +52 -0
  81. class1-0.1.0/tests/test_aliases.py +31 -0
  82. class1-0.1.0/tests/test_allocation.py +27 -0
  83. class1-0.1.0/tests/test_analyze.py +61 -0
  84. class1-0.1.0/tests/test_analyze_trends.py +12 -0
  85. class1-0.1.0/tests/test_api_sources.py +125 -0
  86. class1-0.1.0/tests/test_autobuild_agents.py +254 -0
  87. class1-0.1.0/tests/test_autobuild_loop.py +348 -0
  88. class1-0.1.0/tests/test_autobuild_robustness.py +23 -0
  89. class1-0.1.0/tests/test_autopoiesis.py +139 -0
  90. class1-0.1.0/tests/test_basis.py +105 -0
  91. class1-0.1.0/tests/test_basis_of_estimate.py +49 -0
  92. class1-0.1.0/tests/test_budget.py +200 -0
  93. class1-0.1.0/tests/test_calibration.py +25 -0
  94. class1-0.1.0/tests/test_calibration_data.py +366 -0
  95. class1-0.1.0/tests/test_calibrator.py +58 -0
  96. class1-0.1.0/tests/test_capability_data.py +27 -0
  97. class1-0.1.0/tests/test_capability_ingest.py +44 -0
  98. class1-0.1.0/tests/test_capability_tail.py +58 -0
  99. class1-0.1.0/tests/test_capex.py +44 -0
  100. class1-0.1.0/tests/test_classification.py +31 -0
  101. class1-0.1.0/tests/test_cloud_cost.py +29 -0
  102. class1-0.1.0/tests/test_cloud_prices.py +53 -0
  103. class1-0.1.0/tests/test_code_harvest.py +71 -0
  104. class1-0.1.0/tests/test_commitment.py +29 -0
  105. class1-0.1.0/tests/test_contingency.py +34 -0
  106. class1-0.1.0/tests/test_distributions.py +117 -0
  107. class1-0.1.0/tests/test_drift.py +147 -0
  108. class1-0.1.0/tests/test_driver.py +232 -0
  109. class1-0.1.0/tests/test_effort.py +23 -0
  110. class1-0.1.0/tests/test_energy.py +55 -0
  111. class1-0.1.0/tests/test_escalation.py +46 -0
  112. class1-0.1.0/tests/test_estimate_decay.py +26 -0
  113. class1-0.1.0/tests/test_estimate_pr.py +72 -0
  114. class1-0.1.0/tests/test_estimate_pr_grade_join.py +22 -0
  115. class1-0.1.0/tests/test_estimate_store.py +35 -0
  116. class1-0.1.0/tests/test_evidence.py +291 -0
  117. class1-0.1.0/tests/test_evidence_signed.py +27 -0
  118. class1-0.1.0/tests/test_focus.py +114 -0
  119. class1-0.1.0/tests/test_footprint_actuals.py +47 -0
  120. class1-0.1.0/tests/test_footprint_carbon.py +40 -0
  121. class1-0.1.0/tests/test_footprint_materials.py +35 -0
  122. class1-0.1.0/tests/test_footprint_mc.py +50 -0
  123. class1-0.1.0/tests/test_footprint_water.py +43 -0
  124. class1-0.1.0/tests/test_github_action_api.py +87 -0
  125. class1-0.1.0/tests/test_governor.py +177 -0
  126. class1-0.1.0/tests/test_grades_real.py +90 -0
  127. class1-0.1.0/tests/test_harvest_recommend.py +50 -0
  128. class1-0.1.0/tests/test_health.py +335 -0
  129. class1-0.1.0/tests/test_health_fix_tasks.py +26 -0
  130. class1-0.1.0/tests/test_history_parsers.py +136 -0
  131. class1-0.1.0/tests/test_ingest_core.py +62 -0
  132. class1-0.1.0/tests/test_jobs.py +240 -0
  133. class1-0.1.0/tests/test_js_engine_parity.py +93 -0
  134. class1-0.1.0/tests/test_license.py +136 -0
  135. class1-0.1.0/tests/test_lock.py +155 -0
  136. class1-0.1.0/tests/test_lock_stale.py +108 -0
  137. class1-0.1.0/tests/test_mcp_overhead.py +42 -0
  138. class1-0.1.0/tests/test_metering.py +171 -0
  139. class1-0.1.0/tests/test_metering_usage_records.py +105 -0
  140. class1-0.1.0/tests/test_mine.py +256 -0
  141. class1-0.1.0/tests/test_model_swap_delta.py +39 -0
  142. class1-0.1.0/tests/test_monte_carlo.py +126 -0
  143. class1-0.1.0/tests/test_opendata.py +34 -0
  144. class1-0.1.0/tests/test_organism.py +101 -0
  145. class1-0.1.0/tests/test_otel_receiver.py +114 -0
  146. class1-0.1.0/tests/test_pilot.py +21 -0
  147. class1-0.1.0/tests/test_planner.py +323 -0
  148. class1-0.1.0/tests/test_planner_tuple_provider.py +18 -0
  149. class1-0.1.0/tests/test_policy_gate.py +232 -0
  150. class1-0.1.0/tests/test_post_pr.py +23 -0
  151. class1-0.1.0/tests/test_preflight.py +42 -0
  152. class1-0.1.0/tests/test_price_history.py +41 -0
  153. class1-0.1.0/tests/test_prices.py +48 -0
  154. class1-0.1.0/tests/test_prices_medallion.py +306 -0
  155. class1-0.1.0/tests/test_provider_local.py +38 -0
  156. class1-0.1.0/tests/test_provider_usage.py +57 -0
  157. class1-0.1.0/tests/test_recommend.py +55 -0
  158. class1-0.1.0/tests/test_report_boe.py +21 -0
  159. class1-0.1.0/tests/test_resilience.py +222 -0
  160. class1-0.1.0/tests/test_roadmap.py +184 -0
  161. class1-0.1.0/tests/test_router.py +56 -0
  162. class1-0.1.0/tests/test_scan.py +141 -0
  163. class1-0.1.0/tests/test_scan_bare_imports.py +40 -0
  164. class1-0.1.0/tests/test_scan_gemini_langchain.py +110 -0
  165. class1-0.1.0/tests/test_scan_inferred_model.py +54 -0
  166. class1-0.1.0/tests/test_scan_treesitter.py +49 -0
  167. class1-0.1.0/tests/test_scenario.py +190 -0
  168. class1-0.1.0/tests/test_scout.py +179 -0
  169. class1-0.1.0/tests/test_self_calibrate.py +192 -0
  170. class1-0.1.0/tests/test_self_calibrate_measured.py +97 -0
  171. class1-0.1.0/tests/test_self_cost.py +19 -0
  172. class1-0.1.0/tests/test_source_catalogs.py +44 -0
  173. class1-0.1.0/tests/test_spec_sheet.py +81 -0
  174. class1-0.1.0/tests/test_structured_price.py +98 -0
  175. class1-0.1.0/tests/test_translate.py +122 -0
  176. class1-0.1.0/tests/test_usage_openai.py +40 -0
  177. class1-0.1.0/tests/test_variance_waterfall.py +34 -0
class1-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,135 @@
1
+ Metadata-Version: 2.4
2
+ Name: class1
3
+ Version: 0.1.0
4
+ Summary: Monthly LLM cost-risk estimation and project controls for AI systems
5
+ Requires-Python: >=3.11
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: numpy>=1.26.0
8
+ Requires-Dist: pandas>=2.0.0
9
+ Requires-Dist: openai>=1.0.0
10
+ Requires-Dist: PyJWT>=2.8.0
11
+ Requires-Dist: cryptography>=42.0.0
12
+ Provides-Extra: dev
13
+ Requires-Dist: pytest~=8.3.0; extra == "dev"
14
+ Requires-Dist: mypy; extra == "dev"
15
+ Requires-Dist: ruff; extra == "dev"
16
+
17
+ # Class1 (Internal name: abc7d) — Project controls and cost-risk estimation for AI systems
18
+
19
+ Class1 is a high-performance cost-engineering tool for LLM-based systems. Unlike typical observability tools that look backward at historical spend, Class1 focuses on **estimation**: providing a risk-adjusted forecast of a code change's monthly cost delta **in the pull request, before merge**, with a declared AACE estimate class and a calibration loop.
20
+
21
+ Cost engineering, not dashboards.
22
+
23
+ ---
24
+
25
+ ## How it works
26
+
27
+ When a developer submits a PR, the Class1 GitHub Action:
28
+ 1. **Scans the diff** to identify added, modified, or removed LLM callsites (Python AST + TS/JS tree-sitter).
29
+ 2. **Translates code changes** (model swaps, token changes, tool changes) into baseline vs. head scenarios.
30
+ 3. **Runs a Monte Carlo simulation** using Common Random Numbers (CRN) to estimate the monthly cost delta.
31
+ 4. **Applies a budget policy** to automatically warn or block the PR if the P90 tail risk exceeds your threshold.
32
+ 5. **Posts a detailed cost-risk report** directly to the PR comments.
33
+
34
+ ```
35
+ ABC7D · AI cost-risk report
36
+ =========================================
37
+ Expected Monthly Delta: +$124.50
38
+ P50 (Median) Delta: +$112.00
39
+ P90 (Tail Risk) Delta: +$210.00
40
+ P95 Delta: +$245.00
41
+
42
+ Estimate Class: Class 4 (Study)
43
+ Accuracy Range: -30% / +50%
44
+
45
+ Recommendation:
46
+ Optimal model selected for the workload. Under-spec models (e.g. gpt-4o-mini)
47
+ would result in lower unit cost but higher overall cost due to retries (+14%).
48
+
49
+ Verdict: PASS
50
+ ```
51
+
52
+ ---
53
+
54
+ ## Getting Started: GitHub Action Setup
55
+
56
+ Add the Class1 check to your repository in three simple steps:
57
+
58
+ ### 1. Create a budget policy
59
+ Add a `.class1.json` file at the root of your repository:
60
+ ```json
61
+ {
62
+ "fail_pr_if": {
63
+ "delta_p90_usd": 500.0,
64
+ "warn_at_fraction": 0.8
65
+ }
66
+ }
67
+ ```
68
+ *If a PR's estimated monthly P90 cost increase exceeds $500, the GitHub status check will fail, blocking the merge.*
69
+
70
+ ### 2. Configure the GitHub Actions workflow
71
+ Create `.github/workflows/class1.yml`:
72
+ ```yaml
73
+ name: Class1 Cost-Risk Guard
74
+
75
+ on:
76
+ pull_request:
77
+ branches: [ main ]
78
+
79
+ jobs:
80
+ estimate:
81
+ runs-on: ubuntu-latest
82
+ steps:
83
+ - name: Checkout Code
84
+ uses: actions/checkout@v4
85
+ with:
86
+ fetch-depth: 0 # Required to scan history and base ref diffs
87
+
88
+ - name: Run Class1 Guard
89
+ uses: class1-dev/class1@v1
90
+ with:
91
+ license_key: ${{ secrets.CLASS1_LICENSE_KEY }}
92
+ github_token: ${{ secrets.GITHUB_TOKEN }}
93
+ ```
94
+
95
+ ### 3. Add your License Key (Premium features)
96
+ To unlock blocking budget gates, custom price overrides, and data persistence for post-merge calibration, get a license key from [https://class1.dev](https://class1.dev) and add it as a Repository Secret named `CLASS1_LICENSE_KEY`.
97
+
98
+ *Class1 remains completely **free and advisory** (posting comments without blocking) for open-source repositories and individual developers.*
99
+
100
+ ---
101
+
102
+ ## Architecture & Subsystems (For Developers)
103
+
104
+ If you are developing or hosting Class1 yourself, the project is structured as follows:
105
+
106
+ 1. **`cost_engine/`** — Pure cost-risk math (no GitHub/UI/DB).
107
+ - Monte Carlo simulation with systemic risk factors (`monte_carlo.py`, `distributions.py`).
108
+ - Forward escalation forecast decomposed into price, volume, and structure (`escalation.py`).
109
+ - Contingency and sensitivity tornado analysis (`contingency.py`).
110
+ - Estimate classification based on AACE standards (`classification.py`).
111
+ - Ecological footprint delta tracking (carbon, water, materials) (`footprint/`).
112
+ 2. **`blue_book/`** — The historical ledger database.
113
+ - Per-provider token usage normalization and deduplicated cost calculation.
114
+ - Spec sheet and historical capability indexing (`model_capability`).
115
+ - MEDALLION pipeline (`bronze -> silver -> gold`) for ingestion.
116
+ 3. **`takeoff/`** — The product integration surface.
117
+ - Python stdlib AST diff scanner and TS/JS tree-sitter scanner.
118
+ - Translation layers to transform code changes into Scenario models.
119
+ - CLI tool (`python -m takeoff.estimate_pr`) and GitHub Action harness.
120
+
121
+ ### Local Development Commands
122
+ Run tests constantly using the project's Makefile wrapper:
123
+ ```bash
124
+ make test # Run full test suite (~870 tests, 2.5s)
125
+ make demo # Execute end-to-end local PR estimation demo
126
+ make up # Spin up local throwaway Postgres instance (docker-free)
127
+ make down # Tear down local Postgres instance
128
+ ```
129
+
130
+ ---
131
+
132
+ ## License
133
+
134
+ Class1 is licensed under the Business Source License 1.1 (BSL) — see [LICENSE](file:///Users/owner/Desktop/abc7d/LICENSE) for details. The project relies on price and capability dataset snapshots under their respective open-source licenses.
135
+
class1-0.1.0/README.md ADDED
@@ -0,0 +1,119 @@
1
+ # Class1 (Internal name: abc7d) — Project controls and cost-risk estimation for AI systems
2
+
3
+ Class1 is a high-performance cost-engineering tool for LLM-based systems. Unlike typical observability tools that look backward at historical spend, Class1 focuses on **estimation**: providing a risk-adjusted forecast of a code change's monthly cost delta **in the pull request, before merge**, with a declared AACE estimate class and a calibration loop.
4
+
5
+ Cost engineering, not dashboards.
6
+
7
+ ---
8
+
9
+ ## How it works
10
+
11
+ When a developer submits a PR, the Class1 GitHub Action:
12
+ 1. **Scans the diff** to identify added, modified, or removed LLM callsites (Python AST + TS/JS tree-sitter).
13
+ 2. **Translates code changes** (model swaps, token changes, tool changes) into baseline vs. head scenarios.
14
+ 3. **Runs a Monte Carlo simulation** using Common Random Numbers (CRN) to estimate the monthly cost delta.
15
+ 4. **Applies a budget policy** to automatically warn or block the PR if the P90 tail risk exceeds your threshold.
16
+ 5. **Posts a detailed cost-risk report** directly to the PR comments.
17
+
18
+ ```
19
+ ABC7D · AI cost-risk report
20
+ =========================================
21
+ Expected Monthly Delta: +$124.50
22
+ P50 (Median) Delta: +$112.00
23
+ P90 (Tail Risk) Delta: +$210.00
24
+ P95 Delta: +$245.00
25
+
26
+ Estimate Class: Class 4 (Study)
27
+ Accuracy Range: -30% / +50%
28
+
29
+ Recommendation:
30
+ Optimal model selected for the workload. Under-spec models (e.g. gpt-4o-mini)
31
+ would result in lower unit cost but higher overall cost due to retries (+14%).
32
+
33
+ Verdict: PASS
34
+ ```
35
+
36
+ ---
37
+
38
+ ## Getting Started: GitHub Action Setup
39
+
40
+ Add the Class1 check to your repository in three simple steps:
41
+
42
+ ### 1. Create a budget policy
43
+ Add a `.class1.json` file at the root of your repository:
44
+ ```json
45
+ {
46
+ "fail_pr_if": {
47
+ "delta_p90_usd": 500.0,
48
+ "warn_at_fraction": 0.8
49
+ }
50
+ }
51
+ ```
52
+ *If a PR's estimated monthly P90 cost increase exceeds $500, the GitHub status check will fail, blocking the merge.*
53
+
54
+ ### 2. Configure the GitHub Actions workflow
55
+ Create `.github/workflows/class1.yml`:
56
+ ```yaml
57
+ name: Class1 Cost-Risk Guard
58
+
59
+ on:
60
+ pull_request:
61
+ branches: [ main ]
62
+
63
+ jobs:
64
+ estimate:
65
+ runs-on: ubuntu-latest
66
+ steps:
67
+ - name: Checkout Code
68
+ uses: actions/checkout@v4
69
+ with:
70
+ fetch-depth: 0 # Required to scan history and base ref diffs
71
+
72
+ - name: Run Class1 Guard
73
+ uses: class1-dev/class1@v1
74
+ with:
75
+ license_key: ${{ secrets.CLASS1_LICENSE_KEY }}
76
+ github_token: ${{ secrets.GITHUB_TOKEN }}
77
+ ```
78
+
79
+ ### 3. Add your License Key (Premium features)
80
+ To unlock blocking budget gates, custom price overrides, and data persistence for post-merge calibration, get a license key from [https://class1.dev](https://class1.dev) and add it as a Repository Secret named `CLASS1_LICENSE_KEY`.
81
+
82
+ *Class1 remains completely **free and advisory** (posting comments without blocking) for open-source repositories and individual developers.*
83
+
84
+ ---
85
+
86
+ ## Architecture & Subsystems (For Developers)
87
+
88
+ If you are developing or hosting Class1 yourself, the project is structured as follows:
89
+
90
+ 1. **`cost_engine/`** — Pure cost-risk math (no GitHub/UI/DB).
91
+ - Monte Carlo simulation with systemic risk factors (`monte_carlo.py`, `distributions.py`).
92
+ - Forward escalation forecast decomposed into price, volume, and structure (`escalation.py`).
93
+ - Contingency and sensitivity tornado analysis (`contingency.py`).
94
+ - Estimate classification based on AACE standards (`classification.py`).
95
+ - Ecological footprint delta tracking (carbon, water, materials) (`footprint/`).
96
+ 2. **`blue_book/`** — The historical ledger database.
97
+ - Per-provider token usage normalization and deduplicated cost calculation.
98
+ - Spec sheet and historical capability indexing (`model_capability`).
99
+ - MEDALLION pipeline (`bronze -> silver -> gold`) for ingestion.
100
+ 3. **`takeoff/`** — The product integration surface.
101
+ - Python stdlib AST diff scanner and TS/JS tree-sitter scanner.
102
+ - Translation layers to transform code changes into Scenario models.
103
+ - CLI tool (`python -m takeoff.estimate_pr`) and GitHub Action harness.
104
+
105
+ ### Local Development Commands
106
+ Run tests constantly using the project's Makefile wrapper:
107
+ ```bash
108
+ make test # Run full test suite (~870 tests, 2.5s)
109
+ make demo # Execute end-to-end local PR estimation demo
110
+ make up # Spin up local throwaway Postgres instance (docker-free)
111
+ make down # Tear down local Postgres instance
112
+ ```
113
+
114
+ ---
115
+
116
+ ## License
117
+
118
+ Class1 is licensed under the Business Source License 1.1 (BSL) — see [LICENSE](file:///Users/owner/Desktop/abc7d/LICENSE) for details. The project relies on price and capability dataset snapshots under their respective open-source licenses.
119
+
@@ -0,0 +1,126 @@
1
+ """FinOps actuals — FOCUS-IN + close the calibration loop.
2
+
3
+ The FinOps-canonical source of ACTUAL spend is a FOCUS dataset (FinOps Open Cost & Usage
4
+ Specification): a cloud/billing export, a FinOps platform (Vantage / CloudZero / Finout) export,
5
+ or the provider usage/costs API mapped to FOCUS. blue_book already EXPORTS FOCUS (focus.py); this is
6
+ the inverse — read a FOCUS dataset, aggregate the FinOps `EffectiveCost` per workload/month into the
7
+ monthly ACTUAL, and feed it (paired with the prior ESTIMATE) into the ActuarialTable so the estimate
8
+ class rises from 5 (a guess) toward validated. Pure/offline: the REAL FOCUS rows are the user's data.
9
+ """
10
+ from __future__ import annotations
11
+
12
+ import csv
13
+ from pathlib import Path
14
+
15
+ from cost_engine.calibration import ActuarialTable # NOTE: layer violation — blue_book imports cost_engine. Tracked as tech debt.
16
+
17
+
18
+ def read_focus_csv(path: str | Path) -> list[dict]:
19
+ """Read a FOCUS-format CSV (as exported by focus.export_to_csv or any FinOps tool / cloud billing).
20
+ Uses utf-8-SIG: real exports (verified on Microsoft's Azure EA FOCUS sample) carry a UTF-8 BOM that
21
+ would otherwise corrupt the first column name (\\ufeffBilledCost)."""
22
+ with Path(path).open(newline="", encoding="utf-8-sig") as f:
23
+ return list(csv.DictReader(f))
24
+
25
+
26
+ def monthly_actual(focus_rows: list[dict], workflow: str | None = None, month: str | None = None,
27
+ cost_col: str = "EffectiveCost", tag_col: str = "x_workflow_name") -> float:
28
+ """The ACTUAL monthly spend = sum of FinOps EffectiveCost over FOCUS rows. Optionally scope to a
29
+ workload (the `tag_col` column == `workflow` — x_workflow_name for our LLM exports, or ServiceName/
30
+ ResourceId for cloud FOCUS) and/or a month (YYYY-MM prefix of ChargePeriodStart). EffectiveCost is
31
+ the post-discount FinOps cost — the right number to validate an estimate against."""
32
+ total = 0.0
33
+ for r in focus_rows:
34
+ if workflow is not None and (r.get(tag_col) or "") != workflow:
35
+ continue
36
+ if month is not None and not str(r.get("ChargePeriodStart", "")).startswith(month):
37
+ continue
38
+ try:
39
+ total += float(r.get(cost_col) or 0.0)
40
+ except (TypeError, ValueError):
41
+ continue
42
+ return total
43
+
44
+
45
+ def record_actual(table: ActuarialTable, workflow: str, estimate: dict, focus_rows: list[dict], *,
46
+ month: str | None = None, dominant_driver: str = "output length",
47
+ tag_col: str = "x_workflow_name") -> float:
48
+ """Close the loop: pair the prior ESTIMATE for `workflow` with its ACTUAL spend (from the FOCUS
49
+ dataset) -> ActuarialTable. n_actuals rises -> estimate_class rises (the flywheel). Returns the
50
+ actual. This is the one step that turns a Class-5 guess into a validated estimate."""
51
+ actual = monthly_actual(focus_rows, workflow=workflow, month=month, tag_col=tag_col)
52
+ table.add(workflow, estimate, actual, dominant_driver)
53
+ return actual
54
+
55
+
56
+ def variance_waterfall(estimate: dict, actual: float, driver_elasticities: dict[str, float] | None = None) -> dict[str, float]:
57
+ """Isolate the cost variance (Actual - Expected) into a waterfall of risk drivers.
58
+
59
+ If actual exceeds expected, the variance is distributed proportionally
60
+ across the drivers based on their simulated elasticities. This bridges
61
+ the gap between 'we missed the budget' and 'here is exactly why'.
62
+ """
63
+ driver_elasticities = driver_elasticities or {}
64
+ expected = float(estimate.get("expected", 0.0))
65
+ total_variance = actual - expected
66
+
67
+ waterfall = {}
68
+ if total_variance == 0 or not driver_elasticities:
69
+ waterfall["unexplained"] = total_variance
70
+ return waterfall
71
+
72
+ total_elasticity = sum(driver_elasticities.values())
73
+ if total_elasticity == 0:
74
+ waterfall["unexplained"] = total_variance
75
+ return waterfall
76
+
77
+ explained = 0.0
78
+ for driver, elasticity in driver_elasticities.items():
79
+ # Using abs(elasticity) in case negative correlation exists but we allocate magnitude
80
+ weight = abs(elasticity) / sum(abs(v) for v in driver_elasticities.values())
81
+ impact = total_variance * weight
82
+ waterfall[driver] = impact
83
+ explained += impact
84
+
85
+ remainder = total_variance - explained
86
+ if abs(remainder) > 0.01:
87
+ waterfall["remainder"] = remainder
88
+
89
+ return waterfall
90
+
91
+ def _main(argv=None) -> int:
92
+ """CLI: record a real ACTUAL (from a FOCUS export) against a stored ESTIMATE -> persist the loop.
93
+
94
+ python -m blue_book.actuals --focus bill.csv --workflow support_agent \\
95
+ --estimate '{"expected":18,"p50":16,"p90":30}' [--month 2026-06]
96
+ """
97
+ import argparse
98
+ import json
99
+
100
+ from cost_engine.calibration import ActuarialTable, load_table, save_table # NOTE: layer violation — blue_book imports cost_engine. Tracked as tech debt.
101
+
102
+ ap = argparse.ArgumentParser(description="Close the calibration loop: a FOCUS actual vs a prior estimate.")
103
+ ap.add_argument("--focus", required=True, help="FOCUS CSV (FinOps export / cloud billing / provider-usage->FOCUS)")
104
+ ap.add_argument("--workflow", required=True, help="x_workflow_name to scope the actual to")
105
+ ap.add_argument("--estimate", required=True, help="JSON (file path or inline) with expected/p50/p90")
106
+ ap.add_argument("--month", default=None, help="YYYY-MM to scope the actual (default: all)")
107
+ ap.add_argument("--table", default="snapshots/actuarial_table.json", help="persisted ActuarialTable")
108
+ ap.add_argument("--driver", default="output length")
109
+ ap.add_argument("--scope-col", default="x_workflow_name",
110
+ help="FOCUS column to scope by (x_workflow_name for LLM; ServiceName/ResourceId for cloud)")
111
+ a = ap.parse_args(argv)
112
+
113
+ est = json.loads(Path(a.estimate).read_text()) if Path(a.estimate).exists() else json.loads(a.estimate)
114
+ table = load_table(a.table) if Path(a.table).exists() else ActuarialTable()
115
+ actual = record_actual(table, a.workflow, est, read_focus_csv(a.focus),
116
+ month=a.month, dominant_driver=a.driver, tag_col=a.scope_col)
117
+ save_table(table, a.table)
118
+ cls = table.estimate_class(a.workflow)
119
+ print(f"actual ${actual:,.2f} recorded for '{a.workflow}' (month={a.month or 'all'}) -> "
120
+ f"n_actuals={table.n_actuals(a.workflow)}, class={cls.label}, "
121
+ f"verdict={table._scoped(a.workflow)[-1].verdict}")
122
+ return 0
123
+
124
+
125
+ if __name__ == "__main__":
126
+ raise SystemExit(_main())