chore: initial snapshot for gitea/github upload

This commit is contained in:
Your Name
2026-03-26 16:04:46 +08:00
commit a699a1ac98
3497 changed files with 1586237 additions and 0 deletions

View File

@@ -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

View File

@@ -0,0 +1,3 @@
from litellm.integrations.levo.levo import LevoLogger
__all__ = ["LevoLogger"]

View File

@@ -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),
}