# Direct LLM Provider Examples

Examples demonstrating MCA SDK integration with LLM provider SDKs and frameworks.

## Overview

The MCA SDK provides automatic observability for LLM applications through two approaches:

1. **Auto-patching** (OpenAI, Anthropic): Zero-code instrumentation using `autoinstrument()`
2. **Callback handlers** (LangChain, LlamaIndex): Explicit callback classes for framework integration

All examples demonstrate:
- Request/response latency tracking
- Token usage metrics (prompt/completion or input/output)
- Model information capture
- Cost estimation
- Distributed trace spans
- Error tracking

## Supported Providers

| Provider | Integration Type | Example File |
|----------|------------------|--------------|
| **OpenAI SDK** | Auto-patching | `instrumented_openai.py` |
| **Anthropic SDK** | Auto-patching | `instrumented_anthropic.py` |
| **LangChain** | Callback handler | `instrumented_langchain.py` |
| **LlamaIndex** | Callback handler | `instrumented_llamaindex.py` |

## Installation

### Install MCA SDK with provider support

```bash
# Install for specific provider
pip install mca-sdk[openai]      # OpenAI only
pip install mca-sdk[anthropic]    # Anthropic only
pip install mca-sdk[langchain]    # LangChain only
pip install mca-sdk[llamaindex]   # LlamaIndex only

# Install all LLM provider support
pip install mca-sdk[openai,anthropic,langchain,llamaindex]

# Or install from requirements.txt
pip install -r requirements.txt
```

### Install additional dependencies for examples

Some examples require additional framework packages:

```bash
# For LangChain examples with OpenAI
pip install langchain-openai

# For LlamaIndex examples with OpenAI
pip install llama-index-llms-openai
```

## Setup

### 1. Configure API Keys

Copy the environment variable template:

```bash
cp .env.example .env
```

Edit `.env` and add your API keys:

```bash
# OpenAI API Key
OPENAI_API_KEY=sk-...

# Anthropic API Key
ANTHROPIC_API_KEY=sk-ant-...
```

**Note:** Examples work without API keys (mock mode), but real telemetry requires valid keys.

### 2. Configure OTLP Collector (Optional)

By default, examples use a console exporter for demonstration. To send telemetry to a real collector:

```bash
# In .env file
OTLP_ENDPOINT=http://localhost:4317
```

Then modify example code to use the environment variable instead of `"console"`:

```python
client = MCAClient(
    service_name="my-app",
    model_id="my-model",
    team_name="my-team",
    model_category="external-genai",
    model_type="llm",
    otlp_endpoint=os.getenv("OTLP_ENDPOINT", "http://localhost:4317"),
    otlp_protocol="grpc"
)
```

## Examples

### OpenAI SDK (`instrumented_openai.py`)

Demonstrates auto-instrumentation for OpenAI SDK with:
- Chat completions (non-streaming and streaming)
- Text embeddings
- Error handling

**Run:**
```bash
python instrumented_openai.py
```

**What gets captured:**
- Request latency (sync and async)
- Token usage (prompt_tokens, completion_tokens)
- Model name and parameters (temperature, max_tokens)
- Cost estimation based on OpenAI pricing
- Streaming: token counting from final chunk
- Errors: API errors, network errors, timeouts

**Key features:**
- Zero-code instrumentation with `autoinstrument()`
- Works with both sync and async OpenAI SDK methods
- Supports streaming responses
- Automatic cost calculation

### Anthropic SDK (`instrumented_anthropic.py`)

Demonstrates auto-instrumentation for Anthropic SDK with:
- Non-streaming messages.create
- Streaming with event-based API
- System prompts
- Error handling

**Run:**
```bash
python instrumented_anthropic.py
```

**What gets captured:**
- Request latency (sync and async)
- Token usage (input_tokens, output_tokens)
- Model name and parameters (max_tokens, system)
- Cost estimation based on Anthropic pricing
- Streaming: usage from message_stop event
- Errors: BadRequestError, network errors

**Key features:**
- Zero-code instrumentation with `autoinstrument()`
- Event-based streaming support
- Token type mapping (input vs output)
- Automatic cost calculation

### LangChain (`instrumented_langchain.py`)

Demonstrates callback handler for LangChain with:
- Simple LLM chains
- Multi-step chains
- Span hierarchy (chain > LLM)
- Error handling

**Run:**
```bash
python instrumented_langchain.py
```

**What gets captured:**
- Chain execution spans
- LLM invocation spans (child of chain)
- Tool usage spans (child of chain)
- Agent action spans
- Token usage from LLM outputs
- Run ID correlation for parent-child relationships
- Errors in any callback method

**Key features:**
- Explicit callback: `MCALangChainCallback(client)`
- Pass to chain: `chain.invoke(..., config={"callbacks": [mca_callback]})`
- Captures full chain hierarchy
- Works with agents and tools

### LlamaIndex (`instrumented_llamaindex.py`)

Demonstrates callback handler for LlamaIndex with:
- Query engine execution
- Document retrieval
- Response synthesis
- Span hierarchy (query > retrieve + synthesize > LLM)

**Run:**
```bash
python instrumented_llamaindex.py
```

**What gets captured:**
- Query execution spans
- Retrieval spans (child of query) with node count
- Synthesis spans (child of query)
- LLM invocation spans (child of synthesis)
- Embedding generation spans
- Event ID correlation for parent-child relationships
- Errors in any event handler

**Key features:**
- Explicit callback: `MCALlamaIndexCallback(client)`
- Set globally: `Settings.callback_manager = CallbackManager([mca_callback])`
- Captures full query lifecycle
- Tracks retrieval count and synthesis details

## Expected Output

All examples print telemetry to console. You should see:

```
================================================================================
MCA SDK - [Provider] Integration Example
================================================================================

✓ MCA SDK initialized and [Provider] auto-instrumented
  Telemetry will be printed to console below

⚠ [PROVIDER]_API_KEY not set - using mock mode for demonstration
  Set [PROVIDER]_API_KEY environment variable to test with real API

================================================================================
MOCK MODE DEMONSTRATION
================================================================================
...
```

When API keys are set, you'll see actual API responses and telemetry spans/metrics printed to console.

## Troubleshooting

### API Key Not Found

**Symptom:** Examples run in mock mode

**Solution:** Set API keys in `.env` file and load them:
```bash
export $(cat .env | xargs)  # Load from .env
python instrumented_openai.py
```

Or set directly:
```bash
export OPENAI_API_KEY=sk-...
python instrumented_openai.py
```

### ImportError: No module named 'openai'

**Symptom:** `ImportError` when running examples

**Solution:** Install provider SDK:
```bash
pip install mca-sdk[openai]  # Or anthropic, langchain, llamaindex
```

### ImportError: No module named 'langchain_openai'

**Symptom:** LangChain example fails to import `ChatOpenAI`

**Solution:** Install framework-specific packages:
```bash
pip install langchain-openai  # For LangChain with OpenAI
pip install llama-index-llms-openai  # For LlamaIndex with OpenAI
```

### No telemetry output visible

**Symptom:** Examples run but no spans/metrics printed

**Solution:** Verify console exporter is used:
```python
otlp_endpoint="console"  # Not a real endpoint URL
```

### Connection refused when using real collector

**Symptom:** `ConnectionRefusedError` when pointing to collector

**Solution:** Ensure collector is running:
```bash
# Start collector with Docker
docker run -p 4317:4317 otel/opentelemetry-collector-contrib:latest

# Or use docker-compose from mca-prototype/
docker-compose up -d otel-collector
```

### Streaming responses not captured

**Symptom:** Streaming works but token counts are wrong

**Solution:** Ensure you consume the entire stream. The SDK captures usage from the final chunk (OpenAI) or message_stop event (Anthropic).

### Cost estimation shows $0.00

**Symptom:** Telemetry shows zero cost

**Solution:** Cost estimation requires:
1. Valid model name in pricing table
2. Token usage from API response
3. Known pricing tier

Unknown models fall back to default rates.

## Advanced Usage

### Capture Input/Output Data

By default, the SDK does **NOT** capture request/response content for PHI safety. To enable:

```python
# For OpenAI/Anthropic (auto-patching)
autoinstrument(
    capture_input=True,   # Capture request content
    capture_output=True   # Capture response content
)
```

**⚠️ WARNING:** Only enable in non-PHI environments. You are responsible for sanitizing PHI before enabling capture.

### Custom Data Sanitizer

Provide a custom sanitizer hook:

```python
def my_sanitizer(data: dict) -> dict:
    """Remove PHI fields before capture."""
    # Custom sanitization logic
    return sanitized_data

autoinstrument(
    capture_input=True,
    data_sanitizer_hook=my_sanitizer
)
```

### Exclude Specific Providers

Exclude providers from auto-instrumentation:

```python
# Don't patch OpenAI
autoinstrument(exclude=["openai"])

# Only patch Anthropic
autoinstrument(include=["anthropic"])
```

### Multiple Callbacks

LangChain and LlamaIndex support multiple callbacks:

```python
# LangChain
from langchain.callbacks import StdOutCallbackHandler

callbacks = [
    MCALangChainCallback(client),
    StdOutCallbackHandler()  # Built-in logging
]

chain.invoke(..., config={"callbacks": callbacks})
```

```python
# LlamaIndex
from llama_index.core.callbacks import CallbackManager, LlamaDebugHandler

Settings.callback_manager = CallbackManager([
    MCALlamaIndexCallback(client),
    LlamaDebugHandler()  # Built-in debugging
])
```

## Performance

The MCA SDK is designed for minimal overhead:

**Overhead targets (NFR35):**
- OpenAI patcher: <5ms p95 latency overhead
- Anthropic patcher: <5ms p95 latency overhead
- LangChain callback: <2ms p95 per event
- LlamaIndex callback: <2ms p95 per event

Overhead is measured relative to uninstrumented calls and is negligible compared to API latency (100ms-10s).

## Security Best Practices

1. **Never log API keys:** Keys are automatically redacted in SDK logs
2. **PHI safety:** Default `capture_input=False` prevents content capture
3. **HTTPS required:** Registry URLs must use HTTPS (except localhost)
4. **Token rotation:** Rotate API keys regularly
5. **Sanitize data:** Use `data_sanitizer_hook` if enabling capture

## Links

- [MCA SDK Documentation](../../README.md)
- [OpenAI SDK Documentation](https://platform.openai.com/docs/api-reference)
- [Anthropic SDK Documentation](https://docs.anthropic.com/claude/reference)
- [LangChain Documentation](https://python.langchain.com/)
- [LlamaIndex Documentation](https://docs.llamaindex.ai/)
- [OpenTelemetry Documentation](https://opentelemetry.io/docs/)

## Support

For issues or questions:
1. Check [Troubleshooting](#troubleshooting) section above
2. Review main SDK [README](../../README.md)
3. Check [test examples](../../tests/unit/) for advanced usage patterns
4. File an issue in the repository

## License

Same as MCA SDK main package.
