Skip to main content
The engine ships handlers for Langfuse, Arize Phoenix, LangSmith, Google Cloud Trace, and Google Cloud Logging. If you run a different observability stack (Datadog, Honeycomb, Grafana Tempo, a custom OTLP collector), you can wrap it as an Idun handler by subclassing ObservabilityHandlerBase.
Like the agent adapter factory, the observability factory in idun_agent_engine.observability.base.create_observability_handler is currently a hardcoded if/elif over the ObservabilityProvider enum. Wiring a new handler requires a small upstream change. There is no Python entry-point hook today.

The base class

ObservabilityHandlerBase lives in libs/idun_agent_engine/src/idun_agent_engine/observability/base.py. It is small: three members and one optional helper: That’s it. The minimal handler is fifteen lines.

Two integration patterns

Observability providers fall into one of two categories. Your handler should pick one.

Pattern A: LangChain callbacks (Langfuse-style)

For providers that expose a LangChain BaseCallbackHandler. Return your callback list from get_callbacks() and the engine attaches them to every agent invocation:
langfuse_like.py
Use this pattern for: Langfuse, LangSmith via the LangChain integration, Helicone, Comet Opik.

Pattern B: Global OpenTelemetry instrumentation (Phoenix-style)

For providers that hook into the global OpenTelemetry SDK. Configure your tracer provider and exporter inside __init__, then return an empty list from get_callbacks(): the agent runtime will be auto-instrumented globally:
phoenix_like.py
Use this pattern for: any OTLP-compatible backend (Honeycomb, Tempo, Jaeger, Datadog OTLP, NewRelic OTLP), GCP Trace, Arize Phoenix.

The _resolve_env helper

The canonical implementation lives in idun_agent_schema.engine.observability. Each shipping handler imports it and re-exposes it as a staticmethod for convenience:
Import it from the schema rather than copying the body, so future changes (e.g. expanded env-var syntax) propagate to your handler automatically. The helper lets users keep secrets in env vars and reference them from config.yaml:

Init failures should not crash boot

Following the engine’s CLAUDE.md guidance: telemetry must never alter command or runtime semantics. Wrap your handler’s init in try/except Exception and log the failure. The engine continues without the failed provider rather than aborting the agent boot.

Wiring the handler into the engine

Until a plugin API ships, register your handler in two places.

1. Add an enum value

In your fork of idun_agent_schema, extend ObservabilityProvider:

2. Add a branch in create_observability_handler

In libs/idun_agent_engine/src/idun_agent_engine/observability/base.py:
Then in your config.yaml:

The local trace pipeline (standalone)

The standalone runtime ships its own local trace store backed by SQLite or Postgres. It uses the same OpenTelemetry SpanProcessor hook as Pattern B, registered via attach_span_processor() from idun_agent_engine.observability.otel_lifecycle. Your handler does not need to do anything for the local pipeline to work: it runs alongside whatever provider the user has configured. If you want your handler to interoperate with the local pipeline (e.g. share the same TracerProvider), call attach_span_processor() rather than instantiating your own TracerProvider:

Reference handlers

The five shipping handlers cover both patterns. Read them in this order:

What’s next

Observability overview

Built-in providers and configuration.

Custom framework adapter

The same extension pattern for agent frameworks.

Troubleshooting

Diagnosing observability init failures in the log.
Last modified on May 20, 2026