revenium-middleware-langchain 0.1.1
Revenium middleware for LangChain - AI metering and usage tracking
License: MIT
Requires Python: >=3.9
Keywords: langchain, revenium, metering, ai, llm, middleware, callback, tracing

# Revenium Middleware for LangChain

[![PyPI version](https://img.shields.io/pypi/v/revenium-middleware-langchain.svg)](https://pypi.org/project/revenium-middleware-langchain/)
[![Python versions](https://img.shields.io/pypi/pyversions/revenium-middleware-langchain.svg)](https://pypi.org/project/revenium-middleware-langchain/)
[![Documentation](https://img.shields.io/badge/docs-revenium.io-blue)](https://docs.revenium.io)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

A LangChain callback handler that sends metering data to Revenium's AI metering API. This middleware automatically tracks LLM calls (tokens, timing, model info), chains, tools, and agent actions.

## What Gets Metered

The callback handler automatically captures:

- **Token counts**: Input tokens, output tokens, total tokens
- **Timing**: Request start time, response time, duration in milliseconds
- **Model info**: Model name, provider, stop reason
- **Trace context**: Transaction ID, trace ID, parent transaction ID
- **Metadata**: Agent name, environment, organization, etc.
- **Prompt capture** (optional): Input prompts and output responses

## Supported LLM Providers

The middleware automatically detects and tags the provider for:

| Provider | LangChain Classes |
|----------|-------------------|
| OpenAI | `ChatOpenAI`, `OpenAI` |
| Anthropic | `ChatAnthropic` |
| Google | `ChatGoogleGenAI`, `ChatVertexAI` |
| AWS Bedrock | `ChatBedrock`, `BedrockLLM` |
| Azure OpenAI | `AzureChatOpenAI` |
| Cohere | `ChatCohere` |
| HuggingFace | `ChatHuggingFace` |
| Ollama | `ChatOllama`, `Ollama` |

Provider is also auto-detected from model names: `gpt-*` -> openai, `claude-*` -> anthropic, `gemini-*` -> google.

## Requirements

- Python 3.9+
- `langchain-core >= 0.2.0`
- `revenium_middleware >= 0.3.4`
- A Revenium API key (starts with `hak_`)
- At least one LLM provider SDK installed (e.g., `langchain-openai`, `langchain-anthropic`)

## Getting Started

### 1. Create a project directory

```bash
mkdir my-langchain-project
cd my-langchain-project
```

### 2. Create and activate a virtual environment

```bash
python -m venv .venv
source .venv/bin/activate    # On macOS/Linux
# .venv\Scripts\activate     # On Windows
```

### 3. Install the package

```bash
pip install revenium-middleware-langchain
```

### 4. Install your LLM provider

```bash
pip install langchain-openai       # For OpenAI / Azure OpenAI
pip install langchain-anthropic    # For Anthropic
pip install langchain-google-genai # For Google Gemini
pip install langgraph              # For agents
```

### 5. Configure environment variables

```bash
export REVENIUM_METERING_API_KEY=hak_your_api_key_here
export OPENAI_API_KEY=sk-your_openai_key_here    # Or your provider's key
```

Or copy the `.env.example` file:

```bash
cp .env.example .env
# Edit .env with your actual keys
```

## Quick Start

```python
from langchain_openai import ChatOpenAI
from revenium_middleware_langchain import ReveniumCallbackHandler

# Create the callback handler (uses REVENIUM_METERING_API_KEY from environment)
handler = ReveniumCallbackHandler(
    trace_id="session-123",
    agent_name="support_agent"
)

# Use with any LangChain LLM
llm = ChatOpenAI(model="gpt-4", callbacks=[handler])
response = llm.invoke("Hello!")
```

## Configuration

### Environment Variables

| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `REVENIUM_METERING_API_KEY` | Yes | - | Your Revenium API key (must start with `hak_`) |
| `REVENIUM_METERING_BASE_URL` | No | `https://api.revenium.ai` | Revenium API base URL |
| `REVENIUM_LOG_LEVEL` | No | `INFO` | Log level (`DEBUG`, `INFO`, `WARNING`, `ERROR`) |
| `REVENIUM_CAPTURE_PROMPTS` | No | `false` | Capture prompts and responses (use with caution) |
| `REVENIUM_ENVIRONMENT` | No | - | Environment name (e.g., `production`, `staging`) |
| `REVENIUM_ORGANIZATION_NAME` | No | - | Organization name for metering |
| `REVENIUM_SUBSCRIPTION_ID` | No | - | Subscription ID for metering |
| `REVENIUM_PRODUCT_NAME` | No | - | Product name for metering |
| `REVENIUM_SUBSCRIBER_ID` | No | - | Subscriber ID |
| `REVENIUM_SUBSCRIBER_EMAIL` | No | - | Subscriber email |
| `REVENIUM_SUBSCRIBER_CREDENTIAL` | No | - | Subscriber credential |

See `.env.example` for a complete reference with all configuration options including trace visualization fields.

### Programmatic Configuration

You can also configure the middleware programmatically:

```python
from revenium_middleware_langchain import ReveniumCallbackHandler, ReveniumConfig, SubscriberConfig

config = ReveniumConfig(
    api_key="hak_your_api_key",
    base_url="https://api.revenium.ai",
    environment="production",
    organization_name="my_org",
    subscription_id="sub_123",
    product_name="my_product",
    subscriber=SubscriberConfig(
        id="user_123",
        email="user@example.com",
    ),
    debug=True,
    log_prompts=False,
)

handler = ReveniumCallbackHandler(
    config=config,
    trace_id="session-123",
    trace_name="my_workflow",
    agent_name="my_agent",
)
```

## Usage Examples

### Basic LLM Usage

```python
from langchain_openai import ChatOpenAI
from revenium_middleware_langchain import ReveniumCallbackHandler

handler = ReveniumCallbackHandler(trace_id="session-123")
llm = ChatOpenAI(model="gpt-4", callbacks=[handler])

response = llm.invoke("What is the capital of France?")
print(response.content)
```

### With Chains

```python
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from revenium_middleware_langchain import ReveniumCallbackHandler

handler = ReveniumCallbackHandler(trace_id="chain-example")

prompt = ChatPromptTemplate.from_template("Tell me a joke about {topic}")
llm = ChatOpenAI(model="gpt-4", callbacks=[handler])
output_parser = StrOutputParser()

chain = prompt | llm | output_parser
result = chain.invoke({"topic": "programming"})
```

### With Agents

```python
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
from langchain_core.messages import HumanMessage
from langgraph.prebuilt import create_react_agent
from revenium_middleware_langchain import ReveniumCallbackHandler

@tool
def get_weather(city: str) -> str:
    """Get the weather for a city."""
    return f"Weather in {city}: Sunny, 72F"

handler = ReveniumCallbackHandler(
    trace_id="agent-session",
    agent_name="weather_agent"
)

llm = ChatOpenAI(model="gpt-4", callbacks=[handler])
agent = create_react_agent(llm, [get_weather])

result = agent.invoke(
    {"messages": [HumanMessage(content="What's the weather in New York?")]},
    config={"callbacks": [handler]},
)
```

### Async Usage

For async applications, use the `AsyncReveniumCallbackHandler`:

```python
from langchain_openai import ChatOpenAI
from revenium_middleware_langchain import AsyncReveniumCallbackHandler

handler = AsyncReveniumCallbackHandler(trace_id="async-session")
llm = ChatOpenAI(model="gpt-4", callbacks=[handler])

# Works with async invoke
response = await llm.ainvoke("Hello!")
```

See the [examples/](examples/) directory for complete runnable examples.

## Prompt Capture

The middleware can optionally capture prompts and responses for analytics and debugging.

**Enable via environment variable:**
```bash
REVENIUM_CAPTURE_PROMPTS=true
```

**Or via configuration:**
```python
config = ReveniumConfig(
    api_key="hak_your_key",
    capture_prompts=True,
)
```

> **Security Warning:** Prompts may contain sensitive data. Only enable prompt capture in trusted environments. All captured data is encrypted at rest in Revenium.

When enabled, the middleware captures:
- System prompts and user messages sent to the LLM
- The full response content from the LLM
- These are included in the metering payload for analysis in the Revenium dashboard

## Logging Configuration

The middleware uses Python's standard `logging` module. Configure the log level to control output verbosity:

```bash
# Set via environment variable
export REVENIUM_LOG_LEVEL=DEBUG    # DEBUG, INFO, WARNING, ERROR, CRITICAL
```

**Debug mode** provides detailed output of:
- Metering payloads being built and submitted
- Provider and model detection results
- Token count extraction details
- Trace context management operations
- API submission results

```bash
# Enable debug logging
export REVENIUM_LOG_LEVEL=DEBUG
```

You can also enable debug logging programmatically:

```python
config = ReveniumConfig(
    api_key="hak_your_key",
    log_level="DEBUG",
)
```

## Troubleshooting

### Common Issues

| Problem | Solution |
|---------|----------|
| `ValueError: API key must start with 'hak_'` | Check that your `REVENIUM_METERING_API_KEY` is correct and starts with `hak_` |
| No metering data in Revenium dashboard | Set `REVENIUM_LOG_LEVEL=DEBUG` to see what's being sent |
| Provider shows as "unknown" | Ensure you're using a supported LangChain LLM class (see table above) |
| Token counts are 0 or missing | Some providers don't return token counts for all operations; verify with debug logging |
| `ModuleNotFoundError: langchain_core` | Run `pip install langchain-core>=0.2.0` |
| `ModuleNotFoundError: langchain_openai` | Run `pip install langchain-openai` (or your provider's package) |

### Debug Mode

When troubleshooting, enable debug logging to see the full metering payload:

```bash
export REVENIUM_LOG_LEVEL=DEBUG
```

This will log:
1. When each callback event fires (on_llm_start, on_llm_end, etc.)
2. The extracted provider, model, and token counts
3. The full metering payload being sent to Revenium
4. The API response status

### Provider Detection

The middleware detects providers in two ways:

1. **By LLM class name** - Maps class names like `ChatOpenAI` -> `openai`, `ChatAnthropic` -> `anthropic`
2. **By model name prefix** - Maps prefixes like `gpt-*` -> `openai`, `claude-*` -> `anthropic`, `gemini-*` -> `google`

If your provider is showing as "unknown", check that:
- You're using a supported LangChain LLM class
- The model name follows standard naming conventions
- You can file a [feature request](https://github.com/revenium/revenium-middleware-langchain-python/issues) for new provider support

## Development

### Setup

```bash
# Create virtual environment
python -m venv .venv
source .venv/bin/activate

# Install the package with development dependencies
pip install revenium-middleware-langchain
pip install pytest pytest-asyncio pytest-mock pytest-cov black flake8 mypy
```

### Running Tests

```bash
pytest tests/ -v
```

### Running Examples

```bash
# Set environment variables
export REVENIUM_METERING_API_KEY=hak_your_api_key
export OPENAI_API_KEY=your_openai_key

# Run basic example
python examples/basic_llm.py

# Run agent example
python examples/agent_example.py
```

### Code Quality

```bash
# Lint
flake8 revenium_middleware_langchain/

# Format
black revenium_middleware_langchain/

# Type check
mypy revenium_middleware_langchain/
```

## License

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

## Contributing

See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.

## Code of Conduct

See [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md).

## Security

See [SECURITY.md](SECURITY.md) for our security policy and reporting vulnerabilities.

## Support

- Documentation: [https://docs.revenium.io](https://docs.revenium.io)
- Issues: [https://github.com/revenium/revenium-middleware-langchain-python/issues](https://github.com/revenium/revenium-middleware-langchain-python/issues)
- Email: support@revenium.io