Metadata-Version: 2.4
Name: agentcost
Version: 0.3.0
Summary: Track LLM costs across OpenAI, Anthropic, Gemini, LangChain, and 3500+ models with zero code changes
Home-page: https://agentcost.tech
Author: Kushagra Agrawal
Author-email: kushagraagrawal128@gmail.com
License: MIT
Project-URL: Homepage, https://agentcost.tech
Project-URL: Documentation, https://agentcost.tech/docs/sdk
Project-URL: Source, https://github.com/agentcost-ai/agentcost-sdk
Project-URL: Bug Tracker, https://github.com/agentcost-ai/agentcost-sdk/issues
Keywords: llm,openai,anthropic,gemini,langchain,langgraph,ai-agents,cost-tracking,tokens,observability,monitoring,llm-cost
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: tiktoken>=0.7.0
Requires-Dist: requests>=2.28.0
Provides-Extra: openai
Requires-Dist: openai>=1.0.0; extra == "openai"
Provides-Extra: anthropic
Requires-Dist: anthropic>=0.18.0; extra == "anthropic"
Provides-Extra: gemini
Requires-Dist: google-genai>=1.0.0; extra == "gemini"
Provides-Extra: langchain
Requires-Dist: langchain-core>=0.1.0; extra == "langchain"
Provides-Extra: all
Requires-Dist: openai>=1.0.0; extra == "all"
Requires-Dist: anthropic>=0.18.0; extra == "all"
Requires-Dist: google-genai>=1.0.0; extra == "all"
Requires-Dist: langchain-core>=0.1.0; extra == "all"
Provides-Extra: dev
Requires-Dist: pytest>=7.0.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
Requires-Dist: black>=23.0.0; extra == "dev"
Requires-Dist: mypy>=1.0.0; extra == "dev"
Requires-Dist: ruff>=0.1.0; extra == "dev"
Dynamic: author
Dynamic: author-email
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: home-page
Dynamic: keywords
Dynamic: license
Dynamic: license-file
Dynamic: project-url
Dynamic: provides-extra
Dynamic: requires-dist
Dynamic: requires-python
Dynamic: summary

# AgentCost SDK

**Zero-friction LLM cost tracking for OpenAI, Anthropic, Gemini, and LangChain applications.**

[AgentCost](https://agentcost.tech) is an open-source LLM cost observability platform. This is the
Python SDK — add two lines to your app and every OpenAI, Anthropic, Gemini, and LangChain call is
tracked with model, tokens, cost, and latency, attributed to the agent that made it.
Docs: [agentcost.tech/docs/sdk](https://agentcost.tech/docs/sdk) ·
Project overview: [github.com/agentcost-ai/agentcost](https://github.com/agentcost-ai/agentcost)

## Installation

```bash
pip install agentcost
```

Or install from source:

```bash
cd agentcost-sdk
pip install -e .
```

## Quick Start

```python
from agentcost import track_costs

# 2 lines to add cost tracking!
track_costs.init(api_key="your_api_key", project_id="my-project")

# OpenAI — automatically tracked
from openai import OpenAI
client = OpenAI()
response = client.chat.completions.create(model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}])

# Anthropic — automatically tracked
from anthropic import Anthropic
client = Anthropic()
message = client.messages.create(model="claude-3-5-sonnet-20241022", max_tokens=100, messages=[{"role": "user", "content": "Hello!"}])

# Gemini — automatically tracked (Google Gen AI SDK)
from google import genai
client = genai.Client()
response = client.models.generate_content(model="gemini-2.5-flash", contents="Hello!")

# LangChain — automatically tracked
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4")
response = llm.invoke("Hello!")
```

## Features

- **Zero Code Changes**: Monkey patches OpenAI, Anthropic, Gemini, and LangChain — your code works as-is
- **Automatic Tracking**: Captures `create()` (streaming and not), `parse()`, the OpenAI Responses API, Anthropic's `messages.stream()`, and LangChain `invoke()`/`ainvoke()`/`stream()`/`astream()` — sync and async
- **Accurate Tokens**: Uses provider-reported usage when available (including Gemini); estimates only when an SDK does not return usage
- **Real-Time Costs**: Calculates costs using up-to-date model pricing
- **Batched Sending**: Efficient network usage (size-based + time-based batching)
- **Rate Limiting**: Built-in rate limiter to protect your backend
- **Workflow Tracing**: Group a multi-step run and get cost per run, per step and per tool
- **Coding Agents**: `agentcost connect claude-code` or `codex` tracks every session on the machine, with no code at all
- **Pre-deployment Analysis**: `agentcost analyze` estimates cost and finds loops before you ship — entirely offline
- **Local Mode**: Test without a backend

## Configuration

```python
track_costs.init(
    # Required for cloud mode
    api_key="sk_...",
    project_id="my-project",

    # Optional settings
    base_url="https://api.agentcost.tech",   # Your backend URL
    batch_size=10,                          # Events before auto-flush
    flush_interval=5.0,                     # Seconds between flushes
    debug=True,                             # Enable debug logging
    default_agent_name="my-agent",          # Default agent tag
    local_mode=False,                       # Store locally (no backend)
    enabled=True,                           # Enable/disable tracking

    # Custom pricing (overrides defaults)
    custom_pricing={
        "my-custom-model": {"input": 0.001, "output": 0.002}
    },

    # Global metadata (attached to all events)
    global_metadata={
        "environment": "production",
        "version": "1.0.0"
    }
)
```

## Agent Tagging

Tag LLM calls by agent for granular analytics:

```python
# Option 1: Set default agent
track_costs.set_agent_name("router-agent")

# Option 2: Context manager (recommended)
with track_costs.agent("technical-agent"):
    llm.invoke("How do I fix this?")  # Tagged as "technical-agent"

with track_costs.agent("billing-agent"):
    llm.invoke("What's my balance?")  # Tagged as "billing-agent"
```

## Workflows & Steps

Agent tagging answers *which agent* spent the money. Wrapping a multi-step run
answers *what one run costs*, which step dominates it, and whether it loops:

```python
with track_costs.workflow("support-triage"):

    with track_costs.step("classify"):
        llm.invoke("Which queue does this belong in?")

    with track_costs.tool("search_docs"):
        llm.invoke("Summarise these results")

    with track_costs.step("draft_reply"):
        llm.invoke("Write the response")
```

Every call inside shares one trace id and records its step, its parent and how
deeply it was nested. A nested `workflow()` joins the enclosing run rather than
starting a second one, and `step()` outside a workflow is a no-op — so
instrumenting a shared helper never depends on how it is called.

Entirely optional and entirely additive: without a `workflow()` your events are
exactly what they were before.

### Outcomes

Mark how a run ended and you get cost per completed outcome, which charges
failed runs to the successes they were paid for:

```python
with track_costs.workflow("support-triage"):
    ticket = handle(request)
    track_costs.outcome(ticket.resolved, label=ticket.status)
```

Sent once per run when the workflow closes, so a late failure overwrites an
earlier optimistic call.

## Pre-deployment Analysis

Estimate what an agent will cost, and find its loops, before it has spent
anything. The `agentcost` CLI ships with the package and runs entirely on your
machine — no network call, and no file content outlives the token count taken
from it.

```bash
# What do the prompt and skill files cost on every call?
agentcost analyze ./agent --model gpt-4o
```

For a cost-per-run figure, record one representative run in local mode and
project it to production volume:

```python
import json
from agentcost import track_costs

track_costs.init(local_mode=True)

with track_costs.workflow("support-triage"):
    run_agent(sample_request)

track_costs.flush()
json.dump(track_costs.get_local_events(), open("run.json", "w"))
```

```bash
agentcost analyze ./agent --events run.json --runs-per-day 2000
```

```
Prompt and skill files  (gpt-4o)
  3 file(s), 8,163 tokens, $0.020407 per call just to send them

Test run
  3 run(s), 4.0 calls per run, $0.044000 per run (worst $0.044000)
    $  0.022000  50.0%   2.0 calls  search_docs
    $  0.020000  45.5%   1.0 calls  draft_reply
    $  0.002000   4.5%   1.0 calls  classify

Projected at 2,000 runs/day: $2,640.00 per month

Findings (3)
  [  high] Step 'search_docs' ran 2.0 times per run; a loop or retry will multiply this in production
  [  high] 3 of 3 run(s) made the same call more than once (worst: 2x); the repeats are avoidable
  [medium] 2 files have identical content; sending both pays twice for the same context
```

| Flag | Purpose |
|---|---|
| `--model` | Model to price against (default `gpt-4o`) |
| `--events` | Events from a local-mode run (JSON array or JSONL) |
| `--runs-per-day` | Expected volume, to project a monthly cost |
| `--pattern` | Glob to include; repeatable (defaults cover prompt and doc files) |
| `--json` | Also write the full report as JSON |
| `--fail-on` | Exit non-zero on a finding at or above this severity |

Use `--fail-on high` in CI to block a deploy on a cost regression:

```bash
agentcost analyze ./agent --events run.json --fail-on high
```

## Coding Agents: Claude Code, Codex and Antigravity

These agents already log every model call on the machine they run on. Connect
one and its sessions appear in AgentCost with no instrumentation:

```bash
pip install agentcost
agentcost connect claude-code --api-key sk_...
agentcost connect codex
agentcost connect antigravity
```

`connect` imports the last 30 days and installs a hook, so each finished turn is
sent as it happens — from any folder, in the terminal, the IDE extension or the
desktop app. It is per machine: run it once on each one you work from. Codex
asks you to trust a new hook, so open Codex and run `/hooks` once.

**Antigravity** has no usage export and no hooks. Its figures are read from the
undocumented per-conversation database it keeps, so they are marked
`estimated`, carry no tool attribution, and are sent whenever another connected
agent syncs (or when you run `agentcost sync` on a schedule). Conversations from
before June 2026 are encrypted and cannot be read.

Look before you send anything — this needs no API key and no account:

```bash
agentcost sync --dry-run
```

```
Claude Code: would send 4,059 calls from 12 session(s), 1,915,622,519 tokens, $829.89 at API rates
    $    524.00   3,227 calls  claude-opus-5-5
    $    213.11     525 calls  claude-fable-5-1
    Covered by a subscription: this is what the usage is worth, not a bill.
```

What you get in the dashboard:

| AgentCost | Coding agent |
|---|---|
| Run (`trace_id`) | One session |
| Workflow | `claude-code` or `codex` |
| Agent | The main thread, or `claude-code/<subagent type>` |
| Tool | The tool each call invoked — `Bash`, `Edit`, an MCP tool |
| Session, user | `session_id`, and `--user` if you set one |

**What leaves the machine:** token counts, model, tool names, session id and
timestamps. Prompts, replies, tool arguments and tool output are never sent or
kept. Codex wraps each tool call in a short script;
that script is scanned for the tool name and nothing else is taken from it.
The working folder's name and git branch are sent only with `--include-repo`,
and never the path or the remote URL.

**Subscriptions.** On a Claude or ChatGPT plan nothing is billed per token, so
the dollar figure is what the usage would cost at API rates. Each event says
which it is in `metadata.billing` (`subscription` or `api`), detected
automatically; `--billing` overrides it.

### Enforcing guardrails

Tracking tells you what happened. `--enforce` stops it before it happens:

```bash
agentcost connect claude-code --enforce
```

This adds a second hook, the gate, that runs before every tool call and every
prompt and refuses the ones the project does not allow. It enforces the
guardrail you declare in the dashboard for the agent named `claude-code` or
`codex` (and `claude-code/<subagent type>`, which falls back to the main one):

| Boundary | Effect |
|---|---|
| Allowed tools | A tool not on the list is refused |
| Read-only | A tool tagged as writing is refused |
| Allowed models | Tool calls and prompts are refused while the session is on a model not on the list |
| Max tool calls per run | Further tool calls in that session are refused |
| Max cost per run | Further tool calls and prompts in that session are refused |
| Monthly budget, hard cap | New prompts are refused once it is spent — metered usage only, never a subscription |

The agent is told why, in words it can act on:

```
AgentCost guardrail: Write is not among the tools this project allows claude-code to use.
```

The gate never calls the network. It decides from the policy the last sync
fetched and from the session's own log, so its totals are current to the call
being judged, and it adds about a fifth of a second to a tool call. A guardrail
changed in the dashboard applies from the next turn. If the gate cannot decide —
no policy yet, a file it cannot read — it allows the call: it must never be the
reason work stops.

All of it applies to Claude Code and to Codex. Codex's tools are reported under
the names its hooks use (its shell is `Bash`, file edits are `apply_patch`), so a
boundary written from what the dashboard shows is enforced as written. Per-run
limits are judged on the session's main thread. A model boundary takes effect
from a session's second prompt in Claude Code, which does not name the model
before its first call. Antigravity has no hooks and is never enforced.

The hooks never hold up a session: the sync hook starts the sync in a process of
its own and returns, so the turn is sent even when the agent exits straight
after it, as a headless run does.

| Command | Purpose |
|---|---|
| `agentcost` | List every command and what it does |
| `agentcost connect <agent>` | Start tracking. `--enforce`, `--backfill-days`, `--include-repo`, `--user`, `--billing`, `--base-url`, `--no-hook` |
| `agentcost sync` | Send what is new. `--dry-run` shows it instead |
| `agentcost status` | What is connected and what is waiting |
| `agentcost disconnect <agent>` | Remove the hook; data already sent stays |

A sync that is interrupted, repeated or run from several sessions at once never
loses a call or counts one twice: each log has a cursor that moves only after
the server accepted what came before it, and every call carries the provider's
own id as its `event_id`. Without the hook (`--no-hook`), run `agentcost sync`
yourself or on a schedule.

### Without installing anything: OpenTelemetry

Claude Code and Codex can export their own telemetry, and the AgentCost backend
accepts it at `/v1/otlp`. Set it once in managed settings and every session in
an organisation reports in, including ones that run in the cloud and leave no
local log. Claude Code:

```bash
CLAUDE_CODE_ENABLE_TELEMETRY=1
OTEL_LOGS_EXPORTER=otlp
OTEL_METRICS_EXPORTER=none
OTEL_EXPORTER_OTLP_PROTOCOL=http/json
OTEL_EXPORTER_OTLP_ENDPOINT=https://api.agentcost.tech/v1/otlp
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer sk_..."
OTEL_RESOURCE_ATTRIBUTES="agentcost.billing=subscription"   # or api
```

Only the per-call usage records are read; everything else in the export is
discarded unread. A machine that also runs `agentcost connect` is counted once,
because both paths identify a call by its API request id. This path tracks
only — enforcement needs the hook.

## Metadata

Attach custom metadata for filtering/grouping:

```python
# Persistent metadata
track_costs.add_metadata("user_id", "user_123")
track_costs.add_metadata("tenant_id", "acme_corp")

# Temporary metadata (context manager)
with track_costs.metadata(conversation_id="conv_456", step="routing"):
    llm.invoke("Route this query")
```

## Local Testing

Test without running a backend:

```python
track_costs.init(local_mode=True, debug=True)

# Make LLM calls
llm.invoke("Hello!")
llm.invoke("World!")

# Retrieve captured events
events = track_costs.get_local_events()
for event in events:
    print(f"Model: {event['model']}")
    print(f"Tokens: {event['total_tokens']}")
    print(f"Cost: ${event['cost']:.6f}")
```

## Streaming Support

Streaming calls are automatically tracked:

```python
# Sync streaming
for chunk in llm.stream("Tell me a story"):
    print(chunk.content, end="")
# Event recorded after stream completes

# Async streaming
async for chunk in llm.astream("Tell me a story"):
    print(chunk.content, end="")
# Event recorded after stream completes
```

## Event Structure

Each tracked event contains:

```python
{
    "agent_name": "my-agent",
    "model": "gpt-4",
    "input_tokens": 150,
    "output_tokens": 80,
    "total_tokens": 230,
    "cost": 0.0093,            # USD, real-time calculated
    "latency_ms": 1234,        # Measured latency
    "timestamp": "2026-01-23T10:30:45.123Z",
    "success": True,
    "error": None,
    "streaming": False,
    "metadata": {"conversation_id": "conv_456"}
}
```

## Dynamic Pricing (Real-Time Updates)

The SDK automatically fetches the latest pricing from the backend. This means:

- **No code changes** when model prices change
- Pricing is **cached for 24 hours** (efficient)
- Falls back to built-in defaults if backend is unavailable

### How It Works

```python
# SDK automatically fetches pricing from backend
track_costs.init(
    api_key="...",
    project_id="...",
    base_url="http://localhost:8000",  # If running locally
)

# Prices are fetched once and cached
# GET http://localhost:8000/v1/pricing → {"pricing": {"gpt-4": {"input": 0.03, ...}}}
```

### Manually Update Pricing

```python
from agentcost.cost_calculator import refresh_pricing, update_pricing

# Force refresh from backend
refresh_pricing()

# Or manually set pricing (doesn't require backend)
update_pricing({
    "my-custom-model": {"input": 0.001, "output": 0.002}
})
```

### Backend Pricing API

```bash
# Get all pricing
curl http://localhost:8000/v1/pricing

# Get specific model
curl http://localhost:8000/v1/pricing/gpt-4

# Update pricing (admin)
curl -X POST http://localhost:8000/v1/pricing \
  -H "Content-Type: application/json" \
  -d '{"gpt-4": {"input": 0.025, "output": 0.05}}'
```

## Pricing Coverage (3500+ models)

Costs are priced for 3500+ models across 45+ providers, synced from the backend and updated automatically when pricing changes.

Pricing coverage is not the same as automatic tracking. Calls are intercepted for the four integrations shown in the Quick Start — **OpenAI, Anthropic, Gemini and LangChain** (which also covers LangGraph and CrewAI when they run through LangChain). Calling another provider's SDK directly produces no events; route it through LangChain, or report the usage yourself. The table below is what the SDK can put a price on.

| Provider         | Models                                                              |
| ---------------- | ------------------------------------------------------------------- |
| OpenAI           | gpt-4, gpt-4-turbo, gpt-4o, gpt-4o-mini, gpt-3.5-turbo, o1, o1-mini |
| Anthropic        | claude-3-opus/sonnet/haiku, claude-3.5-sonnet/haiku, claude-4-opus  |
| Google           | gemini-pro, gemini-1.5-pro/flash, gemini-2.0-flash/flash-lite, gemini-2.5-pro/flash/flash-lite, gemini-3-pro-preview |
| Groq             | llama-3.1-8b/70b, llama-3.2-3b, llama-3.3-70b, mixtral-8x7b         |
| DeepSeek         | deepseek-chat, deepseek-coder, deepseek-reasoner                    |
| Cohere           | command, command-light, command-r, command-r-plus                   |
| Mistral          | mistral-small/medium/large                                          |
| Together AI      | llama-3-70b/8b-chat, meta-llama models                              |
| Replicate        | Various open-source models                                          |
| OpenRouter       | Aggregated models from multiple providers                           |
| Perplexity       | pplx models                                                         |
| xAI              | Grok models                                                         |
| Amazon           | Amazon Nova, Titan models                                           |
| Azure            | Azure OpenAI models                                                 |
| AWS              | Bedrock models (Claude, Llama, Mistral)                             |
| Anyscale         | Anyscale endpoints                                                  |
| Cerebras         | Cerebras models                                                     |
| Cloudflare       | Workers AI models                                                   |
| Databricks       | DBRX, Meta Llama models                                             |
| DeepInfra        | Various hosted models                                               |
| Fireworks        | Fireworks AI models                                                 |
| Hyperbolic       | Hyperbolic models                                                   |
| Jina AI          | Embedding models                                                    |
| Lambda           | Lambda models                                                       |
| MiniMax          | MiniMax models                                                      |
| Moonshot         | Moonshot models                                                     |
| Sambanova        | Samba models                                                        |
| Voyage           | Embedding models                                                    |
| IBM              | watsonx models                                                      |
| AI21             | AI21 Labs models                                                    |
| Aleph Alpha      | Aleph Alpha models                                                  |
| Novita           | Novita hosted models                                                |
| Gradient AI      | Gradient endpoints                                                  |
| Dashscope        | Dashscope models (Alibaba)                                          |
| Friendliai       | Friendliai models                                                   |
| GMI              | GMI models                                                          |
| Llamagate        | Llamagate models                                                    |
| Morph            | Morph models                                                        |
| NLP Cloud        | NLP Cloud endpoints                                                 |
| Nscale           | Nscale models                                                       |
| Oracle           | OCI generative models                                               |
| OVHCloud         | OVHCloud models                                                     |
| Vercel           | Vercel AI Gateway, v0 models                                        |
| Weights & Biases | Wandb models                                                        |
| Zai              | Zai models                                                          |

**Note**: The full list of 3500+ models is dynamically loaded from the backend. Run `track_costs.init()` with a valid API key to access all supported models.

## Statistics

```python
stats = track_costs.get_stats()
print(f"Events sent: {stats['batcher']['events_sent']}")
print(f"Batches sent: {stats['batcher']['batches_sent']}")
```

## Graceful Shutdown

```python
track_costs.flush()     # Send pending events
track_costs.shutdown()  # Full shutdown
```

## License

MIT License
