Files
letta-server/letta/services/provider_trace_backends/base.py
Kian Jones 9418ab9815 feat: add provider trace backend abstraction for multi-backend telemetry (#8814)
* feat: add provider trace backend abstraction for multi-backend telemetry

Introduces a pluggable backend system for provider traces:
- Base class with async/sync create and read interfaces
- PostgreSQL backend (existing behavior)
- ClickHouse backend (via OTEL instrumentation)
- Socket backend (writes to Unix socket for crouton sidecar)
- Factory for instantiating backends from config

Refactors TelemetryManager to use backends with support for:
- Multi-backend writes (concurrent via asyncio.gather)
- Primary backend for reads (first in config list)
- Graceful error handling per backend

Config: LETTA_TELEMETRY_PROVIDER_TRACE_BACKEND (comma-separated)
Example: "postgres,socket" for dual-write to Postgres and crouton

🐙 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta <noreply@letta.com>

* feat: add protocol version to socket backend records

Adds PROTOCOL_VERSION constant to socket backend:
- Included in every telemetry record sent to crouton
- Must match ProtocolVersion in apps/crouton/main.go
- Enables crouton to detect and reject incompatible messages

🐙 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta <noreply@letta.com>

* fix: remove organization_id from ProviderTraceCreate calls

The organization_id is now handled via the actor parameter in the
telemetry manager, not through ProviderTraceCreate schema. This fixes
validation errors after changing ProviderTraceCreate to inherit from
BaseProviderTrace which forbids extra fields.

🐙 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta <noreply@letta.com>

* consolidate provider trace

* add clickhouse-connect to fix bug on main lmao

* auto generated sdk changes, and deployment details, and clikchouse prefix bug and added fields to runs trace return api

* auto generated sdk changes, and deployment details, and clikchouse prefix bug and added fields to runs trace return api

* consolidate provider trace

* consolidate provider trace bug fix

---------

Co-authored-by: Letta <noreply@letta.com>
2026-01-19 15:54:43 -08:00

70 lines
1.7 KiB
Python

"""Base class for provider trace backends."""
from abc import ABC, abstractmethod
from enum import Enum
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from letta.schemas.provider_trace import ProviderTrace
from letta.schemas.user import User
class ProviderTraceBackend(str, Enum):
"""Supported provider trace storage backends."""
POSTGRES = "postgres"
CLICKHOUSE = "clickhouse"
SOCKET = "socket"
class ProviderTraceBackendClient(ABC):
"""Abstract base class for provider trace storage backends."""
@abstractmethod
async def create_async(
self,
actor: "User",
provider_trace: "ProviderTrace",
) -> "ProviderTrace | None":
"""
Store a provider trace record.
Args:
actor: The user/actor creating the trace
provider_trace: The trace data to store
Returns:
The created ProviderTrace, or None if the backend doesn't return it
"""
raise NotImplementedError
@abstractmethod
async def get_by_step_id_async(
self,
step_id: str,
actor: "User",
) -> "ProviderTrace | None":
"""
Retrieve a provider trace by step ID.
Args:
step_id: The step ID to look up
actor: The user/actor requesting the trace
Returns:
The ProviderTrace if found, None otherwise
"""
raise NotImplementedError
def create_sync(
self,
actor: "User",
provider_trace: "ProviderTrace",
) -> "ProviderTrace | None":
"""
Synchronous version of create_async.
Default implementation does nothing. Override if sync support is needed.
"""
return None