Getting Started — Python SDK (ecoai-python)
Install
pip install ecoai-python
# With provider extras (install whichever you use)
pip install "ecoai-python[openai]" # OpenAI
pip install "ecoai-python[anthropic]" # Anthropic
pip install "ecoai-python[gemini]" # Google Gemini (google-generativeai SDK)
pip install "ecoai-python[gemini-genai]" # Google Gemini (google-genai SDK)
pip install "ecoai-python[redis]" # Redis storage backend
pip install "ecoai-python[all]" # all of the aboveRequirements: Python 3.10+
Quickstart
OpenAI
import os
from ecoai import EcoAI
from openai import OpenAI
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
eco = EcoAI(client=client, mode="dev")
response = eco.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Summarise the water cycle."}],
)
print(response.choices[0].message.content)
# Second call — returns from cache instantly, costs $0
cached = eco.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Summarise the water cycle."}],
)Anthropic
import os
from ecoai import EcoAI
from anthropic import Anthropic
client = Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])
eco = EcoAI(client=client, mode="dev")
response = eco.messages.create(
model="claude-opus-4-8",
max_tokens=1024,
messages=[{"role": "user", "content": "Explain quantum entanglement."}],
)
print(response.content[0].text)Google Gemini
EcoAI supports both the old google-generativeai SDK and the new google-genai SDK. Pass your client directly — the SDK is detected automatically.
import os
from google import genai
from ecoai import EcoAI
client = genai.Client(api_key=os.environ["GOOGLE_API_KEY"])
eco = EcoAI(client=client)
response = eco.models.generate_content(
model="gemini-2.0-flash",
contents="What causes the northern lights?",
)
print(response.text)import os
import google.generativeai as genai
from ecoai import EcoAI
genai.configure(api_key=os.environ["GOOGLE_API_KEY"])
model = genai.GenerativeModel("gemini-2.5-flash")
eco = EcoAI(client=model)
response = eco.generate_content("What causes the northern lights?")
print(response.text)That's the entire integration. No new infrastructure. No config files. No account needed. EcoAI stores responses in a local SQLite file at
.ecoai/cache.dbby default.
Configuration
from ecoai import EcoAI, SemanticConfig
eco = EcoAI(
client=client,
# Cache mode
mode="dev", # "dev": cache forever | "prod": apply TTL
# Storage
storage="sqlite", # "sqlite" | "memory" | "redis"
sqlite_path=".ecoai/cache.db", # SQLite file path
redis_url="redis://...", # only needed when storage="redis"
# TTL (prod mode only)
ttl=3600, # global TTL in seconds
ttl_by_model={"gpt-4o": 7200}, # per-model overrides, take precedence
# Usage logging
log_usage=True, # write per-call records to SQLite
log_path=".ecoai/usage.db", # usage log path
# Master switch
caching_enabled=True, # set False to disable caching entirely
# Semantic caching (off by default)
semantic=SemanticConfig(
threshold=0.95,
embedding_model="text-embedding-3-small",
),
)Environment variables
ECOAI_MODE=prod
ECOAI_STORAGE=sqlite
ECOAI_REDIS_URL=redis://localhost:6379
ECOAI_DB_PATH=.ecoai/usage.db # used by the dashboard CLI
ECOAI_DASHBOARD_PORT=7315 # dashboard port (default: 7315)Settings file
The EcoAI dashboard writes to .ecoai/settings.json. The Python SDK reads this file at startup as the lowest-priority config layer:
Priority: EcoAI(kwargs) > ECOAI_* env vars > .ecoai/settings.json > defaultsStorage backends
SQLite (default)
eco = EcoAI(client=client, storage="sqlite", sqlite_path=".ecoai/cache.db")Best for: local development, single-process production, Docker (mount .ecoai/ as a volume).
In-memory
eco = EcoAI(client=client, storage="memory")Best for: unit tests, ephemeral processes. Cache is lost when the process exits.
Redis
# Requires: pip install "ecoai-python[redis]"
eco = EcoAI(
client=client,
storage="redis",
redis_url="redis://localhost:6379",
)Best for: multi-instance production, serverless, shared cache across processes.
Dev vs Prod mode
# Dev mode — cache forever, no TTL
eco = EcoAI(client=client, mode="dev")
# Prod mode — cache expires after ttl seconds (default 3600)
eco = EcoAI(client=client, mode="prod", ttl=7200)Streaming
Streaming calls bypass the cache entirely and pass through to the provider:
# NOT cached — passed through directly
stream = eco.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Tell me a story."}],
stream=True,
)
for chunk in stream:
print(chunk.choices[0].delta.content or "", end="", flush=True)Image generation
Image generation via eco.images. Only response_format="b64_json" responses are cached. URL responses expire after ~1 hour and bypass the cache.
# Cached (b64_json)
result = eco.images.generate(
model="dall-e-3",
prompt="A photorealistic sunset over the ocean",
size="1024x1024",
quality="standard",
response_format="b64_json", # required for caching
n=1,
)
b64_data = result.data[0].b64_json
# NOT cached (url — expires after ~1 hour)
result = eco.images.generate(
model="dall-e-3",
prompt="A photorealistic sunset over the ocean",
response_format="url", # bypasses cache
)
# Image edits (DALL-E 2)
with open("original.png", "rb") as f:
edited = eco.images.edit(
image=f,
prompt="Add a lighthouse in the background",
response_format="b64_json",
)Structured output
Structured output calls (response_format={"type": "json_schema", ...}) are automatically tagged as "structured" in usage logs and the dashboard:
result = eco.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Extract the name and age."}],
response_format={
"type": "json_schema",
"json_schema": {
"name": "person",
"schema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "number"},
},
},
},
},
)Cache management
from ecoai import FlushFilter
eco.flush() # clear all cached responses
eco.flush(FlushFilter(pattern="summarise*")) # clear by prompt glob patternUsage statistics
summary = eco.summary()
print(f"Hit rate: {summary.hit_rate:.0%}")
print(f"Tokens saved: {summary.tokens_saved:,}")
print(f"Cost saved: ${summary.cost_saved_usd:.4f}")
print(f"CO₂ saved: {summary.co2_saved_g:.2f}g")
records = eco.history(limit=50)
for r in records:
status = "HIT" if r.cache_hit else "MISS"
print(f"{r.timestamp} {r.model:<30} {status} {r.tokens_saved:>6} saved")Dashboard
The Python SDK includes a standalone dashboard server — no Node.js or pnpm needed.
# Start the dashboard
python -m ecoai dashboard
# Custom database path and port
python -m ecoai dashboard --db /path/to/.ecoai/usage.db --port 8080
# Don't open browser automatically
python -m ecoai dashboard --no-browser
# If installed via pip, the ecoai script is available
ecoai dashboard --db .ecoai/usage.db
# Set port via environment variable (useful in .env files or Docker)
ECOAI_DASHBOARD_PORT=8080 python -m ecoai dashboardOpens at http://localhost:7315 by default. See Dashboard for full details.
Sharing a database between Python and JS SDKs
Both SDKs use the same SQLite schema. You can point them at the same usage.db to see all calls in one dashboard:
# In your JS project
ECOAI_DB_PATH=/shared/.ecoai/usage.db npx ecoai dashboard
# In your Python project
python -m ecoai dashboard --db /shared/.ecoai/usage.dbOr programmatically:
# Python
eco = EcoAI(client=client, log_path="/shared/.ecoai/usage.db")// TypeScript
const eco = new EcoAI({ client: openai, logPath: '/shared/.ecoai/usage.db' });