chore: initial snapshot for gitea/github upload
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
# Levo AI Integration
|
||||
|
||||
This integration enables sending LLM observability data to Levo AI using OpenTelemetry (OTLP) protocol.
|
||||
|
||||
## Overview
|
||||
|
||||
The Levo integration extends LiteLLM's OpenTelemetry support to automatically send traces to Levo's collector endpoint with proper authentication and routing headers.
|
||||
|
||||
## Features
|
||||
|
||||
- **Automatic OTLP Export**: Sends OpenTelemetry traces to Levo collector
|
||||
- **Levo-Specific Headers**: Automatically includes `x-levo-organization-id` and `x-levo-workspace-id` for routing
|
||||
- **Simple Configuration**: Just use `callbacks: ["levo"]` in your LiteLLM config
|
||||
- **Environment-Based Setup**: Configure via environment variables
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Install Dependencies
|
||||
|
||||
```bash
|
||||
pip install opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp-proto-http opentelemetry-exporter-otlp-proto-grpc
|
||||
```
|
||||
|
||||
### 2. Configure LiteLLM
|
||||
|
||||
Add to your `litellm_config.yaml`:
|
||||
|
||||
```yaml
|
||||
litellm_settings:
|
||||
callbacks: ["levo"]
|
||||
```
|
||||
|
||||
### 3. Set Environment Variables
|
||||
|
||||
```bash
|
||||
export LEVOAI_API_KEY="<your-levo-api-key>"
|
||||
export LEVOAI_ORG_ID="<your-levo-org-id>"
|
||||
export LEVOAI_WORKSPACE_ID="<your-workspace-id>"
|
||||
export LEVOAI_COLLECTOR_URL="<your-levo-collector-url>"
|
||||
```
|
||||
|
||||
### 4. Start LiteLLM
|
||||
|
||||
```bash
|
||||
litellm --config config.yaml
|
||||
```
|
||||
|
||||
All LLM requests will now automatically be sent to Levo!
|
||||
|
||||
## Configuration
|
||||
|
||||
### Required Environment Variables
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `LEVOAI_API_KEY` | Your Levo API key for authentication |
|
||||
| `LEVOAI_ORG_ID` | Your Levo organization ID for routing |
|
||||
| `LEVOAI_WORKSPACE_ID` | Your Levo workspace ID for routing |
|
||||
| `LEVOAI_COLLECTOR_URL` | Full collector endpoint URL from Levo support |
|
||||
|
||||
### Optional Environment Variables
|
||||
|
||||
| Variable | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| `LEVOAI_ENV_NAME` | Environment name for tagging traces | `None` |
|
||||
|
||||
**Important**: The `LEVOAI_COLLECTOR_URL` is used exactly as provided. No path manipulation is performed.
|
||||
|
||||
## How It Works
|
||||
|
||||
1. **LevoLogger** extends LiteLLM's `OpenTelemetry` class
|
||||
2. **Configuration** is read from environment variables via `get_levo_config()`
|
||||
3. **OTLP Headers** are automatically set:
|
||||
- `Authorization: Bearer {LEVOAI_API_KEY}`
|
||||
- `x-levo-organization-id: {LEVOAI_ORG_ID}`
|
||||
- `x-levo-workspace-id: {LEVOAI_WORKSPACE_ID}`
|
||||
4. **Traces** are sent to the collector endpoint in OTLP format
|
||||
|
||||
## Code Structure
|
||||
|
||||
```
|
||||
litellm/integrations/levo/
|
||||
├── __init__.py # Exports LevoLogger
|
||||
├── levo.py # LevoLogger implementation
|
||||
└── README.md # This file
|
||||
```
|
||||
|
||||
### Key Classes
|
||||
|
||||
- **LevoLogger**: Extends `OpenTelemetry`, handles Levo-specific configuration
|
||||
- **LevoConfig**: Pydantic model for Levo configuration (defined in `levo.py`)
|
||||
|
||||
## Testing
|
||||
|
||||
See the test files in `tests/test_litellm/integrations/levo/`:
|
||||
- `test_levo.py`: Unit tests for configuration
|
||||
- `test_levo_integration.py`: Integration tests for callback registration
|
||||
|
||||
## Error Handling
|
||||
|
||||
The integration validates all required environment variables at initialization:
|
||||
- Missing `LEVOAI_API_KEY`: Raises `ValueError` with clear message
|
||||
- Missing `LEVOAI_ORG_ID`: Raises `ValueError` with clear message
|
||||
- Missing `LEVOAI_WORKSPACE_ID`: Raises `ValueError` with clear message
|
||||
- Missing `LEVOAI_COLLECTOR_URL`: Raises `ValueError` with clear message
|
||||
|
||||
## Integration with LiteLLM
|
||||
|
||||
The Levo callback is registered in:
|
||||
- `litellm/litellm_core_utils/custom_logger_registry.py`: Maps `"levo"` to `LevoLogger`
|
||||
- `litellm/litellm_core_utils/litellm_logging.py`: Instantiates `LevoLogger` when `callbacks: ["levo"]` is used
|
||||
- `litellm/__init__.py`: Added to `_custom_logger_compatible_callbacks_literal`
|
||||
|
||||
## Documentation
|
||||
|
||||
For detailed documentation, see:
|
||||
- [LiteLLM Levo Integration Docs](../../../../docs/my-website/docs/observability/levo_integration.md)
|
||||
- [Levo Documentation](https://docs.levo.ai)
|
||||
|
||||
## Support
|
||||
|
||||
For issues or questions:
|
||||
- LiteLLM Issues: https://github.com/BerriAI/litellm/issues
|
||||
- Levo Support: support@levo.ai
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
from litellm.integrations.levo.levo import LevoLogger
|
||||
|
||||
__all__ = ["LevoLogger"]
|
||||
@@ -0,0 +1,118 @@
|
||||
import os
|
||||
from typing import TYPE_CHECKING, Any, Optional, Union
|
||||
|
||||
from litellm.integrations.opentelemetry import OpenTelemetry
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from opentelemetry.trace import Span as _Span
|
||||
|
||||
from litellm.integrations.opentelemetry import (
|
||||
OpenTelemetryConfig as _OpenTelemetryConfig,
|
||||
)
|
||||
from litellm.types.integrations.arize import Protocol as _Protocol
|
||||
|
||||
Protocol = _Protocol
|
||||
OpenTelemetryConfig = _OpenTelemetryConfig
|
||||
Span = Union[_Span, Any]
|
||||
else:
|
||||
Protocol = Any
|
||||
OpenTelemetryConfig = Any
|
||||
Span = Any
|
||||
|
||||
|
||||
class LevoConfig:
|
||||
"""Configuration for Levo OTLP integration."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
otlp_auth_headers: Optional[str],
|
||||
protocol: Protocol,
|
||||
endpoint: str,
|
||||
):
|
||||
self.otlp_auth_headers = otlp_auth_headers
|
||||
self.protocol = protocol
|
||||
self.endpoint = endpoint
|
||||
|
||||
|
||||
class LevoLogger(OpenTelemetry):
|
||||
"""Levo Logger that extends OpenTelemetry for OTLP integration."""
|
||||
|
||||
@staticmethod
|
||||
def get_levo_config() -> LevoConfig:
|
||||
"""
|
||||
Retrieves the Levo configuration based on environment variables.
|
||||
|
||||
Returns:
|
||||
LevoConfig: Configuration object containing Levo OTLP settings.
|
||||
|
||||
Raises:
|
||||
ValueError: If required environment variables are missing.
|
||||
"""
|
||||
# Required environment variables
|
||||
api_key = os.environ.get("LEVOAI_API_KEY", None)
|
||||
org_id = os.environ.get("LEVOAI_ORG_ID", None)
|
||||
workspace_id = os.environ.get("LEVOAI_WORKSPACE_ID", None)
|
||||
collector_url = os.environ.get("LEVOAI_COLLECTOR_URL", None)
|
||||
|
||||
# Validate required env vars
|
||||
if not api_key:
|
||||
raise ValueError(
|
||||
"LEVOAI_API_KEY environment variable is required for Levo integration."
|
||||
)
|
||||
if not org_id:
|
||||
raise ValueError(
|
||||
"LEVOAI_ORG_ID environment variable is required for Levo integration."
|
||||
)
|
||||
if not workspace_id:
|
||||
raise ValueError(
|
||||
"LEVOAI_WORKSPACE_ID environment variable is required for Levo integration."
|
||||
)
|
||||
if not collector_url:
|
||||
raise ValueError(
|
||||
"LEVOAI_COLLECTOR_URL environment variable is required for Levo integration. "
|
||||
"Please contact Levo support to get your collector URL."
|
||||
)
|
||||
|
||||
# Use collector URL exactly as provided by the user
|
||||
endpoint = collector_url
|
||||
protocol: Protocol = "otlp_http"
|
||||
|
||||
# Build OTLP headers string
|
||||
# Format: Authorization=Bearer {api_key},x-levo-organization-id={org_id},x-levo-workspace-id={workspace_id}
|
||||
headers_parts = [f"Authorization=Bearer {api_key}"]
|
||||
headers_parts.append(f"x-levo-organization-id={org_id}")
|
||||
headers_parts.append(f"x-levo-workspace-id={workspace_id}")
|
||||
|
||||
otlp_auth_headers = ",".join(headers_parts)
|
||||
|
||||
return LevoConfig(
|
||||
otlp_auth_headers=otlp_auth_headers,
|
||||
protocol=protocol,
|
||||
endpoint=endpoint,
|
||||
)
|
||||
|
||||
async def async_health_check(self):
|
||||
"""
|
||||
Health check for Levo integration.
|
||||
|
||||
Returns:
|
||||
dict: Health status with status and message/error_message keys.
|
||||
"""
|
||||
try:
|
||||
config = self.get_levo_config()
|
||||
|
||||
if not config.otlp_auth_headers:
|
||||
return {
|
||||
"status": "unhealthy",
|
||||
"error_message": "LEVOAI_API_KEY environment variable not set",
|
||||
}
|
||||
|
||||
return {
|
||||
"status": "healthy",
|
||||
"message": "Levo credentials are configured properly",
|
||||
}
|
||||
except ValueError as e:
|
||||
return {
|
||||
"status": "unhealthy",
|
||||
"error_message": str(e),
|
||||
}
|
||||
Reference in New Issue
Block a user