Metadata-Version: 2.4
Name: costimizer
Version: 0.1.8
Summary: Costimizer AI observability SDK for LLM FinOps
Author-email: Costimizer <itadmin@costimizer.ai>
License: MIT
Project-URL: Homepage, https://costimizer.ai
Project-URL: Documentation, https://costimizer.ai
Project-URL: Repository, https://gitlab.bigohtech.com/costimizer/finops/finops-ai-sdk
Project-URL: Issues, https://gitlab.bigohtech.com/costimizer/finops/finops-ai-sdk/-/issues
Keywords: llm,finops,observability,openai,anthropic,gemini,openrouter,cost-tracking
Classifier: Development Status :: 4 - Beta
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.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx>=0.28.0
Provides-Extra: openai
Requires-Dist: openai>=1.60.0; extra == "openai"
Provides-Extra: openrouter
Requires-Dist: openai>=1.60.0; extra == "openrouter"
Provides-Extra: anthropic
Requires-Dist: anthropic>=0.40.0; extra == "anthropic"
Provides-Extra: gemini
Requires-Dist: google-genai>=1.0.0; extra == "gemini"
Provides-Extra: google
Requires-Dist: google-genai>=1.0.0; extra == "google"
Provides-Extra: langchain
Requires-Dist: langchain-core>=0.2.0; extra == "langchain"
Provides-Extra: otel
Requires-Dist: opentelemetry-api>=1.27.0; extra == "otel"
Requires-Dist: opentelemetry-sdk>=1.27.0; extra == "otel"
Provides-Extra: otel-langchain
Requires-Dist: costimizer[otel]; extra == "otel-langchain"
Requires-Dist: opentelemetry-instrumentation-langchain>=0.48b0; extra == "otel-langchain"
Provides-Extra: otel-openai
Requires-Dist: costimizer[otel]; extra == "otel-openai"
Requires-Dist: opentelemetry-instrumentation-openai-v2>=2.0b0; extra == "otel-openai"
Provides-Extra: all
Requires-Dist: openai>=1.60.0; extra == "all"
Requires-Dist: anthropic>=0.40.0; extra == "all"
Requires-Dist: google-genai>=1.0.0; extra == "all"
Requires-Dist: langchain-core>=0.2.0; extra == "all"
Requires-Dist: opentelemetry-api>=1.27.0; extra == "all"
Requires-Dist: opentelemetry-sdk>=1.27.0; extra == "all"
Requires-Dist: opentelemetry-instrumentation-langchain>=0.48b0; extra == "all"
Requires-Dist: opentelemetry-instrumentation-openai-v2>=2.0b0; extra == "all"
Provides-Extra: dev
Requires-Dist: pytest>=8.3.0; extra == "dev"
Requires-Dist: build>=1.2.0; extra == "dev"
Requires-Dist: twine>=5.1.0; extra == "dev"
Requires-Dist: opentelemetry-api>=1.27.0; extra == "dev"
Dynamic: license-file

# Costimizer Python SDK

[![PyPI](https://img.shields.io/pypi/v/costimizer.svg)](https://pypi.org/project/costimizer/)

Capture LLM calls and send them to Costimizer FinOps.

Install only the provider client you use

```bash
pip install "costimizer[openai]"
pip install "costimizer[openrouter]"
pip install "costimizer[otel-langchain]"   # LangChain auto-instrument
pip install "costimizer[otel-openai]"      # native OpenAI auto-instrument
```

Core SDK (`httpx` only) installs with:

```bash
pip install costimizer
```

## OpenAI

```bash
pip install "costimizer[openai]"
```

```python
from costimizer import Costimizer
from costimizer.ai.openai import OpenAI

costimizer = Costimizer(
    project_token="fo_ingest_your_key",
    host="https://api.costimizer.ai",
)

client = OpenAI(
    api_key="sk-...",
    costimizer_client=costimizer,
)

response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "Hello"}],
    costimizer_trace_name="support-chat",
)
costimizer.shutdown()
```

## OpenRouter

OpenRouter uses the OpenAI-compatible API, so it installs the `openai` package:

```bash
pip install "costimizer[openrouter]"
```

```python
from costimizer import Costimizer
from costimizer.ai.openrouter import OpenRouter

costimizer = Costimizer(project_token="fo_ingest_your_key")

client = OpenRouter(
    api_key="sk-or-...",
    costimizer_client=costimizer,
)

response = client.chat.completions.create(
    model="anthropic/claude-3.5-sonnet",
    messages=[{"role": "user", "content": "Hello"}],
    costimizer_trace_name="router-chat",
)
costimizer.shutdown()
```

## LangChain (callback — OpenRouter / LangGraph)

Use this when your app calls models through **OpenRouter** (or any LangChain chat model) and you pass callbacks manually — e.g. LangGraph agents with many parallel LLM steps.

```bash
pip install "costimizer[langchain]"
```

**Ingest key:** create with `provider: "openrouter"` so it matches every generation event.

```python
import os
from costimizer import Costimizer
from costimizer.langchain import CostimizerCallbackHandler

costimizer = Costimizer(
    project_token=os.environ["COSTIMIZER_PROJECT_TOKEN"],
    host=os.environ.get("COSTIMIZER_HOST", "https://stgfinops.costimizer.ai"),
    debug=True,
)

callbacks = [
    CostimizerCallbackHandler(
        costimizer,
        trace_name="costimizer-support-chat",
        distinct_id="user-123",
        provider="openrouter",
        base_url="https://openrouter.ai/api/v1/",
    )
]

# Pass the same callbacks list into every LangChain model factory / invoke:
# model_handler.get_sonnet(callbacks=callbacks)
# model_handler.get_haiku(callbacks=callbacks)

# Flush queued events when the agent turn finishes:
costimizer.shutdown()
```

| Env var | Purpose |
| --- | --- |
| `COSTIMIZER_PROJECT_TOKEN` | Ingest key (`fo_ingest_...`) |
| `COSTIMIZER_HOST` | FinOps API base URL |
| `COSTIMIZER_PROVIDER` | Optional default ingest provider (`openrouter`) |
| `COSTIMIZER_BASE_URL` | Optional default LLM base URL on events |
| `COSTIMIZER_DEBUG` | Log ingest success/failure to stderr |

The callback tracks each LangChain `run_id` separately, so parallel `asyncio.gather` LLM calls in LangGraph do not overwrite each other's input/output.

## LangChain (callback — OpenAI)

```bash
pip install "costimizer[langchain]"
```

```python
from costimizer import Costimizer
from costimizer.langchain import CostimizerCallbackHandler
from langchain_openai import ChatOpenAI

costimizer = Costimizer(project_token="fo_ingest_your_key")
handler = CostimizerCallbackHandler(
    costimizer,
    trace_name="support-chat",
    distinct_id="user-123",
    provider="openai",
    base_url="https://api.openai.com/v1/",
)

model = ChatOpenAI(model="gpt-4o-mini", temperature=0.7, seed=42)
model.invoke("Hello", config={"callbacks": [handler]})
costimizer.shutdown()
```

## OpenTelemetry (native OpenAI)

```bash
pip install "costimizer[otel-openai]"
```

```python
from costimizer import Costimizer
from costimizer.otel import instrument
import openai

costimizer = Costimizer(project_token="fo_ingest_your_key")
instrument(costimizer, openai=True, trace_name="support-chat")

client = openai.OpenAI()
client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "Hello"}],
)
costimizer.shutdown()
```

## Optional tracking fields

All provider wrappers accept the same Costimizer-only kwargs:

| Kwarg                     | Purpose                                         |
| ------------------------- | ----------------------------------------------- |
| `costimizer_trace_name`   | Label for dashboards                            |
| `costimizer_trace_id`     | Group related calls                             |
| `costimizer_distinct_id`  | User or session ID                              |
| `costimizer_properties`   | Custom metadata dict                            |
| `costimizer_privacy_mode` | `"metadata_only"` (default) or `"full_content"` |
