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.
- kilowattchiaro_engine-0.1.0/.github/workflows/ci.yml +15 -0
- kilowattchiaro_engine-0.1.0/.github/workflows/release.yml +23 -0
- kilowattchiaro_engine-0.1.0/.gitignore +6 -0
- kilowattchiaro_engine-0.1.0/.kwc-export +1 -0
- kilowattchiaro_engine-0.1.0/LICENSE.md +34 -0
- kilowattchiaro_engine-0.1.0/PKG-INFO +221 -0
- kilowattchiaro_engine-0.1.0/README.md +166 -0
- kilowattchiaro_engine-0.1.0/docs/architecture.md +79 -0
- kilowattchiaro_engine-0.1.0/docs/deck.html +773 -0
- kilowattchiaro_engine-0.1.0/docs/deck.pdf +0 -0
- kilowattchiaro_engine-0.1.0/docs/diagrams/delivery-pipeline.svg +78 -0
- kilowattchiaro_engine-0.1.0/docs/diagrams/runtime-architecture.svg +66 -0
- kilowattchiaro_engine-0.1.0/docs/methodology.md +807 -0
- kilowattchiaro_engine-0.1.0/docs/quote-engine.md +95 -0
- kilowattchiaro_engine-0.1.0/pyproject.toml +37 -0
- kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/__init__.py +10 -0
- kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/config.py +29 -0
- kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/historical_backtest.py +722 -0
- kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/hourly_match.py +135 -0
- kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/household_estimation.py +168 -0
- kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/incentive_regimes.py +257 -0
- kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/load_profiles/__init__.py +16 -0
- kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/load_profiles/archetypes.py +335 -0
- kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/load_profiles/infer.py +247 -0
- kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/models/__init__.py +1 -0
- kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/models/contract.py +121 -0
- kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/models/hourly.py +172 -0
- kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/models/price_components.py +372 -0
- kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/models/solar_eval.py +540 -0
- kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/price_projection.py +309 -0
- kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/pvgis.py +18 -0
- kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/solar_eval/__init__.py +78 -0
- kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/solar_eval/_constants.py +23 -0
- kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/solar_eval/cash_flow.py +171 -0
- kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/solar_eval/models.py +37 -0
- kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/solar_eval/npv.py +97 -0
- kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/solar_eval/orchestrator.py +396 -0
- kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/solar_eval/payback.py +39 -0
- kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/solar_eval/scenario.py +138 -0
- kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/tariff.py +120 -0
- kilowattchiaro_engine-0.1.0/src/kilowattchiaro_engine/validation.py +230 -0
- kilowattchiaro_engine-0.1.0/tests/conftest.py +1 -0
- kilowattchiaro_engine-0.1.0/tests/mock_pvgis.py +179 -0
- kilowattchiaro_engine-0.1.0/tests/test_engine_stress.py +415 -0
- kilowattchiaro_engine-0.1.0/tests/test_historical_backtest.py +385 -0
- kilowattchiaro_engine-0.1.0/tests/test_hourly_match.py +244 -0
- kilowattchiaro_engine-0.1.0/tests/test_household_estimation.py +169 -0
- kilowattchiaro_engine-0.1.0/tests/test_incentive_regimes.py +292 -0
- kilowattchiaro_engine-0.1.0/tests/test_load_inference.py +277 -0
- kilowattchiaro_engine-0.1.0/tests/test_load_profiles.py +144 -0
- kilowattchiaro_engine-0.1.0/tests/test_price_projection.py +324 -0
- kilowattchiaro_engine-0.1.0/tests/test_published_deck.py +95 -0
- kilowattchiaro_engine-0.1.0/tests/test_solar_eval.py +813 -0
- kilowattchiaro_engine-0.1.0/tests/test_tariff.py +132 -0
- kilowattchiaro_engine-0.1.0/tests/test_validation.py +209 -0
- 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 @@
|
|
|
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
|
+
[](https://github.com/domenicodigangi/kilowattchiaro/actions/workflows/ci.yml)
|
|
62
|
+
[](https://pypi.org/project/kilowattchiaro-engine/)
|
|
63
|
+
[](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
|
+

|
|
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
|
+
[](https://github.com/domenicodigangi/kilowattchiaro/actions/workflows/ci.yml)
|
|
7
|
+
[](https://pypi.org/project/kilowattchiaro-engine/)
|
|
8
|
+
[](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
|
+

|
|
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
|
+

|
|
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
|
+

|
|
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.
|