GreenPrompt ๐ฑ
Track and reduce the real-world energy cost of your AI prompts.
GreenPrompt is a local-first tool that intercepts Ollama LLM calls, measures CPU/GPU power consumption in real time, logs every prompt run to a local SQLite database, and provides a CLI and interactive web dashboard for energy analysis and reporting.
Table of Contents
- Features
- Requirements
- Installation
- Quick Start
- CLI Reference
- REST API
- Web Dashboard
- Prompt Scoring
- Architecture
- Configuration
- Platform Support
- Troubleshooting
- Contributing
- License
Features
- Real-time power sampling โ Continuously samples system CPU/GPU power every second, retaining a 10-minute sliding window. macOS uses
powermetrics; Linux uses Intel/AMD RAPL energy counters, an ARM big.LITTLE frequency model, ornvidia-smifor GPU power. - Per-prompt energy measurement โ Computes watt-hours consumed during each LLM call against a 1-minute idle baseline.
- Token tracking โ Records prompt tokens, completion tokens, and total tokens per run from Ollama's response metadata.
- Dual estimation โ Provides both hardware-measured power and token-based estimates for cross-validation and offline comparison.
- Persistent logging โ Stores all run data in a local SQLite database (
greenprompt_usage.db). - Prompt quality scoring โ Automatically scores every prompt on 18 dimensions using offline NLTK heuristics (RTCF structure, clarity, conciseness, energy awareness, and more).
- Interactive dashboard โ Plotly-powered web dashboard with 6 chart types: energy timeline, CPU/GPU breakdown, model comparison, estimated vs. actual, and baseline overlay.
- REST API โ Flask server exposing endpoints for running prompts and querying usage history.
- Ollama proxy โ Optional transparent reverse proxy to intercept existing Ollama traffic without code changes.
Requirements
- Python 3.9 or higher
- Ollama installed and running locally
- Live power sampling on macOS and Linux โ
powermetricson macOS (Apple Silicon or Intel); RAPL, ARM big.LITTLE, ornvidia-smion Linux - Windows: scoring, logging, and token tracking work fully; power sampling returns zero (see Platform Support)
Installation
From PyPI
pip install greenprompt
With Poetry
poetry add greenprompt
Development Installation
git clone https://github.com/uday1201/greenprompt.git
cd greenprompt
poetry install
Quick Start
# 1. Initialize (first time only; on macOS run it once with sudo so that
# powermetrics can be used without a password prompt afterwards)
sudo greenprompt setup
# 2. Start the API server (required for prompt and dashboard commands)
greenprompt run --port 5000
# 3. Send a prompt and see energy stats
greenprompt prompt "Explain quantum entanglement in one sentence." --model llama3.2:latest
# 4. Open the analytics dashboard
greenprompt dashboard
CLI Reference
GreenPrompt installs three equivalent command aliases: greenprompt, gprompt, and greenp.
Command Overview
No GreenPrompt command needs to be run as root. The one exception is the
first setup on macOS: running it once with sudo installs a sudoers rule so
that powermetrics can be sampled without a password prompt from then on.
Without it, GreenPrompt still runs โ energy readings are simply zero.
| Command | Description | Requires sudo |
|---|---|---|
setup |
Write config, download NLTK data, verify Ollama, create DB | Once, on macOS |
run |
Start the Flask API server in the background | No |
prompt |
Send a prompt; print response and energy stats | No |
monitor |
Display the last N prompt usage entries from DB | No |
score |
Score a prompt without sending it to a model | No |
dashboard |
Open the analytics dashboard in a browser | No |
stop |
Stop the running API server | No |
log_api |
Tail the API server log file | No |
greenprompt setup
Initializes the environment: detects CPU power capabilities and writes them to the user config file (~/.greenprompt/config.json by default), downloads NLTK resources, verifies Ollama, and creates the SQLite database.
On macOS it also installs /etc/sudoers.d/greenprompt, which allows powermetrics to be sampled without a password. That step is the only reason to use sudo here, and it is skipped with an explanatory message if you run without it.
sudo greenprompt setup [--ollama-port PORT]
| Flag | Default | Description |
|---|---|---|
--ollama-port |
11434 |
Port where Ollama is running |
Note: Run
setupfrom the directory where you want the database (greenprompt_usage.db) to live. All subsequent commands should be run from the same directory.
greenprompt run
Starts the GreenPrompt Flask API server as a background process, along with the power sampler for the current platform: PowerMonitor on macOS, LinuxPowerMonitor on Linux. Neither requires root.
greenprompt run [--port PORT]
Note: Wait 10โ15 seconds after starting before sending the first prompt, so the sampler has collected enough readings to average over.
| Flag | Default | Description |
|---|---|---|
--port |
5000 |
Port for the Flask server |
greenprompt prompt
Sends a prompt to Ollama via the running API server, prints the LLM response, and displays detailed energy and token statistics.
greenprompt prompt "Your prompt here" [--model MODEL]
| Flag | Default | Description |
|---|---|---|
--model |
llama3.2:latest |
Ollama model name to use |
Example:
greenprompt prompt "List three benefits of solar energy. Format: bullet points." --model llama2
Output:
Response:
โข Solar energy is renewable and inexhaustible.
โข It reduces electricity bills significantly over time.
โข It produces no greenhouse gas emissions during operation.
--- Prompt usage data ---
Prompt tokens: 14
Completion tokens: 43
Total tokens: 57
Duration (sec): 3.21
Baseline power (W): 4.20
Baseline energy (Wh):0.000070
CPU power (W): 8.60
GPU power (W): 0.00
Combined power (W): 8.60
Energy used (Wh): 0.000767
greenprompt monitor
Displays the last N prompt usage entries from the local SQLite database.
greenprompt monitor [--count N]
| Flag | Default | Description |
|---|---|---|
--count |
10 |
Number of recent entries to show |
greenprompt score
Scores a prompt on 18 quality dimensions without sending it to any model. Entirely offline and instant.
greenprompt score "Your prompt here"
Example:
greenprompt score "You are a physics expert. Explain Newton's second law to a high school student. Format: bullet points."
Output:
{
'total_score': 34,
'max_score': 50,
'score_percent': 68.0,
'details': {
'RTCF Structure': 3,
'Clarity & Specificity': 5,
'Conciseness': 5,
'Contextual Priming': 0,
'Output Specification': 5,
'Instructional Tone': 3,
...
}
}
greenprompt dashboard
Opens the Plotly analytics dashboard at http://localhost:5000/dashboard in the default browser. Requires the API server to be running.
greenprompt dashboard
greenprompt stop
Stops the Flask API server by killing the process on the given port.
greenprompt stop [--port PORT]
| Flag | Default | Description |
|---|---|---|
--port |
5000 |
Port where the API server is running |
greenprompt log_api
Tails the API server log file at /tmp/api.log.
greenprompt log_api [--follow]
| Flag | Default | Description |
|---|---|---|
--follow |
False |
Follow (tail -f) the log output |
REST API
The GreenPrompt Flask server exposes the following endpoints after greenprompt run.
POST /api/prompt
Run a prompt through Ollama, measure energy, and log the result.
Request body:
{
"prompt": "Explain photosynthesis.",
"model": "llama2"
}
Response:
{
"prompt": "Explain photosynthesis.",
"prompt_score": 36.0,
"prompt_score_details": {
"RTCF Structure": 1,
"Clarity & Specificity": 5
},
"response": "Photosynthesis is the process by which plants...",
"model": "llama2",
"prompt_tokens": 5,
"completion_tokens": 82,
"total_tokens": 87,
"total_energy (Wh)": 0.000512,
"duration_sec": 4.13,
"combined_power_w (W)": 0.446,
"cpu_power_w (W)": 0.312,
"gpu_power_w (W)": 0.134,
"energy_estimate_tokens": 0.00087,
"energy_estimate_prompt": 0.000005,
"baseline_energy (Wh)": 0.000067,
"baseline_power (W)": 4.03,
"gpu_usage": "No GPU detected",
"system_info": { "OS": "Darwin", "CPU": "Apple M2", ... }
}
GET /api/usage/all
Retrieve all prompt usage records as a JSON array.
curl http://localhost:5000/api/usage/all
GET /api/usage/model/<model>
Filter usage records by model name.
curl http://localhost:5000/api/usage/model/llama2
GET /api/usage/timeframe?start=ISO&end=ISO
Filter usage records by ISO 8601 timestamp range.
curl "http://localhost:5000/api/usage/timeframe?start=2024-01-01T00:00:00&end=2024-12-31T23:59:59"
GET /dashboard
Serves the interactive analytics dashboard.
ANY /ollama/api/<path>
Transparent reverse proxy to the local Ollama server at http://localhost:11434. Preserves method, headers, query params, and body.
# Example: list models via proxy
curl http://localhost:5000/ollama/api/tags
Web Dashboard
The dashboard at http://localhost:5000/dashboard provides six interactive Plotly charts:
| Chart | Description |
|---|---|
| Overview indicators | Total prompts, total energy (Wh), total CPU/GPU watts, total tokens, energy per token |
| Energy usage timeline | Line chart of energy (Wh) per prompt over time |
| CPU vs GPU power | Grouped bar chart of CPU and GPU watts per prompt |
| Estimated vs actual energy | Comparison of token-estimate, prompt-estimate, and hardware-measured energy |
| Baseline vs total energy | Overlay of idle baseline and total energy per prompt |
| Model comparison | Average energy per model (bar chart) |
Prompt Scoring
Every prompt sent through GreenPrompt is automatically scored on 18 quality dimensions. Use greenprompt score to evaluate prompts standalone.
The scorer is entirely offline โ no API call is made. It uses NLTK POS tagging and regex pattern matching.
Scoring Dimensions (50 points total)
| Dimension | Max | Detection method |
|---|---|---|
| RTCF Structure | 4 | Role pattern + task verb + context marker + format spec (1 pt each) |
| Clarity & Specificity | 5 | Task verb present and prompt โค 400 chars = 5; task verb only = 3 |
| Conciseness | 5 | Starts at 5; minus 1 per filler phrase ("please", "could you", "just", etc.) |
| Contextual Priming | 3 | Matches context:, background:, for <word>, audience: |
| Output Specification | 5 | Matches format:, output as, or table/bullet/list/json/csv/markdown |
| Instructional Tone | 3 | Any imperative verb detected via POS tagging |
| Examples & Few-Shot | 2 | Matches example:, Q:, A:, sample output, e.g. |
| Task Decomposition | 2 | Matches first...then sequence or step N numbering |
| Positive/Negative Examples | 2 | Matches do not, exclude, not include, except |
| Iterative Refinement | 2 | Matches revise, improve, refine, rewrite, repeat |
| Creativity Control | 2 | Matches creative, imaginative, unusual, inventive |
| Tone & Style | 2 | Matches tone:, style:, formal, casual, humorous, professional |
| Error Prevention | 2 | Matches do not guess, only answer if sure, if unsure, say so |
| Evaluation & Validation | 2 | Matches double-check, verify, validate, cross-check |
| Sensitivity & Inclusivity | 2 | Matches inclusive, avoid bias, unbiased, sensitive to |
| Efficiency & Sustainability | 2 | Matches concise, briefly, max N words, minimize tokens |
| Energy Awareness | 2 | Matches energy usage, carbon, footprint, sustainable, green |
| Keyword Richness | 2 | โฅ5 unique non-stopword tokens = 2; โฅ2 = 1; else 0 |
See docs/prompt-scoring.md for full details and optimization examples.
Architecture
greenprompt/
โโโ cli.py Entry point โ argparse subcommands, starts API subprocess
โโโ api.py Flask server โ REST endpoints, Ollama proxy, power monitor init
โโโ core.py run_prompt() โ orchestrates Ollama call, power measurement, scoring
โโโ dbconn.py SQLite โ init_db, save_prompt_usage, get_prompt_usage
โโโ samplerMac.py PowerMonitor โ daemon thread sampling powermetrics every second
โโโ samplerLinux.py LinuxPowerMonitor โ RAPL / ARM / TDP CPU sampling + nvidia-smi GPU
โโโ sysUsage.py OS-agnostic wrappers โ system info, power measurement, GPU detection
โโโ scoreBasic.py Prompt scorer โ 18-dimension offline NLTK/regex analysis
โโโ analytics.py Plotly chart functions for the dashboard
โโโ constants.py Tracked source โ defaults, live platform values, user-config overlay
โโโ setup.py Setup routine โ writes ~/.greenprompt/config.json, DB init, NLTK download
โโโ templates/
โโโ dashboard.html Dashboard HTML with embedded Plotly JS
Request Flow
User
โ
โผ
greenprompt prompt "..."
โ (CLI sends HTTP POST)
โผ
POST /api/prompt (api.py Flask server)
โ
โผ
core.py: run_prompt(prompt, model)
โโ PowerMonitor.samples โโโบ baseline avg (1 min before prompt)
โโ POST http://127.0.0.1:11434/api/generate (Ollama)
โโ PowerMonitor.samples โโโบ during-prompt avg
โโ energy_wh = (avg_combined_w ร duration_sec) / 3600
โโ scoreBasic.score_prompt(prompt)
โโ dbconn.save_prompt_usage(result)
โ
โผ
greenprompt_usage.db (SQLite)
Power Sampling
On macOS and Linux, a daemon thread samples once a second into a collections.deque holding the last 600 readings (10 minutes). When run_prompt() completes, the matching measure_power_*() function filters samples by [start_time, end_time] to compute average watts and energy, and separately averages the 60 seconds before the prompt as the idle baseline.
macOS โ PowerMonitor (samplerMac.py) calls:
sudo powermetrics --samplers cpu_power -n 1 -i 1000
Linux โ LinuxPowerMonitor (samplerLinux.py) picks the best available CPU source at startup:
| Mode | Condition | Method |
|---|---|---|
rapl |
/sys/class/powercap/intel-rapl*/energy_uj present |
Intel/AMD energy counter delta โ measured, not estimated |
arm_biglittle |
psutil.cpu_freq(percpu=True) reports more than one distinct max frequency |
Per-cluster frequency-squared model over scaling_cur_freq (power โ Vยฒf) |
linear_tdp |
Neither of the above | cpu_percent / 100 ร CPU_TDP_W |
GPU power comes from a single long-running nvidia-smi dmon -s p -d 1 process, with a per-call subprocess fallback. If the prompt is shorter than one sample interval, measure_power_linux() interpolates from neighbouring samples and flags the result with "extrapolated": true.
For detailed architecture documentation see docs/architecture.md.
Configuration
Settings live in a JSON file written by greenprompt setup, by default ~/.greenprompt/config.json (override with $GREENPROMPT_CONFIG or $GREENPROMPT_HOME).
| Key | Default | Description |
|---|---|---|
OLLAMA_URL |
http://127.0.0.1:11434 |
Ollama server URL |
CPU_TDP_W |
40.0 |
CPU TDP in watts; used only by the Linux linear_tdp fallback |
CPU_POWER_SOURCE |
estimated |
Informational; rapl when direct energy counters were found |
# where is my config?
python -c "from greenprompt import constants; print(constants.config_path())"
Platform values (OS, MACHINE, PLATFORM, ...) are derived live on every import by greenprompt/constants.py and are never stored, so they always match the machine actually running.
Database location: Created in the working directory where setup was run:
<cwd>/greenprompt_usage.db
Always run GreenPrompt commands from the same directory to use the same database file.
See docs/configuration.md for the full configuration reference.
Platform Support
| Feature | macOS Apple Silicon | macOS Intel | Linux | Windows |
|---|---|---|---|---|
| Live CPU power sampling | โ powermetrics | โ powermetrics | โ RAPL / ARM / TDP | ๐ Intel Power Gadget |
| Live GPU power sampling | โ powermetrics | โ powermetrics | โ nvidia-smi | ๐ nvidia-smi |
| GPU detection | โ system_profiler | โ system_profiler | โ nvidia-smi | โ nvidia-smi |
| GPU utilization stats | โ | โ | โ nvidia-smi | โ nvidia-smi |
| Token counting | โ | โ | โ | โ |
| Prompt scoring | โ | โ | โ | โ |
| Database logging | โ | โ | โ | โ |
| REST API & dashboard | โ | โ | โ | โ |
| Token-based energy estimate | โ | โ | โ | โ |
run / stop server lifecycle |
โ | โ | โ | โ needs lsof |
Windows users will see energy_wh = 0 for hardware power measurement; all other features are fully functional. See docs/platform-support.md for the implementation roadmap.
Troubleshooting
โ Could not connect to Ollama at http://127.0.0.1:11434
Ollama is not running. Start it:
ollama serve
Error connecting to API
The GreenPrompt API server is not running:
greenprompt run --port 5000
Power readings are all zero on macOS
The passwordless powermetrics rule is not installed, so the sampler cannot read power. Run setup once with sudo:
sudo greenprompt setup
Power readings are all zero on Linux
Check which mode the sampler chose โ greenprompt log_api reports it at startup. In linear_tdp mode readings are estimates derived from CPU_TDP_W, not measurements. RAPL requires read access to /sys/class/powercap/intel-rapl*/energy_uj, which some distributions restrict to root. GPU power requires nvidia-smi on PATH.
Power readings are all zero on Windows
Live power sampling is not yet implemented on Windows. The energy_estimate_tokens field provides a token-count-based approximation.
Dashboard shows no data
The database is either empty or was created in a different working directory. Check:
greenprompt monitor --count 5
If empty, send some prompts first. If the DB file is in a different directory, cd there before running GreenPrompt.
Warning: Power usage data is incomplete or missing
The PowerMonitor has not yet collected enough samples. Wait 10โ15 seconds after greenprompt run before sending the first prompt.
Contributing
See CONTRIBUTING.md for the full contribution workflow. Quick summary:
git checkout -b feature/your-feature
poetry install
# make changes
poetry run ruff check .
poetry run ruff fmt .
git commit -m "feat(scope): description"
git push origin feature/your-feature
# open pull request
Roadmap
- Windows support via Intel Power Gadget / WMI, and a
lsof-free server lifecycle - Test coverage for the scoring, database, and analytics modules
- Model-aware token energy estimates (the current table predates Ollama model naming)
- VS Code extension
- Browser extension for cloud LLM APIs
- Carbon offset integration
- Team dashboards and enterprise reporting
License
MIT License โ ยฉ Uday & Anirudh
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file greenprompt-0.2.0.tar.gz.
File metadata
- Download URL: greenprompt-0.2.0.tar.gz
- Upload date:
- Size: 43.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8589bc8e81b26b9334b3e941137181a3fe0db534f067d4b704573d02242ba88c
|
|
| MD5 |
69ecca2409227e164842575bd0f92659
|
|
| BLAKE2b-256 |
39a15d180f4468d7496dc2eeb5cdf6b4e655a337a7e9cce7739cb5718ceb1098
|
Provenance
The following attestation bundles were made for greenprompt-0.2.0.tar.gz:
Publisher:
publish.yaml on uday1201/greenprompt
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
greenprompt-0.2.0.tar.gz -
Subject digest:
8589bc8e81b26b9334b3e941137181a3fe0db534f067d4b704573d02242ba88c - Sigstore transparency entry: 2568389822
- Sigstore integration time:
-
Permalink:
uday1201/greenprompt@6854d5c699c7f5d71f2c0e178e486f5dd25103f5 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/uday1201
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yaml@6854d5c699c7f5d71f2c0e178e486f5dd25103f5 -
Trigger Event:
push
-
Statement type:
File details
Details for the file greenprompt-0.2.0-py3-none-any.whl.
File metadata
- Download URL: greenprompt-0.2.0-py3-none-any.whl
- Upload date:
- Size: 43.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
113147d1bf64e4a4ee1c8fd8cdd9f1c7395a947f91e43568cfef5894e9f9d6a1
|
|
| MD5 |
8f3566458af7066b2f1972d910c6b349
|
|
| BLAKE2b-256 |
544e706bdef1121263be36cc0b969fe56d1241b497c87970c93d3a9994ea06ee
|
Provenance
The following attestation bundles were made for greenprompt-0.2.0-py3-none-any.whl:
Publisher:
publish.yaml on uday1201/greenprompt
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
greenprompt-0.2.0-py3-none-any.whl -
Subject digest:
113147d1bf64e4a4ee1c8fd8cdd9f1c7395a947f91e43568cfef5894e9f9d6a1 - Sigstore transparency entry: 2568389840
- Sigstore integration time:
-
Permalink:
uday1201/greenprompt@6854d5c699c7f5d71f2c0e178e486f5dd25103f5 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/uday1201
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yaml@6854d5c699c7f5d71f2c0e178e486f5dd25103f5 -
Trigger Event:
push
-
Statement type: