greenpick 0.4.0__tar.gz → 0.4.1__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 (54) hide show
  1. {greenpick-0.4.0/src/greenpick.egg-info → greenpick-0.4.1}/PKG-INFO +9 -8
  2. {greenpick-0.4.0 → greenpick-0.4.1}/README.md +8 -7
  3. {greenpick-0.4.0 → greenpick-0.4.1}/pyproject.toml +1 -1
  4. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/__init__.py +1 -1
  5. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/data/annual.json +3 -1
  6. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/data/regions.json +1 -1
  7. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/server.py +1 -1
  8. {greenpick-0.4.0 → greenpick-0.4.1/src/greenpick.egg-info}/PKG-INFO +9 -8
  9. {greenpick-0.4.0 → greenpick-0.4.1}/tests/test_services.py +16 -4
  10. {greenpick-0.4.0 → greenpick-0.4.1}/LICENSE +0 -0
  11. {greenpick-0.4.0 → greenpick-0.4.1}/setup.cfg +0 -0
  12. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/__main__.py +0 -0
  13. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/config.py +0 -0
  14. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/data/__init__.py +0 -0
  15. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/data/_net.py +0 -0
  16. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/data/auto_repository.py +0 -0
  17. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/data/canada_repository.py +0 -0
  18. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/data/eia_repository.py +0 -0
  19. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/data/electricitymaps_repository.py +0 -0
  20. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/data/emission_factors.json +0 -0
  21. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/data/entsoe_repository.py +0 -0
  22. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/data/gpu_power.json +0 -0
  23. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/data/inference_energy.json +0 -0
  24. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/data/instance_families.json +0 -0
  25. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/data/mock_repository.py +0 -0
  26. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/data/network.json +0 -0
  27. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/data/power.json +0 -0
  28. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/data/pricing.json +0 -0
  29. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/data/repository.py +0 -0
  30. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/data/static_data.py +0 -0
  31. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/data/typical.json +0 -0
  32. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/data/typical_repository.py +0 -0
  33. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/models.py +0 -0
  34. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/services/__init__.py +0 -0
  35. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/services/carbon_service.py +0 -0
  36. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/services/cost_service.py +0 -0
  37. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/services/data_hint.py +0 -0
  38. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/services/forecast_service.py +0 -0
  39. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/services/inference_service.py +0 -0
  40. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/services/instance_service.py +0 -0
  41. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/services/latency_service.py +0 -0
  42. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/services/locality_service.py +0 -0
  43. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/services/metric_scaffold_service.py +0 -0
  44. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/services/recommender.py +0 -0
  45. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/services/residency_service.py +0 -0
  46. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/services/rightsizing_service.py +0 -0
  47. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/services/scheduling_service.py +0 -0
  48. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/services/serving_service.py +0 -0
  49. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick/services/training_service.py +0 -0
  50. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick.egg-info/SOURCES.txt +0 -0
  51. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick.egg-info/dependency_links.txt +0 -0
  52. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick.egg-info/entry_points.txt +0 -0
  53. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick.egg-info/requires.txt +0 -0
  54. {greenpick-0.4.0 → greenpick-0.4.1}/src/greenpick.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: greenpick
3
- Version: 0.4.0
3
+ Version: 0.4.1
4
4
  Summary: Design-time sustainability co-pilot (MCP server): greener, cheaper cloud infrastructure decisions with carbon + cost + latency tradeoffs.
5
5
  Author-email: "Data Science Advisers, LLC" <info@datascienceadvisers.com>
6
6
  License-Expression: MIT
@@ -60,11 +60,12 @@ seconds. (It responds when you ask; auto-nudging on infra-code changes is on the
60
60
  > this month's average for each grid, from a year of real data.
61
61
 
62
62
  <p align="center">
63
- <img src="https://raw.githubusercontent.com/bithug01/greenpick/master/docs/img/region-carbon-30x.svg" alt="Same H100 training run, ~30× the carbon depending only on region (Stockholm vs Sydney)" width="560" />
63
+ <img src="https://raw.githubusercontent.com/bithug01/greenpick/master/docs/img/region-carbon-30x.svg" alt="Same H100 training run, ~28× the carbon depending only on region (Stockholm vs Sydney)" width="560" />
64
64
  </p>
65
65
 
66
- *The same 40-GPU-hour training run emits ~0.7 kg CO₂ in Stockholm vs ~19 kg in Sydney — a ~30×
67
- difference from region choice alone. Ask GreenPick and it surfaces that (with the cost and latency
66
+ *The same 40-GPU-hour training run emits ~0.6 kg CO₂ in Stockholm vs ~17.5 kg in Sydney — a ~28×
67
+ difference from region choice alone (typical October grids; Sydney on Ember's 2025 national figure,
68
+ a lower bound for its coal-heavier state grid). Ask GreenPick and it surfaces that (with the cost and latency
68
69
  tradeoff) while you're still in the editor, before you commit.*
69
70
 
70
71
  **▶ [See the 5-minute demo](docs/demo.md)** — three scenarios with real tool output (training placement, the egress backfire, the forecast).
@@ -195,12 +196,12 @@ See `docs/user-questions-backlog.md`.
195
196
  intensity and average hour-of-day profile (UTC), computed from 12 months of real hourly EIA-930 /
196
197
  ENTSO-E data (`data/typical.json`, refreshed by `scripts/refresh_typical.py`). Labelled
197
198
  `quality: "typical"` — a realistic norm, never presented as a current reading.
198
- - **Annual** — for Singapore, Tokyo and Mumbai/Pune, where no free hourly data exists: the country's
199
+ - **Annual** — for Singapore, Tokyo, Mumbai/Pune and Sydney, where no free hourly data exists: the country's
199
200
  yearly lifecycle grid intensity from [Ember](https://ember-energy.org/) (via Our World in Data,
200
201
  CC BY 4.0), with source and year (`data/annual.json`). Labelled `quality: "annual_average"`; fine for
201
202
  comparing regions, but carries no timing signal.
202
- - **Mock** — a hand-set illustrative value, labelled `quality: "mock"`, used only where nothing else
203
- exists (currently Sydney).
203
+ - **Mock** — a hand-set illustrative value, labelled `quality: "mock"`, kept only as a last resort if
204
+ every other source is missing. No catalog region relies on it.
204
205
  - Cost: a **dated static pricing table** (`data/pricing.json`), refreshed from live Vantage
205
206
  pricing via `scripts/refresh_pricing.py` using one matched method across all three clouds.
206
207
  - Instance-family efficiency: directional perf-per-watt multipliers (`data/instance_families.json`).
@@ -318,7 +319,7 @@ carbon, set two free keys (`EIA_API_KEY` for US, `ENTSOE_TOKEN` for EU) and
318
319
  `GREENPICK_CARBON_SOURCE=auto` — see [Turn on live data](#turn-on-live-data).
319
320
 
320
321
  **Why "directional" and not audited numbers?** Because it's for a *design-time decision*, not a
321
- compliance report. "Stockholm is ~30× cleaner than Sydney" is what you need to pick a region in
322
+ compliance report. "Stockholm is ~28× cleaner than Sydney" is what you need to pick a region in
322
323
  seconds — a certified figure is a different job for a different tool (Electricity Maps, WattTime).
323
324
  GreenPick is for *decisions*, not *disclosures*, and says so in every tool's limitations.
324
325
 
@@ -30,11 +30,12 @@ seconds. (It responds when you ask; auto-nudging on infra-code changes is on the
30
30
  > this month's average for each grid, from a year of real data.
31
31
 
32
32
  <p align="center">
33
- <img src="https://raw.githubusercontent.com/bithug01/greenpick/master/docs/img/region-carbon-30x.svg" alt="Same H100 training run, ~30× the carbon depending only on region (Stockholm vs Sydney)" width="560" />
33
+ <img src="https://raw.githubusercontent.com/bithug01/greenpick/master/docs/img/region-carbon-30x.svg" alt="Same H100 training run, ~28× the carbon depending only on region (Stockholm vs Sydney)" width="560" />
34
34
  </p>
35
35
 
36
- *The same 40-GPU-hour training run emits ~0.7 kg CO₂ in Stockholm vs ~19 kg in Sydney — a ~30×
37
- difference from region choice alone. Ask GreenPick and it surfaces that (with the cost and latency
36
+ *The same 40-GPU-hour training run emits ~0.6 kg CO₂ in Stockholm vs ~17.5 kg in Sydney — a ~28×
37
+ difference from region choice alone (typical October grids; Sydney on Ember's 2025 national figure,
38
+ a lower bound for its coal-heavier state grid). Ask GreenPick and it surfaces that (with the cost and latency
38
39
  tradeoff) while you're still in the editor, before you commit.*
39
40
 
40
41
  **▶ [See the 5-minute demo](docs/demo.md)** — three scenarios with real tool output (training placement, the egress backfire, the forecast).
@@ -165,12 +166,12 @@ See `docs/user-questions-backlog.md`.
165
166
  intensity and average hour-of-day profile (UTC), computed from 12 months of real hourly EIA-930 /
166
167
  ENTSO-E data (`data/typical.json`, refreshed by `scripts/refresh_typical.py`). Labelled
167
168
  `quality: "typical"` — a realistic norm, never presented as a current reading.
168
- - **Annual** — for Singapore, Tokyo and Mumbai/Pune, where no free hourly data exists: the country's
169
+ - **Annual** — for Singapore, Tokyo, Mumbai/Pune and Sydney, where no free hourly data exists: the country's
169
170
  yearly lifecycle grid intensity from [Ember](https://ember-energy.org/) (via Our World in Data,
170
171
  CC BY 4.0), with source and year (`data/annual.json`). Labelled `quality: "annual_average"`; fine for
171
172
  comparing regions, but carries no timing signal.
172
- - **Mock** — a hand-set illustrative value, labelled `quality: "mock"`, used only where nothing else
173
- exists (currently Sydney).
173
+ - **Mock** — a hand-set illustrative value, labelled `quality: "mock"`, kept only as a last resort if
174
+ every other source is missing. No catalog region relies on it.
174
175
  - Cost: a **dated static pricing table** (`data/pricing.json`), refreshed from live Vantage
175
176
  pricing via `scripts/refresh_pricing.py` using one matched method across all three clouds.
176
177
  - Instance-family efficiency: directional perf-per-watt multipliers (`data/instance_families.json`).
@@ -288,7 +289,7 @@ carbon, set two free keys (`EIA_API_KEY` for US, `ENTSOE_TOKEN` for EU) and
288
289
  `GREENPICK_CARBON_SOURCE=auto` — see [Turn on live data](#turn-on-live-data).
289
290
 
290
291
  **Why "directional" and not audited numbers?** Because it's for a *design-time decision*, not a
291
- compliance report. "Stockholm is ~30× cleaner than Sydney" is what you need to pick a region in
292
+ compliance report. "Stockholm is ~28× cleaner than Sydney" is what you need to pick a region in
292
293
  seconds — a certified figure is a different job for a different tool (Electricity Maps, WattTime).
293
294
  GreenPick is for *decisions*, not *disclosures*, and says so in every tool's limitations.
294
295
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "greenpick"
3
- version = "0.4.0"
3
+ version = "0.4.1"
4
4
  description = "Design-time sustainability co-pilot (MCP server): greener, cheaper cloud infrastructure decisions with carbon + cost + latency tradeoffs."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -4,7 +4,7 @@ Greener cloud-region choices with honest carbon + cost + latency tradeoffs,
4
4
  delivered inside the vibe-coding loop. Directional guidance, not audited accounting.
5
5
  """
6
6
 
7
- __version__ = "0.4.0"
7
+ __version__ = "0.4.1"
8
8
 
9
9
  # httpx logs every request URL at INFO, and our live sources pass their API key / token as a URL
10
10
  # query parameter — so at the default log level the secret would land in the MCP client's logs.
@@ -8,6 +8,8 @@
8
8
  "JP": { "country": "Japan", "g_co2_per_kwh": 477.3, "year": 2025,
9
9
  "note": "National average; the Tokyo (TEPCO) area mix differs somewhat and varies by hour." },
10
10
  "IN": { "country": "India", "g_co2_per_kwh": 670.1, "year": 2025,
11
- "note": "National average; Maharashtra (Mumbai, Pune) runs about 6% above it on Ember's state-level data." }
11
+ "note": "National average; Maharashtra (Mumbai, Pune) runs about 6% above it on Ember's state-level data." },
12
+ "AU": { "country": "Australia", "g_co2_per_kwh": 525.2, "year": 2025,
13
+ "note": "National average; New South Wales (Sydney) is more coal-heavy and runs higher, so treat this as a lower bound for Sydney." }
12
14
  }
13
15
  }
@@ -6,7 +6,7 @@
6
6
  "us-west-2": { "name": "Oregon", "lat": 45.87, "lon": -119.69, "zone": "US-NW-PACW", "eia_ba": "BPAT", "jurisdiction": "US" },
7
7
  "eu-west-1": { "name": "Ireland", "lat": 53.41, "lon": -8.24, "zone": "IE", "entsoe_eic": "10Y1001A1001A59C", "jurisdiction": "EEA" },
8
8
  "eu-north-1": { "name": "Stockholm", "lat": 59.33, "lon": 18.07, "zone": "SE", "entsoe_eic": "10YSE-1--------K", "jurisdiction": "EEA" },
9
- "ap-southeast-2": { "name": "Sydney", "lat": -33.87, "lon": 151.21, "zone": "AU-NSW", "jurisdiction": "AU" },
9
+ "ap-southeast-2": { "name": "Sydney", "lat": -33.87, "lon": 151.21, "zone": "AU-NSW", "annual_zone": "AU", "jurisdiction": "AU" },
10
10
  "ca-central-1": { "name": "Montreal", "lat": 45.50, "lon": -73.57, "zone": "CA-QC", "ca_source": "hq", "jurisdiction": "CA" },
11
11
  "ap-southeast-1": { "name": "Singapore", "lat": 1.29, "lon": 103.85, "zone": "SG", "annual_zone": "SG", "jurisdiction": "SG" },
12
12
  "ap-northeast-1": { "name": "Tokyo", "lat": 35.68, "lon": 139.69, "zone": "JP-TK", "annual_zone": "JP", "jurisdiction": "JP" },
@@ -381,6 +381,6 @@ def explain_methodology_and_limits() -> dict:
381
381
  "forecast": "Day-ahead forecast is RELATIVE: upcoming hours ranked by forecast wind+solar output (ENTSO-E A69), not an absolute gCO2/kWh forecast. EU only; US/uncovered zones degrade to the recent historical pattern (labelled 'historical').",
382
382
  "data_residency": "GreenPick flags when a recommended move CROSSES a jurisdiction (esp. leaving the EEA) as a geographic heads-up only. It does NOT assess legal/GDPR compliance, does not encode adequacy decisions, and is not legal advice — confirm with your own data governance.",
383
383
  "ignored": "Embodied/hardware carbon and provider renewable-energy certificates.",
384
- "fallback_data": "Used when live data isn't available (no key, the source didn't respond, or GreenPick isn't set to live). 'typical' = this calendar month's average intensity and average hour-of-day profile (UTC) for the region, computed from 12 months of real hourly EIA-930 / ENTSO-E data with GreenPick's own emission factors (bundled typical.json, refreshed by scripts/refresh_typical.py) — a realistic norm, not a current reading. 'annual_average' = the country's yearly lifecycle grid intensity from Ember (via Our World in Data; bundled annual.json with source and year), for regions with no free hourly data (Singapore, Tokyo, Mumbai/Pune) — sound for comparing regions, no timing signal (no hourly profile). 'mock' = a hand-set illustrative value with a synthetic hourly pattern, used only where nothing else exists (Sydney); timing advice on mock isn't meaningful. Both carry a live_data_hint saying why the result isn't live and how to turn live data on.",
384
+ "fallback_data": "Used when live data isn't available (no key, the source didn't respond, or GreenPick isn't set to live). 'typical' = this calendar month's average intensity and average hour-of-day profile (UTC) for the region, computed from 12 months of real hourly EIA-930 / ENTSO-E data with GreenPick's own emission factors (bundled typical.json, refreshed by scripts/refresh_typical.py) — a realistic norm, not a current reading. 'annual_average' = the country's yearly lifecycle grid intensity from Ember (via Our World in Data; bundled annual.json with source and year), for regions with no free hourly data (Singapore, Tokyo, Mumbai/Pune, Sydney — national figures, so Sydney's is a lower bound for its coal-heavier state) — sound for comparing regions, no timing signal (no hourly profile). 'mock' = a hand-set illustrative value with a synthetic hourly pattern, a last resort no catalog region relies on; timing advice on mock isn't meaningful. Both carry a live_data_hint saying why the result isn't live and how to turn live data on.",
385
385
  "data_sources": "GREENPICK_CARBON_SOURCE=auto routes US regions to EIA-930 and EU regions to ENTSO-E (both free, key needed), and Canadian regions to Hydro-Québec (Québec) and IESO (Ontario) open data (free, no key). All mixes are weighted with the same per-fuel factors. Without live data a region uses its clearly-labelled typical value (or mock where no free source exists). Electricity Maps is available when selected explicitly (GREENPICK_CARBON_SOURCE=electricitymaps).",
386
386
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: greenpick
3
- Version: 0.4.0
3
+ Version: 0.4.1
4
4
  Summary: Design-time sustainability co-pilot (MCP server): greener, cheaper cloud infrastructure decisions with carbon + cost + latency tradeoffs.
5
5
  Author-email: "Data Science Advisers, LLC" <info@datascienceadvisers.com>
6
6
  License-Expression: MIT
@@ -60,11 +60,12 @@ seconds. (It responds when you ask; auto-nudging on infra-code changes is on the
60
60
  > this month's average for each grid, from a year of real data.
61
61
 
62
62
  <p align="center">
63
- <img src="https://raw.githubusercontent.com/bithug01/greenpick/master/docs/img/region-carbon-30x.svg" alt="Same H100 training run, ~30× the carbon depending only on region (Stockholm vs Sydney)" width="560" />
63
+ <img src="https://raw.githubusercontent.com/bithug01/greenpick/master/docs/img/region-carbon-30x.svg" alt="Same H100 training run, ~28× the carbon depending only on region (Stockholm vs Sydney)" width="560" />
64
64
  </p>
65
65
 
66
- *The same 40-GPU-hour training run emits ~0.7 kg CO₂ in Stockholm vs ~19 kg in Sydney — a ~30×
67
- difference from region choice alone. Ask GreenPick and it surfaces that (with the cost and latency
66
+ *The same 40-GPU-hour training run emits ~0.6 kg CO₂ in Stockholm vs ~17.5 kg in Sydney — a ~28×
67
+ difference from region choice alone (typical October grids; Sydney on Ember's 2025 national figure,
68
+ a lower bound for its coal-heavier state grid). Ask GreenPick and it surfaces that (with the cost and latency
68
69
  tradeoff) while you're still in the editor, before you commit.*
69
70
 
70
71
  **▶ [See the 5-minute demo](docs/demo.md)** — three scenarios with real tool output (training placement, the egress backfire, the forecast).
@@ -195,12 +196,12 @@ See `docs/user-questions-backlog.md`.
195
196
  intensity and average hour-of-day profile (UTC), computed from 12 months of real hourly EIA-930 /
196
197
  ENTSO-E data (`data/typical.json`, refreshed by `scripts/refresh_typical.py`). Labelled
197
198
  `quality: "typical"` — a realistic norm, never presented as a current reading.
198
- - **Annual** — for Singapore, Tokyo and Mumbai/Pune, where no free hourly data exists: the country's
199
+ - **Annual** — for Singapore, Tokyo, Mumbai/Pune and Sydney, where no free hourly data exists: the country's
199
200
  yearly lifecycle grid intensity from [Ember](https://ember-energy.org/) (via Our World in Data,
200
201
  CC BY 4.0), with source and year (`data/annual.json`). Labelled `quality: "annual_average"`; fine for
201
202
  comparing regions, but carries no timing signal.
202
- - **Mock** — a hand-set illustrative value, labelled `quality: "mock"`, used only where nothing else
203
- exists (currently Sydney).
203
+ - **Mock** — a hand-set illustrative value, labelled `quality: "mock"`, kept only as a last resort if
204
+ every other source is missing. No catalog region relies on it.
204
205
  - Cost: a **dated static pricing table** (`data/pricing.json`), refreshed from live Vantage
205
206
  pricing via `scripts/refresh_pricing.py` using one matched method across all three clouds.
206
207
  - Instance-family efficiency: directional perf-per-watt multipliers (`data/instance_families.json`).
@@ -318,7 +319,7 @@ carbon, set two free keys (`EIA_API_KEY` for US, `ENTSOE_TOKEN` for EU) and
318
319
  `GREENPICK_CARBON_SOURCE=auto` — see [Turn on live data](#turn-on-live-data).
319
320
 
320
321
  **Why "directional" and not audited numbers?** Because it's for a *design-time decision*, not a
321
- compliance report. "Stockholm is ~30× cleaner than Sydney" is what you need to pick a region in
322
+ compliance report. "Stockholm is ~28× cleaner than Sydney" is what you need to pick a region in
322
323
  seconds — a certified figure is a different job for a different tool (Electricity Maps, WattTime).
323
324
  GreenPick is for *decisions*, not *disclosures*, and says so in every tool's limitations.
324
325
 
@@ -348,10 +348,10 @@ async def test_auto_router_picks_source_by_region(monkeypatch):
348
348
  repo = AutoRepository(eia_key="k", entsoe_token="t")
349
349
  us = await repo.carbon_now("aws", "us-east-1") # -> EIA
350
350
  eu = await repo.carbon_now("aws", "eu-north-1") # -> ENTSO-E
351
- syd = await repo.carbon_now("aws", "ap-southeast-2") # -> mock
351
+ syd = await repo.carbon_now("aws", "ap-southeast-2") # -> no live source: sourced yearly figure
352
352
  assert us.source == "eia"
353
353
  assert eu.source == "entsoe"
354
- assert syd.quality == "mock"
354
+ assert syd.quality == "annual_average"
355
355
 
356
356
 
357
357
  @pytest.mark.asyncio
@@ -1494,14 +1494,26 @@ async def test_typical_reading_is_this_months_real_average():
1494
1494
 
1495
1495
 
1496
1496
  @pytest.mark.asyncio
1497
- async def test_uncovered_region_still_falls_back_to_mock():
1497
+ async def test_mock_is_only_the_last_resort(monkeypatch):
1498
+ from greenpick.data import typical_repository as tr
1498
1499
  repo = TypicalCarbonRepository()
1500
+ # Sydney has no typical data but a sourced yearly figure -> annual, not mock.
1499
1501
  r = await repo.carbon_now("aws", "ap-southeast-2")
1500
- assert r.quality == "mock"
1502
+ assert r.quality == "annual_average" and r.g_co2_per_kwh == 525.2
1503
+ # With no yearly figure either, mock still answers (labelled) rather than failing.
1504
+ monkeypatch.setattr(tr, "annual_entry", lambda p, reg: None)
1505
+ m = await repo.carbon_now("aws", "ap-southeast-2")
1506
+ assert m.quality == "mock"
1501
1507
  hist = await repo.carbon_history("aws", "ap-southeast-2")
1502
1508
  assert hist and hist[0].period.startswith("H") and not hist[0].period.endswith("Z")
1503
1509
 
1504
1510
 
1511
+ def test_no_catalog_region_relies_on_mock():
1512
+ from greenpick.data.static_data import iter_regions
1513
+ for prov, reg, meta in iter_regions():
1514
+ assert typical_entry(prov, reg) or annual_entry(prov, reg), f"{prov}/{reg} would be mock"
1515
+
1516
+
1505
1517
  @pytest.mark.asyncio
1506
1518
  async def test_typical_history_is_the_months_real_profile_in_utc():
1507
1519
  from greenpick.services import scheduling_service as ss
File without changes
File without changes