Metadata-Version: 2.4
Name: metricai
Version: 0.7.1
Summary: Python SDK for MetricAI — AI billing and metering proxy
Project-URL: Homepage, https://metricai.co.in
License: MIT
License-File: LICENSE
Keywords: ai,billing,india,llm,metering,openai,upi
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: anthropic>=0.30.0
Requires-Dist: boto3>=1.34.0
Requires-Dist: click>=8.1.0
Requires-Dist: crewai>=0.35.0; python_version < '3.14'
Requires-Dist: django>=4.2
Requires-Dist: fastapi>=0.110.0
Requires-Dist: google-genai>=1.0.0
Requires-Dist: groq>=0.9.0
Requires-Dist: grpcio>=1.60.0
Requires-Dist: httpx>=0.27.0
Requires-Dist: langchain-anthropic>=0.1.0
Requires-Dist: langchain-core>=0.2
Requires-Dist: langchain-openai>=0.1.0
Requires-Dist: langgraph>=0.1.0
Requires-Dist: llama-index-core>=0.10.0
Requires-Dist: openai>=1.0.0
Requires-Dist: perplexityai>=0.29.0
Requires-Dist: pydantic>=2.0.0
Requires-Dist: pyjwt>=2.8.0
Requires-Dist: python-dotenv>=1.0.0
Requires-Dist: starlette>=0.37.0
Requires-Dist: tavily-python>=0.3.0
Requires-Dist: typing-extensions>=4.0.0
Requires-Dist: uvicorn>=0.27.0
Requires-Dist: xai-sdk>=1.0.0
Provides-Extra: all
Requires-Dist: anthropic>=0.30.0; extra == 'all'
Requires-Dist: boto3>=1.34.0; extra == 'all'
Requires-Dist: crewai>=0.35.0; (python_version < '3.14') and extra == 'all'
Requires-Dist: deepgram-sdk>=3.0.0; extra == 'all'
Requires-Dist: django>=4.2; extra == 'all'
Requires-Dist: elevenlabs>=1.0.0; extra == 'all'
Requires-Dist: google-genai>=1.0.0; extra == 'all'
Requires-Dist: groq>=0.9.0; extra == 'all'
Requires-Dist: grpcio>=1.60.0; extra == 'all'
Requires-Dist: langchain-anthropic>=0.1.0; extra == 'all'
Requires-Dist: langchain-core>=0.2; extra == 'all'
Requires-Dist: langchain-openai>=0.1.0; extra == 'all'
Requires-Dist: langgraph>=0.1.0; extra == 'all'
Requires-Dist: livekit-agents[cartesia,deepgram,openai]>=1.0.0; extra == 'all'
Requires-Dist: livekit>=1.0.0; extra == 'all'
Requires-Dist: llama-index-core>=0.10.0; extra == 'all'
Requires-Dist: openai>=1.0.0; extra == 'all'
Requires-Dist: perplexityai>=0.29.0; extra == 'all'
Requires-Dist: python-dotenv>=1.0.0; extra == 'all'
Requires-Dist: sarvamai>=0.1.0; extra == 'all'
Requires-Dist: starlette>=0.37.0; extra == 'all'
Requires-Dist: tavily-python>=0.3.0; extra == 'all'
Requires-Dist: xai-sdk>=1.0.0; extra == 'all'
Provides-Extra: anthropic
Requires-Dist: anthropic>=0.30.0; extra == 'anthropic'
Provides-Extra: bedrock
Requires-Dist: boto3>=1.34.0; extra == 'bedrock'
Provides-Extra: crewai
Requires-Dist: crewai>=0.35.0; extra == 'crewai'
Provides-Extra: deepgram
Requires-Dist: deepgram-sdk>=3.0.0; extra == 'deepgram'
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: respx; extra == 'dev'
Provides-Extra: django
Requires-Dist: django>=4.2; extra == 'django'
Provides-Extra: elevenlabs
Requires-Dist: elevenlabs>=1.0.0; extra == 'elevenlabs'
Provides-Extra: fastapi
Requires-Dist: starlette>=0.37.0; extra == 'fastapi'
Provides-Extra: gemini
Requires-Dist: google-genai>=1.0.0; extra == 'gemini'
Provides-Extra: grok
Requires-Dist: grpcio>=1.60.0; extra == 'grok'
Requires-Dist: xai-sdk>=1.0.0; extra == 'grok'
Provides-Extra: groq
Requires-Dist: groq>=0.9.0; extra == 'groq'
Provides-Extra: langchain
Requires-Dist: langchain-anthropic>=0.1.0; extra == 'langchain'
Requires-Dist: langchain-core>=0.2; extra == 'langchain'
Requires-Dist: langchain-openai>=0.1.0; extra == 'langchain'
Provides-Extra: langgraph
Requires-Dist: langchain-core>=0.2; extra == 'langgraph'
Requires-Dist: langgraph>=0.1.0; extra == 'langgraph'
Provides-Extra: livekit
Requires-Dist: livekit>=1.0.0; extra == 'livekit'
Provides-Extra: livekit-all
Requires-Dist: livekit-agents[cartesia,deepgram,openai]>=1.0.0; extra == 'livekit-all'
Provides-Extra: llamaindex
Requires-Dist: llama-index-core>=0.10.0; extra == 'llamaindex'
Provides-Extra: openai
Requires-Dist: openai>=1.0.0; extra == 'openai'
Provides-Extra: perplexity
Requires-Dist: perplexityai>=0.29.0; extra == 'perplexity'
Provides-Extra: providers
Requires-Dist: anthropic>=0.30.0; extra == 'providers'
Requires-Dist: boto3>=1.34.0; extra == 'providers'
Requires-Dist: google-genai>=1.0.0; extra == 'providers'
Requires-Dist: groq>=0.9.0; extra == 'providers'
Requires-Dist: grpcio>=1.60.0; extra == 'providers'
Requires-Dist: openai>=1.0.0; extra == 'providers'
Requires-Dist: perplexityai>=0.29.0; extra == 'providers'
Requires-Dist: tavily-python>=0.3.0; extra == 'providers'
Requires-Dist: xai-sdk>=1.0.0; extra == 'providers'
Provides-Extra: sarvam
Requires-Dist: sarvamai>=0.1.0; extra == 'sarvam'
Provides-Extra: tavily
Requires-Dist: tavily-python>=0.3.0; extra == 'tavily'
Description-Content-Type: text/markdown

# MetricAI Python SDK

> AI billing and metering proxy for India — route LLM calls, track costs, attribute spend, and manage budgets in INR/UPI.

[![PyPI Version](https://img.shields.io/pypi/v/metricai.svg)](https://pypi.org/project/metricai/)
[![Python Versions](https://img.shields.io/pypi/pyversions/metricai)](https://pypi.org/project/metricai/)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)

---

## Features

| Capability | Description |
|------------|-------------|
| **Multi-Provider Proxy** | Route OpenAI, Anthropic, Gemini, Grok, Groq, Perplexity, and more through a single endpoint |
| **Native SDK Compatibility** | Use official provider SDKs with zero code changes via `instrument()` |
| **Per-Agent & Per-User Attribution** | Group costs by `agent_id` and `user_id` in the dashboard |
| **Budget Caps** | Hard INR caps per agent or conversation to prevent runaway spend |
| **Outcome Billing** | Charge on success/failure outcomes, not just token usage |
| **Streaming Telemetry** | Track token counts and tool usage in real-time for streaming responses |
| **Agentic Workflows** | Built-in `MetricAIAgent` and `MetricAIPipeline` with tool calling and governance |
| **Framework Integrations** | LangChain, LangGraph, CrewAI, LlamaIndex, AutoGen, FastAPI, Django |
| **BYOK Support** | Bring your own API keys — MetricAI never stores them |
| **Fail-Open** | Requests forward directly to providers if the proxy is unreachable |

---

## Installation

```bash
pip install metricai
```

### With Optional Providers

```bash
# Just OpenAI
pip install "metricai[openai]"

# Just Anthropic
pip install "metricai[anthropic]"

# All providers at once
pip install "metricai[all]"
```

Available extras: `openai`, `anthropic`, `gemini`, `groq`, `grok`, `perplexity`, `bedrock`, `tavily`, `langchain`, `langgraph`, `crewai`, `llamaindex`, `fastapi`, `django`, `deepgram`, `elevenlabs`, `sarvam`

---

## Quickstart

### 1. Set your API key

```bash
export METRICAI_API_KEY="your_metricai_api_key"
```

For BYOK mode (bring your own provider keys):

```bash
export METRICAI_API_KEY="your_metricai_api_key"
export OPENAI_API_KEY="sk-proj-..."        # Optional — for OpenAI routes
export ANTHROPIC_API_KEY="sk-ant-..."      # Optional — for Anthropic routes
export GEMINI_API_KEY="AI..."              # Optional — for Gemini routes
```

### 2. Use native SDKs via proxy

```python
from metricai import MetricAI

client = MetricAI(api_key="your_metricai_api_key")

# OpenAI via proxy
openai_client = client.openai_sdk(agent_id="support-agent", user_id="user_42")
response = openai_client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "What's the weather in Mumbai?"}]
)
print(response.choices[0].message.content)
```

### 3. Track usage and costs

```python
from metricai import MetricAI, MetricAISession, timed_call

client = MetricAI(api_key="your_metricai_api_key")

# Session-based tracking with budget cap
with MetricAISession(client, agent_id="booking-agent", user_id="user_123", max_budget_inr=50.0) as session:
    response = client.openai_sdk().chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": "Book a flight to Delhi"}]
    )
    session.track(provider="openai", model="gpt-4o-mini", input_tokens=1500, output_tokens=300)
```

### 4. Outcome billing

```python
# Simulate first to estimate cost
client.track(
    provider="openai",
    model="gpt-4o-mini",
    outcome={"success": True, "outcome_type": "booking_confirmed", "charge_on_success_inr": 2.5},
    simulate=True,
)

# Live outcome billing
client.track(
    provider="openai",
    model="gpt-4o-mini",
    outcome={"success": True, "charge_on_success_inr": 2.5},
    latency_ms=850,
)
```

### 5. Standalone agent with tools

```python
from metricai import MetricAI
from metricai.workflow import MetricAIAgent

client = MetricAI(api_key="your_metricai_api_key", llm_keys={"gemini": "your_gemini_key"})

agent = MetricAIAgent(
    client,
    agent_id="research-agent",
    user_id="user_456",
    model="gemini-2.0-flash",
    system_prompt="You are a helpful research assistant.",
    budget_cap_inr=25.0,
)

response = agent.run("What are the top 5 AI news stories today?")
print(response.content)
```

---

## Core Concepts

### Client Initialization

```python
from metricai import MetricAI, MetricAIConfig

# Simple initialization
client = MetricAI(api_key="your_metricai_api_key")

# With configuration object
config = MetricAIConfig(
    api_key="your_metricai_api_key",
    mode="byok",                        # "byok" or "managed"
    llm_keys={"openai": "sk-..."},     # Provider keys for BYOK mode
    proxy_base_url="https://proxy.metricai.co.in",
    default_billing_mode="hybrid",      # "usage", "outcome", or "hybrid"
    default_budget_cap_inr=100.0,       # Daily INR cap
    fail_open=True,                     # Forward to provider if proxy unreachable
)

client = MetricAI(config=config)
```

### Headers and Attribution

```python
# Get headers for any SDK call
headers = client.headers(
    agent_id="my-agent",
    user_id="user_123",
    session_id="session_abc",
    billing_mode="hybrid",
    budget_cap_inr=50.0,
)

# Pass headers to any HTTP client
response = openai_client.chat.completions.create(
    model="gpt-4o",
    messages=[...],
    extra_headers=headers,
)
```

### Routing

```python
from metricai import MetricAI, RoutingConfig, RoutingManager

client = MetricAI(api_key="your_metricai_api_key")

# Configure routing policy server-side
router = RoutingManager(client)
router.configure(
    agent_id="routing-agent",
    strategy="cost-optimized",
    config={"max_model": "gpt-4o-mini", "fallback_model": "gpt-3.5-turbo"}
)

# Or via config
client = MetricAI(
    api_key="your_metricai_api_key",
    routing=RoutingConfig(strategy="latency-optimized")
)
```

### Global Instrumentation

Instrument all provider SDK clients globally — no need to change existing code:

```python
from metricai import init, instrument

# Initialize once at app startup
init(api_key="your_metricai_api_key", auto_instrument=True)

# All subsequent OpenAI/Anthropic calls are automatically routed through MetricAI
from openai import OpenAI
client = OpenAI()  # Already patched — routes through MetricAI proxy
```

---

## Framework Integrations

### LangChain

```python
from langchain_openai import ChatOpenAI
from metricai.integrations.langchain import MetricAIChatCallbackHandler

handler = MetricAIChatCallbackHandler(agent_id="lc-agent", user_id="user_1")
llm = ChatOpenAI(callbacks=[handler])
response = llm.invoke("Hello!")
```

### LangGraph

```python
from langgraph.prebuilt import create_react_agent
from metricai.integrations.langgraph import MetricAIGraphTracer

tracer = MetricAIGraphTracer(agent_id="graph-agent", user_id="user_2")
agent = create_react_agent(model, tools, tracer=tracer)
```

### CrewAI

```python
from crewai import Agent
from metricai.integrations.crewai import MetricAICrewAIWrapper

wrapper = MetricAICrewAIWrapper(api_key="your_key")
agent = wrapper.wrap_agent(
    Agent(role="Researcher", goal="Find AI news", backstory="You are a researcher"),
    agent_id="crewai-researcher",
)
```

### FastAPI Middleware

```python
from fastapi import FastAPI
from metricai.integrations.web import MetricAIFastAPIMiddleware

app = FastAPI()
app.add_middleware(
    MetricAIFastAPIMiddleware,
    api_key="your_metricai_api_key",
    default_agent_id="fastapi-agent",
)
```

---

## Environment Variables

| Variable | Description | Default |
|----------|-------------|---------|
| `METRICAI_API_KEY` | Your MetricAI API key | Required |
| `METRICAI_PROXY_URL` | Proxy base URL | `https://proxy.metricai.co.in` |
| `METRICAI_API_URL` | Telemetry API URL | Same as proxy URL |
| `METRICAI_ENVIRONMENT` | `production` or `sandbox` | `production` |
| `OPENAI_API_KEY` | BYOK key for OpenAI | — |
| `ANTHROPIC_API_KEY` | BYOK key for Anthropic | — |
| `GEMINI_API_KEY` | BYOK key for Gemini | — |

---

## Supported Providers

| Provider | Proxy Path | Native SDK |
|----------|-----------|------------|
| OpenAI | `/v1/proxy/openai` | ✅ `openai` |
| Anthropic | `/v1/proxy/claude` | ✅ `anthropic` |
| Gemini | `/v1/proxy/gemini` | ✅ `google-genai` |
| Grok | `/v1/proxy/grok` | ✅ `xai-sdk` |
| Groq | `/v1/proxy/groq` | ✅ `groq` |
| Perplexity | `/v1/proxy/perplexity` | ✅ `perplexityai` |
| AWS Bedrock | `/v1/proxy/bedrock` | ✅ `boto3` |
| Tavily | `/v1/proxy/tavily` | ✅ `tavily-python` |
| Deepgram | `/v1/proxy/deepgram` | ✅ `deepgram-sdk` |
| ElevenLabs | `/v1/proxy/elevenlabs` | ✅ `elevenlabs` |
| Sarvam | `/v1/proxy/sarvam` | ✅ `sarvamai` |

---

## Error Handling

```python
from metricai import (
    MetricAI,
    MetricAIError,
    MetricAIAuthError,
    MetricAIQuotaExhaustedError,
    MetricAIRateLimitError,
    MetricAIProxyError,
)

client = MetricAI(api_key="your_metricai_api_key")

try:
    response = client.openai_sdk().chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": "Hello"}]
    )
except MetricAIAuthError:
    print("Invalid API key")
except MetricAIQuotaExhaustedError:
    print("Budget cap reached — upgrade your plan")
except MetricAIRateLimitError:
    print("Rate limited — retry after a moment")
except MetricAIProxyError as e:
    print(f"Proxy error: {e}")
except MetricAIError as e:
    print(f"Unexpected error: {e}")
```

---

## API Reference

### `MetricAI`

Main client class for all operations.

```python
MetricAI(
    api_key: str,                          # Required: your MetricAI API key
    base_url: str | None = None,           # Override proxy URL
    timeout: int = 60,                     # Request timeout in seconds
    agent_id: str | None = None,           # Default agent ID
    user_id: str | None = None,            # Default user ID
    billing_mode: str | None = None,       # "usage", "outcome", or "hybrid"
    budget_cap_inr: float | None = None,   # Daily budget cap in INR
    mode: str = "byok",                    # "byok" or "managed"
    llm_keys: dict | None = None,          # Provider API keys
    config: MetricAIConfig | None = None,  # Configuration object
)
```

**Methods:**

- `openai_sdk(...)` → Returns OpenAI-compatible client routed through proxy
- `anthropic_sdk(...)` → Returns Anthropic client routed through proxy
- `gemini_sdk(...)` → Returns Gemini client routed through proxy
- `grok_sdk(...)` → Returns Grok client routed through proxy
- `headers(...)` → Returns headers dict for any SDK call
- `track(...)` → Track a billing event
- `track_stream(...)` → Track streaming responses
- `session(...)` → Context manager for session tracking
- `get_quota_status()` → Get current billing usage

### `MetricAISession`

Session context manager for grouped tracking:

```python
with MetricAISession(
    client,
    agent_id="my-agent",
    user_id="my-user",
    max_budget_inr=50.0,
) as session:
    # Your LLM calls here
    session.track(provider="openai", model="gpt-4o", input_tokens=100, output_tokens=50)
```

### `MetricAIAgent`

Tool-capable agent with built-in governance:

```python
agent = MetricAIAgent(
    client,
    agent_id="my-agent",
    user_id="my-user",
    provider="openai",          # or "anthropic", "gemini", "grok"
    model="gpt-4o",
    system_prompt="You are a helpful assistant.",
    tools=[...],                # Optional: list of tool definitions
    max_iterations=10,
    budget_cap_inr=25.0,
    billing_mode="hybrid",
)

response = agent.run("User's request here")
print(response.content)
print(f"Tokens: {response.input_tokens} + {response.output_tokens}")
```

---

## Examples

More examples are available in the [`examples/`](examples/) directory:

- [`byok_openai_sdk.py`](examples/byok_openai_sdk.py) — Using official SDKs via proxy
- [`standalone_workflow.py`](examples/standalone_workflow.py) — Standalone agent with tools
- [`langchain_metricai_sample.py`](examples/langchain_metricai_sample.py) — LangChain integration
- [`langgraph_tracer.py`](examples/langgraph_tracer.py) — LangGraph tracing
- [`crewai_crew.py`](examples/crewai_crew.py) — CrewAI integration

---

## License

MIT License — see [LICENSE](LICENSE) for details.

---

## Support

- Documentation: [https://docs.metricai.co.in](https://docs.metricai.co.in)
- Issues: [GitHub Issues](https://github.com/your-org/metricai/issues)
- Email: support@metricai.co.in