kilowattchiaro-engine 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 (56) hide show
  1. kilowattchiaro_engine-0.1.0/.github/workflows/ci.yml +15 -0
  2. kilowattchiaro_engine-0.1.0/.github/workflows/release.yml +23 -0
  3. kilowattchiaro_engine-0.1.0/.gitignore +6 -0
  4. kilowattchiaro_engine-0.1.0/.kwc-export +1 -0
  5. kilowattchiaro_engine-0.1.0/LICENSE.md +34 -0
  6. kilowattchiaro_engine-0.1.0/PKG-INFO +221 -0
  7. kilowattchiaro_engine-0.1.0/README.md +166 -0
  8. kilowattchiaro_engine-0.1.0/docs/architecture.md +79 -0
  9. kilowattchiaro_engine-0.1.0/docs/deck.html +773 -0
  10. kilowattchiaro_engine-0.1.0/docs/deck.pdf +0 -0
  11. kilowattchiaro_engine-0.1.0/docs/diagrams/delivery-pipeline.svg +78 -0
  12. kilowattchiaro_engine-0.1.0/docs/diagrams/runtime-architecture.svg +66 -0
  13. kilowattchiaro_engine-0.1.0/docs/methodology.md +807 -0
  14. kilowattchiaro_engine-0.1.0/docs/quote-engine.md +95 -0
  15. kilowattchiaro_engine-0.1.0/pyproject.toml +37 -0
  16. kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/__init__.py +10 -0
  17. kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/config.py +29 -0
  18. kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/historical_backtest.py +722 -0
  19. kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/hourly_match.py +135 -0
  20. kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/household_estimation.py +168 -0
  21. kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/incentive_regimes.py +257 -0
  22. kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/load_profiles/__init__.py +16 -0
  23. kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/load_profiles/archetypes.py +335 -0
  24. kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/load_profiles/infer.py +247 -0
  25. kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/models/__init__.py +1 -0
  26. kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/models/contract.py +121 -0
  27. kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/models/hourly.py +172 -0
  28. kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/models/price_components.py +372 -0
  29. kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/models/solar_eval.py +540 -0
  30. kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/price_projection.py +309 -0
  31. kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/pvgis.py +18 -0
  32. kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/solar_eval/__init__.py +78 -0
  33. kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/solar_eval/_constants.py +23 -0
  34. kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/solar_eval/cash_flow.py +171 -0
  35. kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/solar_eval/models.py +37 -0
  36. kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/solar_eval/npv.py +97 -0
  37. kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/solar_eval/orchestrator.py +396 -0
  38. kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/solar_eval/payback.py +39 -0
  39. kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/solar_eval/scenario.py +138 -0
  40. kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/tariff.py +120 -0
  41. kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/validation.py +230 -0
  42. kilowattchiaro_engine-0.1.0/tests/conftest.py +1 -0
  43. kilowattchiaro_engine-0.1.0/tests/mock_pvgis.py +179 -0
  44. kilowattchiaro_engine-0.1.0/tests/test_engine_stress.py +415 -0
  45. kilowattchiaro_engine-0.1.0/tests/test_historical_backtest.py +385 -0
  46. kilowattchiaro_engine-0.1.0/tests/test_hourly_match.py +244 -0
  47. kilowattchiaro_engine-0.1.0/tests/test_household_estimation.py +169 -0
  48. kilowattchiaro_engine-0.1.0/tests/test_incentive_regimes.py +292 -0
  49. kilowattchiaro_engine-0.1.0/tests/test_load_inference.py +277 -0
  50. kilowattchiaro_engine-0.1.0/tests/test_load_profiles.py +144 -0
  51. kilowattchiaro_engine-0.1.0/tests/test_price_projection.py +324 -0
  52. kilowattchiaro_engine-0.1.0/tests/test_published_deck.py +95 -0
  53. kilowattchiaro_engine-0.1.0/tests/test_solar_eval.py +813 -0
  54. kilowattchiaro_engine-0.1.0/tests/test_tariff.py +132 -0
  55. kilowattchiaro_engine-0.1.0/tests/test_validation.py +209 -0
  56. kilowattchiaro_engine-0.1.0/uv.lock +292 -0
@@ -0,0 +1,15 @@
1
+ name: ci
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ jobs:
8
+ test:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - uses: actions/checkout@v4
12
+ - uses: astral-sh/setup-uv@v5
13
+ - run: uv venv --python 3.12
14
+ - run: uv pip install -e '.[dev]'
15
+ - run: uv run pytest -q
@@ -0,0 +1,23 @@
1
+ name: release
2
+
3
+ # Publishes to PyPI via Trusted Publishing (OIDC) — no tokens stored anywhere.
4
+ # One-time setup on pypi.org: add a trusted publisher for project
5
+ # kilowattchiaro-engine -> repo domenicodigangi/kilowattchiaro, workflow
6
+ # release.yml, environment pypi.
7
+
8
+ on:
9
+ push:
10
+ tags: ["v*"]
11
+
12
+ jobs:
13
+ publish:
14
+ runs-on: ubuntu-latest
15
+ environment: pypi
16
+ permissions:
17
+ id-token: write
18
+ contents: read
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+ - uses: astral-sh/setup-uv@v5
22
+ - run: uv build
23
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,6 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .venv/
4
+ dist/
5
+ *.egg-info/
6
+ .pytest_cache/
@@ -0,0 +1 @@
1
+ created by build-public-repo.py — safe to regenerate
@@ -0,0 +1,34 @@
1
+ # KiloWattChiaro Source-Available License
2
+
3
+ **SPDX-License-Identifier: LicenseRef-KWC-Source-Available**
4
+
5
+ Copyright (c) 2026 Domenico Di Gangi. All rights reserved.
6
+
7
+ This source code is made public so that anyone can **read, run, and
8
+ verify** the methodology behind the KiloWattChiaro solar evaluation —
9
+ transparency is the product's core promise. It is *source-available*,
10
+ not open source.
11
+
12
+ **You may:**
13
+
14
+ 1. View, download, and run the code for personal, educational, or
15
+ evaluation purposes.
16
+ 2. Verify, reproduce, and publicly discuss the results and methodology,
17
+ with attribution.
18
+ 3. Open issues and submit patches (contributions are licensed to the
19
+ copyright holder under these same terms).
20
+
21
+ **You may not, without prior written permission:**
22
+
23
+ 1. Use the code, or substantial portions of it, in a commercial product
24
+ or service.
25
+ 2. Redistribute, republish, or offer the code (modified or not) as a
26
+ library, package, or service of your own.
27
+ 3. Remove or alter attribution or this license.
28
+
29
+ The software is provided "as is", without warranty of any kind, express
30
+ or implied. No liability is accepted for any use of it — solar
31
+ investment decisions remain your own.
32
+
33
+ For commercial licensing or any permission requests:
34
+ **kilowattchiaro@gmail.com**.
@@ -0,0 +1,221 @@
1
+ Metadata-Version: 2.5
2
+ Name: kilowattchiaro-engine
3
+ Version: 0.1.0
4
+ Summary: The deterministic solar-evaluation engine behind kilowattchiaro.it — NPV, IRR, payback, scenarios and historical backtests for Italian residential solar.
5
+ Project-URL: Homepage, https://kilowattchiaro.it
6
+ Project-URL: Repository, https://github.com/domenicodigangi/kilowattchiaro
7
+ Project-URL: Methodology, https://github.com/domenicodigangi/kilowattchiaro/blob/master/docs/methodology.md
8
+ Author: Domenico Di Gangi
9
+ License: # KiloWattChiaro Source-Available License
10
+
11
+ **SPDX-License-Identifier: LicenseRef-KWC-Source-Available**
12
+
13
+ Copyright (c) 2026 Domenico Di Gangi. All rights reserved.
14
+
15
+ This source code is made public so that anyone can **read, run, and
16
+ verify** the methodology behind the KiloWattChiaro solar evaluation —
17
+ transparency is the product's core promise. It is *source-available*,
18
+ not open source.
19
+
20
+ **You may:**
21
+
22
+ 1. View, download, and run the code for personal, educational, or
23
+ evaluation purposes.
24
+ 2. Verify, reproduce, and publicly discuss the results and methodology,
25
+ with attribution.
26
+ 3. Open issues and submit patches (contributions are licensed to the
27
+ copyright holder under these same terms).
28
+
29
+ **You may not, without prior written permission:**
30
+
31
+ 1. Use the code, or substantial portions of it, in a commercial product
32
+ or service.
33
+ 2. Redistribute, republish, or offer the code (modified or not) as a
34
+ library, package, or service of your own.
35
+ 3. Remove or alter attribution or this license.
36
+
37
+ The software is provided "as is", without warranty of any kind, express
38
+ or implied. No liability is accepted for any use of it — solar
39
+ investment decisions remain your own.
40
+
41
+ For commercial licensing or any permission requests:
42
+ **kilowattchiaro@gmail.com**.
43
+ License-File: LICENSE.md
44
+ Keywords: energy,irr,italy,npv,payback,photovoltaic,solar
45
+ Classifier: Intended Audience :: Science/Research
46
+ Classifier: Operating System :: OS Independent
47
+ Classifier: Programming Language :: Python :: 3
48
+ Classifier: Topic :: Scientific/Engineering
49
+ Requires-Python: >=3.11
50
+ Requires-Dist: pydantic-settings>=2.2
51
+ Requires-Dist: pydantic[email]>=2.7
52
+ Provides-Extra: dev
53
+ Requires-Dist: pytest>=8; extra == 'dev'
54
+ Description-Content-Type: text/markdown
55
+
56
+ # KiloWattChiaro
57
+
58
+ > **The independent check on the solar purchase.**
59
+ > Live and free at **[kilowattchiaro.it](https://kilowattchiaro.it)**.
60
+
61
+ [![ci](https://github.com/domenicodigangi/kilowattchiaro/actions/workflows/ci.yml/badge.svg)](https://github.com/domenicodigangi/kilowattchiaro/actions/workflows/ci.yml)
62
+ [![PyPI](https://img.shields.io/pypi/v/kilowattchiaro-engine)](https://pypi.org/project/kilowattchiaro-engine/)
63
+ [![License: Source-Available](https://img.shields.io/badge/license-source--available-8a6d1a)](LICENSE.md)
64
+
65
+ An Italian homeowner considering rooftop solar has two questions nobody
66
+ neutral will answer: **is solar worth it for my house?** and **is this
67
+ quote fair?** The market's default path runs through lead aggregators
68
+ that resell your phone number to competing installers — everyone you can
69
+ ask gets paid if you say yes.
70
+
71
+ KiloWattChiaro is the neutral answer: a **deterministic evaluation
72
+ engine** with a fully published methodology, an **agentic quote
73
+ reviewer** that must back every technical claim with a manufacturer
74
+ datasheet citation, and a hard architectural wall between the check and
75
+ the sale — a *"solar is not worth it for your roof"* verdict is a
76
+ first-class result, presented exactly like a yes.
77
+
78
+ ## What's in this repository
79
+
80
+ This repo publishes the part of the product whose entire value is being
81
+ inspectable — the code that computes every number a homeowner sees:
82
+
83
+ | Public — this repo | Private — the product |
84
+ |---|---|
85
+ | `src/kilowattchiaro_engine/` — the deterministic evaluation engine, **exactly as deployed in production** (NPV, IRR, payback, hourly self-consumption, price scenarios, historical backtests) | Web app, HTTP service layer, persistence |
86
+ | `tests/` — 325 tests: unit, stress, validation, and backtests across the 2011–2025 Italian incentive regimes | Booking service (separate system, no AI in it) |
87
+ | [`docs/methodology.md`](docs/methodology.md) — the full methodology: every formula and every assumption, with values | Quote-agent runtime — in production at [kilowattchiaro.it/preventivo](https://kilowattchiaro.it/preventivo) |
88
+ | [`docs/architecture.md`](docs/architecture.md) — system design, runtime + delivery diagrams | Component catalogue & price-benchmark data ([open rubric CSV](https://kilowattchiaro.it/open-data) is CC BY 4.0) |
89
+ | [`docs/quote-engine.md`](docs/quote-engine.md) — design of the quote-fairness engine (description + skeleton, deliberately not runnable) | Infrastructure (IaC, deploy, secrets) |
90
+ | [`docs/deck.pdf`](docs/deck.pdf) — the slides from the Demo Day video, appendices included | CRM and installer-partner data |
91
+
92
+ ## The pitch
93
+
94
+ - **[`docs/deck.pdf`](docs/deck.pdf)** — 15 pages: the twelve slides from
95
+ the video, plus three appendices that are not spoken in it (why a
96
+ general-purpose chatbot cannot do this, how the citation gate works, and
97
+ the push-to-production delivery pipeline).
98
+ - **[`docs/deck.html`](docs/deck.html)** — the same deck, interactive and
99
+ self-contained: download the raw file and open it in a browser. Arrow
100
+ keys navigate, `End` jumps to the last spoken slide, and the three
101
+ appendices follow it.
102
+
103
+ Both are generated, script-free copies of the presenter deck: the speaker
104
+ notes and the narration are stripped at build time, and
105
+ [`tests/test_published_deck.py`](tests/test_published_deck.py) fails the
106
+ build if a copy still carrying them is ever committed here.
107
+
108
+ ## Install
109
+
110
+ ```bash
111
+ pip install kilowattchiaro-engine
112
+ # or, from source:
113
+ pip install git+https://github.com/domenicodigangi/kilowattchiaro
114
+ ```
115
+
116
+ Python ≥ 3.11. Dependencies: `pydantic`, `pydantic-settings` — nothing else.
117
+
118
+ ## Quickstart
119
+
120
+ Evaluate a typical Roman household (3,000 kWh/year) putting 6 kWp on the
121
+ roof, with a 10 kWh battery:
122
+
123
+ ```python
124
+ from kilowattchiaro_engine import evaluate_solar
125
+ from kilowattchiaro_engine.models.solar_eval import ConsumptionProfile, PVGISMonthly
126
+
127
+ consumption = ConsumptionProfile(
128
+ annual_kwh=3000, f1_kwh=1200, f2_kwh=1050, f3_kwh=750, annual_cost_eur=750.0,
129
+ )
130
+
131
+ # Monthly production for 6 kWp in Rome (kWh) — normally fetched from PVGIS
132
+ production = [
133
+ PVGISMonthly(month=m, e_m=e, h_m=h)
134
+ for m, e, h in [
135
+ (1, 450, 3.0), (2, 520, 3.5), (3, 720, 4.5), (4, 830, 5.2),
136
+ (5, 950, 6.0), (6, 1000, 6.5), (7, 1050, 6.8), (8, 980, 6.3),
137
+ (9, 780, 5.0), (10, 600, 4.0), (11, 420, 3.0), (12, 380, 2.5),
138
+ ]
139
+ ]
140
+
141
+ result = evaluate_solar(
142
+ consumption=consumption,
143
+ monthly_production=production,
144
+ desired_kwp=6.0,
145
+ include_battery=True,
146
+ battery_kwh=10.0,
147
+ )
148
+
149
+ for s in result.scenarios:
150
+ print(f"{s.label:22} NPV 20y: {s.npv_20yr_eur or 0:>9,.0f} € "
151
+ f"payback: {s.payback_years or float('inf'):>5.1f} y "
152
+ f"annual savings: {s.annual_savings_eur:>7,.0f} €")
153
+ ```
154
+
155
+ Every assumption behind those numbers is a named, overridable setting
156
+ (`KWC_ENGINE_*` environment variables — see
157
+ [`src/kilowattchiaro_engine/config.py`](src/kilowattchiaro_engine/config.py))
158
+ and is documented with its value in the
159
+ [assumptions register](docs/methodology.md#12-registro-delle-assunzioni).
160
+
161
+ ## Why deterministic
162
+
163
+ Large language models do approximate arithmetic; money math shouldn't
164
+ be approximate. In KiloWattChiaro **a model runs in exactly one place**
165
+ — the quote agent that reads unstructured PDFs — and even there, every
166
+ number it states comes from this engine over MCP: same inputs, same
167
+ answer, and anyone can check the logic because the logic is this
168
+ repository.
169
+
170
+ ![Runtime architecture](docs/diagrams/runtime-architecture.svg)
171
+
172
+ The second invariant is drawn in the diagram: the engines that judge
173
+ your roof and your quote don't know the installers exist. The booking
174
+ service is a separate system with no AI in it; the only thing that
175
+ crosses the separation is the homeowner's own click. The check cannot
176
+ favor the sale.
177
+
178
+ More in [`docs/architecture.md`](docs/architecture.md) — including the
179
+ delivery pipeline (tests and an eval gate on every change, among them a
180
+ fault-injection eval that fails the build if a fabricated datasheet is
181
+ ever cited).
182
+
183
+ ## Tests
184
+
185
+ ```bash
186
+ pip install -e '.[dev]'
187
+ pytest
188
+ ```
189
+
190
+ 325 tests across the engine: unit tests for every module (tariff
191
+ classification, load-profile inference, hourly matching, price
192
+ projections, incentive regimes, household estimation), numerical stress
193
+ tests (zero/near-zero edge cases), validation against reference
194
+ outcomes, and historical backtests replaying every Italian incentive
195
+ regime from 2011 to 2025.
196
+
197
+ ## Related
198
+
199
+ - **[kilowattchiaro.it](https://kilowattchiaro.it)** — the live product:
200
+ solar evaluation (free, no account), quote check and booking (free, a
201
+ sign-up).
202
+ - **[Quote check](https://kilowattchiaro.it/preventivo)** — the agentic
203
+ quote reviewer in production (LangGraph, MCP, Qdrant, citation-gated
204
+ RAG, eval harness).
205
+ - **[Open data](https://kilowattchiaro.it/open-data)** — datasets under
206
+ CC BY 4.0, including the photovoltaic price rubric.
207
+
208
+ ## License
209
+
210
+ **Source-available** — read, run, and verify freely; commercial use or
211
+ redistribution requires written permission. See [LICENSE.md](LICENSE.md).
212
+ This is a published package, deliberately not open source: transparency
213
+ is the point, the product is the business.
214
+
215
+ ## Author
216
+
217
+ **Domenico Di Gangi** — solo founder, Pisa, Italy. KiloWattChiaro is
218
+ built and operated end-to-end by one person: product, engine, agent,
219
+ infrastructure.
220
+
221
+ Contact: **kilowattchiaro@gmail.com**
@@ -0,0 +1,166 @@
1
+ # KiloWattChiaro
2
+
3
+ > **The independent check on the solar purchase.**
4
+ > Live and free at **[kilowattchiaro.it](https://kilowattchiaro.it)**.
5
+
6
+ [![ci](https://github.com/domenicodigangi/kilowattchiaro/actions/workflows/ci.yml/badge.svg)](https://github.com/domenicodigangi/kilowattchiaro/actions/workflows/ci.yml)
7
+ [![PyPI](https://img.shields.io/pypi/v/kilowattchiaro-engine)](https://pypi.org/project/kilowattchiaro-engine/)
8
+ [![License: Source-Available](https://img.shields.io/badge/license-source--available-8a6d1a)](LICENSE.md)
9
+
10
+ An Italian homeowner considering rooftop solar has two questions nobody
11
+ neutral will answer: **is solar worth it for my house?** and **is this
12
+ quote fair?** The market's default path runs through lead aggregators
13
+ that resell your phone number to competing installers — everyone you can
14
+ ask gets paid if you say yes.
15
+
16
+ KiloWattChiaro is the neutral answer: a **deterministic evaluation
17
+ engine** with a fully published methodology, an **agentic quote
18
+ reviewer** that must back every technical claim with a manufacturer
19
+ datasheet citation, and a hard architectural wall between the check and
20
+ the sale — a *"solar is not worth it for your roof"* verdict is a
21
+ first-class result, presented exactly like a yes.
22
+
23
+ ## What's in this repository
24
+
25
+ This repo publishes the part of the product whose entire value is being
26
+ inspectable — the code that computes every number a homeowner sees:
27
+
28
+ | Public — this repo | Private — the product |
29
+ |---|---|
30
+ | `src/kilowattchiaro_engine/` — the deterministic evaluation engine, **exactly as deployed in production** (NPV, IRR, payback, hourly self-consumption, price scenarios, historical backtests) | Web app, HTTP service layer, persistence |
31
+ | `tests/` — 325 tests: unit, stress, validation, and backtests across the 2011–2025 Italian incentive regimes | Booking service (separate system, no AI in it) |
32
+ | [`docs/methodology.md`](docs/methodology.md) — the full methodology: every formula and every assumption, with values | Quote-agent runtime — in production at [kilowattchiaro.it/preventivo](https://kilowattchiaro.it/preventivo) |
33
+ | [`docs/architecture.md`](docs/architecture.md) — system design, runtime + delivery diagrams | Component catalogue & price-benchmark data ([open rubric CSV](https://kilowattchiaro.it/open-data) is CC BY 4.0) |
34
+ | [`docs/quote-engine.md`](docs/quote-engine.md) — design of the quote-fairness engine (description + skeleton, deliberately not runnable) | Infrastructure (IaC, deploy, secrets) |
35
+ | [`docs/deck.pdf`](docs/deck.pdf) — the slides from the Demo Day video, appendices included | CRM and installer-partner data |
36
+
37
+ ## The pitch
38
+
39
+ - **[`docs/deck.pdf`](docs/deck.pdf)** — 15 pages: the twelve slides from
40
+ the video, plus three appendices that are not spoken in it (why a
41
+ general-purpose chatbot cannot do this, how the citation gate works, and
42
+ the push-to-production delivery pipeline).
43
+ - **[`docs/deck.html`](docs/deck.html)** — the same deck, interactive and
44
+ self-contained: download the raw file and open it in a browser. Arrow
45
+ keys navigate, `End` jumps to the last spoken slide, and the three
46
+ appendices follow it.
47
+
48
+ Both are generated, script-free copies of the presenter deck: the speaker
49
+ notes and the narration are stripped at build time, and
50
+ [`tests/test_published_deck.py`](tests/test_published_deck.py) fails the
51
+ build if a copy still carrying them is ever committed here.
52
+
53
+ ## Install
54
+
55
+ ```bash
56
+ pip install kilowattchiaro-engine
57
+ # or, from source:
58
+ pip install git+https://github.com/domenicodigangi/kilowattchiaro
59
+ ```
60
+
61
+ Python ≥ 3.11. Dependencies: `pydantic`, `pydantic-settings` — nothing else.
62
+
63
+ ## Quickstart
64
+
65
+ Evaluate a typical Roman household (3,000 kWh/year) putting 6 kWp on the
66
+ roof, with a 10 kWh battery:
67
+
68
+ ```python
69
+ from kilowattchiaro_engine import evaluate_solar
70
+ from kilowattchiaro_engine.models.solar_eval import ConsumptionProfile, PVGISMonthly
71
+
72
+ consumption = ConsumptionProfile(
73
+ annual_kwh=3000, f1_kwh=1200, f2_kwh=1050, f3_kwh=750, annual_cost_eur=750.0,
74
+ )
75
+
76
+ # Monthly production for 6 kWp in Rome (kWh) — normally fetched from PVGIS
77
+ production = [
78
+ PVGISMonthly(month=m, e_m=e, h_m=h)
79
+ for m, e, h in [
80
+ (1, 450, 3.0), (2, 520, 3.5), (3, 720, 4.5), (4, 830, 5.2),
81
+ (5, 950, 6.0), (6, 1000, 6.5), (7, 1050, 6.8), (8, 980, 6.3),
82
+ (9, 780, 5.0), (10, 600, 4.0), (11, 420, 3.0), (12, 380, 2.5),
83
+ ]
84
+ ]
85
+
86
+ result = evaluate_solar(
87
+ consumption=consumption,
88
+ monthly_production=production,
89
+ desired_kwp=6.0,
90
+ include_battery=True,
91
+ battery_kwh=10.0,
92
+ )
93
+
94
+ for s in result.scenarios:
95
+ print(f"{s.label:22} NPV 20y: {s.npv_20yr_eur or 0:>9,.0f} € "
96
+ f"payback: {s.payback_years or float('inf'):>5.1f} y "
97
+ f"annual savings: {s.annual_savings_eur:>7,.0f} €")
98
+ ```
99
+
100
+ Every assumption behind those numbers is a named, overridable setting
101
+ (`KWC_ENGINE_*` environment variables — see
102
+ [`src/kilowattchiaro_engine/config.py`](src/kilowattchiaro_engine/config.py))
103
+ and is documented with its value in the
104
+ [assumptions register](docs/methodology.md#12-registro-delle-assunzioni).
105
+
106
+ ## Why deterministic
107
+
108
+ Large language models do approximate arithmetic; money math shouldn't
109
+ be approximate. In KiloWattChiaro **a model runs in exactly one place**
110
+ — the quote agent that reads unstructured PDFs — and even there, every
111
+ number it states comes from this engine over MCP: same inputs, same
112
+ answer, and anyone can check the logic because the logic is this
113
+ repository.
114
+
115
+ ![Runtime architecture](docs/diagrams/runtime-architecture.svg)
116
+
117
+ The second invariant is drawn in the diagram: the engines that judge
118
+ your roof and your quote don't know the installers exist. The booking
119
+ service is a separate system with no AI in it; the only thing that
120
+ crosses the separation is the homeowner's own click. The check cannot
121
+ favor the sale.
122
+
123
+ More in [`docs/architecture.md`](docs/architecture.md) — including the
124
+ delivery pipeline (tests and an eval gate on every change, among them a
125
+ fault-injection eval that fails the build if a fabricated datasheet is
126
+ ever cited).
127
+
128
+ ## Tests
129
+
130
+ ```bash
131
+ pip install -e '.[dev]'
132
+ pytest
133
+ ```
134
+
135
+ 325 tests across the engine: unit tests for every module (tariff
136
+ classification, load-profile inference, hourly matching, price
137
+ projections, incentive regimes, household estimation), numerical stress
138
+ tests (zero/near-zero edge cases), validation against reference
139
+ outcomes, and historical backtests replaying every Italian incentive
140
+ regime from 2011 to 2025.
141
+
142
+ ## Related
143
+
144
+ - **[kilowattchiaro.it](https://kilowattchiaro.it)** — the live product:
145
+ solar evaluation (free, no account), quote check and booking (free, a
146
+ sign-up).
147
+ - **[Quote check](https://kilowattchiaro.it/preventivo)** — the agentic
148
+ quote reviewer in production (LangGraph, MCP, Qdrant, citation-gated
149
+ RAG, eval harness).
150
+ - **[Open data](https://kilowattchiaro.it/open-data)** — datasets under
151
+ CC BY 4.0, including the photovoltaic price rubric.
152
+
153
+ ## License
154
+
155
+ **Source-available** — read, run, and verify freely; commercial use or
156
+ redistribution requires written permission. See [LICENSE.md](LICENSE.md).
157
+ This is a published package, deliberately not open source: transparency
158
+ is the point, the product is the business.
159
+
160
+ ## Author
161
+
162
+ **Domenico Di Gangi** — solo founder, Pisa, Italy. KiloWattChiaro is
163
+ built and operated end-to-end by one person: product, engine, agent,
164
+ infrastructure.
165
+
166
+ Contact: **kilowattchiaro@gmail.com**
@@ -0,0 +1,79 @@
1
+ # Architecture
2
+
3
+ How KiloWattChiaro runs in production, and how changes reach it. The two
4
+ diagrams below are the same ones used in the demo-day deck; every fact on
5
+ them is current as of August 2026.
6
+
7
+ ## Runtime — one VPS, one agent, one wall
8
+
9
+ ![Runtime architecture](diagrams/runtime-architecture.svg)
10
+
11
+ **The main box is a single European VPS** (Hetzner, Falkenstein), fronted
12
+ by Cloudflare and routed internally by Traefik, running three containers
13
+ under Docker Compose:
14
+
15
+ - **Next.js 14** (`:3000`) — the web app at
16
+ [kilowattchiaro.it](https://kilowattchiaro.it).
17
+ - **Solar API — FastAPI** (`:8000`) — the service layer around the
18
+ [deterministic engine published in this repository](../src/kilowattchiaro_engine/).
19
+ It also exposes an **MCP server** at `/api/mcp` (`solar_evaluate_v2`,
20
+ `quote_compare_v1`), so agents can call the engine as a tool instead of
21
+ doing arithmetic themselves.
22
+ - **Booking — FastAPI** (`:8010`) — a deliberately separate service with
23
+ **no LLM in it** and its own store (SQLite CRM). It verifies user
24
+ identity against Supabase read-only and never touches the evaluation
25
+ database.
26
+
27
+ **The quote agent** runs elsewhere on purpose — Fly.io Frankfurt
28
+ (`:7860`): OCR → strict structured extraction, component resolution
29
+ against a curated, datasheet-pinned catalogue (1,885 SKUs), Qdrant
30
+ retrieval over ~4,900 indexed datasheet chunks, a web-hunt fallback for
31
+ unknown components, and Redis for daily quotas and a short document
32
+ cache. Quote PDFs upload **directly from the browser to Fly** under a
33
+ five-minute HMAC grant — pseudonymous: Fly never receives the Supabase
34
+ identity, and the browser never sees the service credential.
35
+
36
+ **Supabase** (Frankfurt, AWS eu-central-1) holds Postgres and auth —
37
+ user profiles and evaluations.
38
+
39
+ ### The two invariants the diagram encodes
40
+
41
+ 1. **The model runs in exactly one box.** The quote agent is the only
42
+ place an LLM executes, and every number it states comes from the
43
+ deterministic engine over MCP — same inputs, same answer. The engine
44
+ itself has no model in it (you can read all of it in this repo).
45
+ 2. **The check cannot favor the sale.** The engines that judge a roof or
46
+ a quote don't know the installers exist. The booking service is a
47
+ separate system; the only thing that crosses the separation is the
48
+ homeowner's explicit click, carrying their evaluation to the one
49
+ installer they named. A negative verdict is a full result.
50
+
51
+ ## Delivery — from push to production
52
+
53
+ ![Delivery pipeline](diagrams/delivery-pipeline.svg)
54
+
55
+ Every push runs CI: 650+ tests across backend, frontend, agent, and E2E,
56
+ plus an **eval gate** on the agent (SKU-resolution and web-evidence
57
+ admission benchmarks; a fault-injection suite feeds the agent fabricated
58
+ datasheets and fails the build if fabricated content is ever cited).
59
+ Green CI auto-deploys to a **test environment reachable only over
60
+ Tailscale**, where a three-stage QA gate runs before a release candidate
61
+ is tagged. Production is a **manual promotion** of a tagged RC, followed
62
+ by an automatic smoke gate on the live URLs. Infrastructure is OpenTofu;
63
+ secrets are SOPS-encrypted in-repo.
64
+
65
+ ## Where this repository fits
66
+
67
+ ```
68
+ public private
69
+ ────────────────────────────────────── ─────────────────────────────────
70
+ this repo watt-chiaro monorepo
71
+ kilowattchiaro-engine (the math) ◄── service layer imports the engine
72
+ methodology, diagrams, docs web app · booking · agent runtime
73
+ catalogue data · infra · deploy
74
+ kilowattchiaro-agent (source snapshot)◄─ agent/ subtree, minus data
75
+ ```
76
+
77
+ The engine package is the exact code in production; the private service
78
+ layer contributes only I/O — HTTP connectors (PVGIS, Google Solar),
79
+ persistence, auth, and orchestration.