Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .github/workflows/lamatic-update.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Detect & Sync Lamatic Flows

on:
push:
branches:
- feat/cloud-carbon-advisor
paths:
- '**/lamatic/flows/**'

jobs:
detect-flow-changes:
environment: feat/cloud-carbon-advisor
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, '[LAMATIC-COMMIT]')"
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Sync Flows to lamatic
uses: Lamatic/sync-flows-to-lamatic@v1
with:
lamatic-endpoint: ${{ secrets.LAMATIC_PROJECT_ENDPOINT }}
api-key: ${{ secrets.LAMATIC_PROJECT_API_KEY }}
project-id: ${{ secrets.LAMATIC_PROJECT_ID }}
auto-deploy: ${{ secrets.LAMATIC_AUTO_DEPLOY_PROJECT }}

9 changes: 9 additions & 0 deletions kits/cloud-carbon-advisor/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Lamatic credentials for deploying and running the carbon-advisor flow.
# Copy to .env.local (never commit real values) and fill in from Studio > Settings.
LAMATIC_API_KEY=your_api_key_here
LAMATIC_PROJECT_ID=your_project_id_here
LAMATIC_API_URL=https://your-project.lamatic.dev/graphql

# The deployed flow's ID — Studio > flow details panel (three-dot menu) > Flow ID.
# Left blank on purpose: unset ⇒ the app runs in offline heuristic mode.
LAMATIC_CARBON_ADVISOR_FLOW_ID=
10 changes: 10 additions & 0 deletions kits/cloud-carbon-advisor/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Never commit real credentials — every .env* is ignored except the template.
.env
.env.*
!.env.example

# dependencies pulled in by the app
apps/node_modules
apps/.next

.DS_Store
190 changes: 190 additions & 0 deletions kits/cloud-carbon-advisor/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
# Cloud Carbon Advisor

Git blame for your cloud carbon.

Upload a cloud usage export (a [FOCUS](https://focus.finops.org/)-style billing
CSV). It computes an auditable CO₂e footprint per service and region, finds your
carbon hotspots, and returns an **impact-ranked decarbonization plan** — with a
specific lever for each hotspot, its effort, its risk, and a projected saving.

Every gram is computed by code from published emissions factors. The model never
emits a number — it only decides *which lever to pull*. See [Architecture](#architecture).

## What you get back

- **A footprint, per service and region** — estimated electricity (kWh), the
region's grid carbon intensity, and the resulting kgCO₂e, aggregated into the
hotspots that actually matter.
- **A diagnosed driver for each hotspot** — is this carbon coming from a *dirty
grid*, *heavy compute*, *storage bloat*, or *egress*? The driver decides the
fix.
- **A costed lever** — a concrete action (migrate region, move to ARM, tier cold
storage, schedule to low-carbon hours…), its effort and risk, and a projected
reduction in kgCO₂e — with cross-region moves flagged, never presented as free.
- **The region-migration ceiling** — computed exactly: how much CO₂e each
hotspot would drop if it ran on the cleanest same-provider region.

## Why this and not the cloud providers' own carbon tools

| Tool | Multi-cloud | No cloud credentials needed | Tells you **what to do** |
|---|:---:|:---:|:---:|
| AWS Customer Carbon Footprint Tool | ❌ AWS only | ❌ needs the account | ❌ reports only |
| GCP Carbon Footprint / Azure Emissions Impact | ❌ one cloud each | ❌ needs the account | ❌ reports only |
| Cloud Carbon Footprint (OSS) | ✅ | ❌ connects to billing APIs | ⚠️ estimates, limited guidance |
| **This kit** | ✅ | ✅ works from a static usage export | ✅ a ranked, costed lever per hotspot |

The providers' first-party tools each cover one cloud, require access to the
account, and stop at *reporting* — they tell you your number, not which of your
workloads to move or how. [Cloud Carbon Footprint](https://www.cloudcarbonfootprint.org/),
the excellent open-source estimator this kit borrows its methodology from, is
multi-cloud but is a self-hosted application that connects to your billing APIs.

This kit deliberately trades live integration for zero-credential portability: it
works from a usage **file** you already have, across any provider in one report,
and its differentiated output is the **plan** — a prioritized list of levers an
engineer can act on this week — not another dashboard of numbers.

**What this kit does not claim:** it is not an audited carbon-accounting system
of record (see [Limitations](#limitations)), and it does not connect to any cloud
API. It is a fast, defensible planning tool for *where to cut carbon first*.

## Architecture

```text
Comment thread
coderabbitai[bot] marked this conversation as resolved.
FOCUS usage CSV
├─ apps/lib/compute-emissions.ts deterministic — runs in the Next.js app
│ energy(kWh) × PUE × grid-intensity(region) = gCO₂e, per service/region;
│ ranks hotspots; computes the cleaner-region delta exactly — all arithmetic
└─ flows/carbon-advisor judgment only — runs in Lamatic
├─ Diagnose (InstructorLLM) dirty-grid | compute-heavy | storage-bloat
│ | egress-heavy — driver class, no numbers
├─ Recommend (InstructorLLM) a lever + effort/risk + a reductionKey
│ bucket (not a number)
└─ Finalize (code) coerce every enum, drop any invented
hotspot id — never trust the model's shape
└─ apps/lib/assemble.ts deterministic — prices each chosen lever
from a fixed reductionKey→multiplier table, reconciles totals, writes
relatable equivalences, and produces the report.
```

**The model never does arithmetic.** Estimating emissions is a chain of lookups
(usage × energy coefficient × PUE × grid intensity); pricing a lever is a bucket
multiplier times the hotspot's own footprint. Both run in TypeScript, are
unit-tested, and are the same code the offline eval asserts against. *Which
driver, and which lever* is a judgment call — the one part of the pipeline that
genuinely needs a model.

Enforcement of "never a number" is layered, not just a prompt request: the
[constitution](./constitutions/default.md) states it, the flow's `Finalize` code
node forces every field into a fixed enum and never reads a numeric field out of
the model's output, and the app prices levers from its own tested table — so
every figure in a report traces back to a deterministic source. `npm run eval`
asserts exactly that.

## Emissions methodology & sources

This kit follows the methodology of the open-source
[Cloud Carbon Footprint](https://www.cloudcarbonfootprint.org/docs/methodology)
project:

```text
emissions (gCO₂e) = energy (kWh) × PUE × grid carbon intensity (gCO₂e/kWh)
energy (kWh) = usage amount × energy coefficient for that usage class
```

- **Energy coefficients** (compute per vCPU-hour, memory/storage per GB-hour,
network per GB) — Cloud Carbon Footprint methodology, including its ~40% lower
coefficient for ARM/Graviton silicon.
- **PUE** — provider sustainability disclosures (AWS 1.135, Google 1.10, Azure
1.125), 1.20 where unknown.
- **Grid carbon intensity by region** — Cloud Carbon Footprint's
grid-emissions-factors, provider carbon-data pages, and Ember's yearly grid
averages.

All factors live in one auditable file, [`apps/lib/emissions-factors.ts`](./apps/lib/emissions-factors.ts),
and are trivially swappable. These are order-of-magnitude-correct *planning*
figures; the kit's value is the **relative** comparison between regions and
levers, which is robust to the absolute uncertainty in any single coefficient.

## Quickstart

**Option A — full experience (with the Lamatic flow):**

1. Import [`flows/carbon-advisor.ts`](./flows/carbon-advisor.ts) into Lamatic
Studio, attach a Gemini (or other) credential to the two model nodes, deploy,
and copy the Flow ID.
2. `cd kits/cloud-carbon-advisor/apps`
3. `cp .env.example .env.local` and fill in the four values (see below).
4. `npm install && npm run dev`
5. Open http://localhost:3000, press **Load example**, then **Analyze footprint**.

**Option B — offline (no credentials):** skip step 1 and leave
`LAMATIC_CARBON_ADVISOR_FLOW_ID` blank. The app computes the full footprint and
runs a deterministic **heuristic** plan, clearly badged, so you can explore it
with zero setup. Connect the flow to replace the heuristic with real reasoning.

Independent of Studio: `npm run eval` runs 58 offline assertions (numeric
integrity, classifier, cleaner-region math, savings pricing, model-output
coercion, CSV-injection) with no network and no model calls.

## Environment

| Variable | Source |
|---|---|
| `LAMATIC_API_KEY` | Studio → Settings → API Keys |
| `LAMATIC_PROJECT_ID` | Studio → Settings → Project → Project ID |
| `LAMATIC_API_URL` | Studio → API Docs → Endpoint |
| `LAMATIC_CARBON_ADVISOR_FLOW_ID` | Flow → three-dot menu → Flow ID |

All four are read server-side only and never prefixed `NEXT_PUBLIC_`: the three
`LAMATIC_API_*` values are consumed by the Lamatic client used in
`apps/actions/orchestrate.ts`, and `LAMATIC_CARBON_ADVISOR_FLOW_ID` is resolved
through the `apps/orchestrate.js` deployment manifest. Account identifiers are
stripped before the flow is called.

## Input format

A CSV with at least these columns (common aliases are accepted, e.g.
`Region`/`RegionId`, `UsageQuantity`/`PricingQuantity`):

`ServiceName`, `ServiceCategory`, `RegionId`, `PricingUnit`, `PricingQuantity` —
plus optional `ProviderName`, `SubAccountId`, `SkuId`, `BilledCost`,
`BillingCurrency`. See [`apps/public/sample-usage.csv`](./apps/public/sample-usage.csv)
for a working example.

## Limitations

- **Planning-grade, not audited.** Estimates use public average factors, not your
actual metered power draw or your provider's market-based (contractual) carbon
accounting. Treat the output as *where to look first*, not a compliance figure.
- **Representative region set.** [`emissions-factors.ts`](./apps/lib/emissions-factors.ts)
covers the most-used regions and the cleanest ones; an unlisted region falls
back to the global grid average (~475 gCO₂e/kWh) rather than scoring zero.
- **No utilisation signal.** Billing usage doesn't reveal CPU utilisation, so
`over-provisioned` is diagnosed conservatively and rightsizing is an estimate.
- **Location-based intensity.** Grid intensities are yearly location-based
averages, not hour-by-hour marginal intensity; `schedule-shift` savings are
therefore directional.
- **The region-migration ceiling is a ceiling.** Moving a workload to the
cleanest grid ignores data-residency, latency, and egress-repatriation cost —
which is exactly why the model weighs those into `effort`/`risk` and flags
cross-region moves.

## Common failure modes

| Symptom | Cause | Fix |
|---|---|---|
| `Missing LAMATIC_…` | No `.env.local`, or a blank value | Fill in the four variables (or leave the Flow ID blank for offline mode) and restart |
| `CSV is missing required column(s)` | Not a FOCUS-style export, or wrong delimiter | Ensure ServiceName, RegionId, PricingUnit, PricingQuantity columns exist |
| Badge says **offline heuristic** | No Flow ID configured | Expected — set `LAMATIC_CARBON_ADVISOR_FLOW_ID` to use the Lamatic flow |
| An unlisted region shows ~475 g/kWh | Region not in the factor table | Expected fallback; add it to `emissions-factors.ts` for precision |
| A hotspot shows a `flags` chip | The model returned an out-of-range enum or a bad id | Expected and handled — the coerced-to-safe value is shown, not an error |
| `Could not reach Lamatic` | Wrong `LAMATIC_API_URL`, or no network | Re-copy the endpoint from Studio → API Docs |

## License

Contributed to [Lamatic AgentKit](https://github.com/Lamatic/AgentKit) under the
repository's license.
152 changes: 152 additions & 0 deletions kits/cloud-carbon-advisor/agent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# Cloud Carbon Advisor

## Overview

An agent that answers the question a cloud carbon dashboard never does: *what do
I do about it?* It takes a FOCUS usage export, computes an auditable CO₂e
footprint per service and region, finds the hotspots, diagnoses why each one
emits what it does, and returns a specific, costed decarbonization lever for each
— or says honestly that no single driver dominates.

## Purpose

Every first-party cloud carbon tool (AWS Customer Carbon Footprint Tool, Google
Cloud Carbon Footprint, Azure Emissions Impact Dashboard) covers a single cloud,
requires access to the account, and stops at *reporting* — a number, a trend, a
breakdown. None of them rank *which of your workloads to move first*, or tell you
the lever and its cost. Cloud Carbon Footprint (open source) is multi-cloud and
estimate-based but is a self-hosted app wired to your billing APIs.

This agent automates the step after the number: from a usage file — any provider,
no cloud credentials — it produces a prioritized, costed plan, with the
discipline a carbon figure demands: every number is computed by code, never by a
model.

## Architecture

```text
FOCUS usage CSV
├─ apps/lib/compute-emissions.ts deterministic — runs in the Next.js app
│ └─ Hotspot[] energy × PUE × grid-intensity = gCO₂e,
│ ranked; cleaner-region delta computed exactly
└─ flows/carbon-advisor judgment — runs in Lamatic
├─ Diagnose (InstructorLLM) driverClass + confidence + evidence, no numbers
├─ Recommend (InstructorLLM) action + effort/risk + reductionKey bucket
└─ Finalize (code) coerce every enum, drop invented ids
└─ apps/lib/assemble.ts deterministic — prices each lever from a
fixed multiplier table, builds the report
```

Estimating emissions and pricing a lever are both solved arithmetic problems, so
they run in TypeScript and are unit-tested (`npm run eval`). *Which driver, and
which lever* is judgment over the hotspot's shape — which is what the model is
for. The flow never receives account identifiers (stripped before the call) and
never emits a number.

## Flows

### `carbon-advisor`

**Trigger** — API Request (GraphQL). Accepts:

| Field | Type | Meaning |
|---|---|---|
| `hotspots` | `[string]` | Ranked `Hotspot[]` from the app's footprint engine, each JSON-encoded, identifier-free |
| `periodLabel` | string | Human-readable billing period |
| `currency` | string | ISO currency code |

**Processing** — Diagnose classifies each hotspot's dominant carbon driver
(`dirty-grid`, `compute-heavy`, `storage-bloat`, `egress-heavy`,
`over-provisioned`, `mixed`) with evidence and rejected alternatives. Recommend
chooses one lever and a `reductionKey` bucket, weighing cross-region data
residency and latency into effort/risk. Finalize coerces every enum into range,
drops any hotspot id the model invented, and never reads a number out of the
model's output.

**Response** —

```typescript
{
diagnoses: Array<{ hotspotId, driverClass, confidence, evidence, reasoning, rejectedDrivers }>,
recommendations: Array<{ hotspotId, action, rationale, effort, risk, prerequisites, reductionKey }>
}
```

The app then prices each `reductionKey` deterministically, reconciles totals, and
renders the footprint dashboard and plan.

**When to use it** — at the end of a billing period, before a FinOps/GreenOps
review, or whenever "our cloud footprint is X tonnes" needs to become "and here
are the three moves that cut it most".

**Dependencies** — one structured-output ("instructor") model for diagnosis and
one for recommendation.

## Guardrails

Beyond [`constitutions/default.md`](./constitutions/default.md):

- **Never emit a number.** Neither model node is asked for a gram, kWh, or
percentage. The app computes every figure from the footprint and a fixed
multiplier table.
- **Never invent an enum or an id.** `driverClass`, `reductionKey`, `effort`, and
`risk` are coerced to a safe value if out of range; a `hotspotId` that does not
match an input hotspot is dropped. Coerced fields surface as a `flags` chip
rather than a silent error.
- **No greenwashing.** A cross-continent migration is never presented as free —
its residency/latency cost is raised into `risk` and a prerequisite, and an
honest `region-migration-partial` beats a reckless `-major`.

### Not in scope

- Live cloud billing APIs or credentials — this kit is static-file-in by design.
- Audited, market-based carbon accounting — figures are location-based planning
estimates (see the README's Limitations).
- Hour-by-hour marginal grid intensity — intensities are yearly averages, so
`schedule-shift` savings are directional.

## Integration reference

| Service | Purpose | Credential |
|---|---|---|
| Lamatic | Hosts and executes the flow | `LAMATIC_API_KEY`, `LAMATIC_PROJECT_ID`, `LAMATIC_API_URL` |
| Structured-output model (diagnose) | Carbon-driver classification per hotspot | Configured in Studio on the Diagnose node |
| Structured-output model (recommend) | Lever + bucket per hotspot | Configured in Studio on the Recommend node |

No cloud-provider credentials are ever requested — usage data is a file the user
supplies.

## Environment setup

| Variable | Source |
|---|---|
| `LAMATIC_API_KEY` | Studio → Settings → API Keys |
| `LAMATIC_PROJECT_ID` | Studio → Settings → Project → Project ID |
| `LAMATIC_API_URL` | Studio → API Docs → Endpoint |
| `LAMATIC_CARBON_ADVISOR_FLOW_ID` | Flow → three-dot menu → Flow ID (blank ⇒ offline heuristic mode) |

## Quickstart

1. Import `flows/carbon-advisor.ts` into Lamatic Studio, attach a model
credential to both model nodes, deploy, and copy the Flow ID.
2. `cd kits/cloud-carbon-advisor/apps`
3. `cp .env.example .env.local` and fill in the four values above.
4. `npm install && npm run dev`
5. Open http://localhost:3000, press **Load example**, then **Analyze footprint**.

Offline: leave the Flow ID blank to run the deterministic heuristic plan with no
credentials. `npm run eval` runs the 58-assertion offline suite.

## Common failure modes

| Symptom | Cause | Fix |
|---|---|---|
| `Missing LAMATIC_…` | No `.env.local`, or a blank required value | Fill the variables (Flow ID may stay blank) and restart |
| `CSV is missing required column(s)` | Not a FOCUS-style export | Ensure ServiceName, RegionId, PricingUnit, PricingQuantity exist |
| Badge shows **offline heuristic** | No Flow ID configured | Set `LAMATIC_CARBON_ADVISOR_FLOW_ID` to use the flow |
| A `flags` chip on a hotspot | Model returned an out-of-range enum or bad id | Expected and handled — the safe coerced value is shown |
| `Could not reach Lamatic` | Wrong `LAMATIC_API_URL` or no network | Re-copy the endpoint from Studio → API Docs |
| Every recommendation is "no recommendation" | The model node is misconfigured | Confirm a credential is attached to both model nodes in Studio |
Loading
Loading