AgentsGenerator owns the observability lifecycle. Both the sync (generate_crew_and_kickoff) and async (agenerate_crew_and_kickoff) run paths bracket adapter.setup() and adapter.run()/adapter.arun() in a single observability_session. Adapters no longer initialise or finalize observability themselves — this closes the AutoGen leak (the v0.4 adapter never called finalize, so every run leaked a session) and makes finalize impossible to forget for any future adapter.
praisonai.observability.hooks.init_observability(framework_tag, *, tags=None) and finalize_observability(_framework_tag, *, status=...) are public hooks. The generator drives both automatically through observability_session(); custom adapters called directly (not via AgentsGenerator) should use observability_session() themselves.
Quick Start
1
Simple Usage
2
With Configuration
3
Pair init with finalize in custom adapters
observability_session calls init_observability on entry and finalize_observability on exit. Status is derived from sys.exc_info() automatically, so success/failure is tagged correctly with no boilerplate.Standalone: manual finalize for callers not going through the generator
Standalone: manual finalize for callers not going through the generator
This pattern is valid only for standalone callers that invoke an adapter directly, outside
AgentsGenerator. Do not copy it into a custom adapter that will be called by AgentsGenerator — the generator already owns init+finalize via observability_session, and a self-finalizing adapter would double-finalize.4
Branch on availability
How It Works
init_observability(framework_tag, *, tags=None) centralizes observability initialization:
- Auto-call site: the generator opens
observability_session(adapter.name)and runs_run_adapter_setup(adapter)inside the session, so setup events and any setup/import failure are recorded and finalized instead of slipping outside observability. The call sequence is:_prepare_for_run(config)→ validates, resolves adapter; does not run setup, does not init observabilitywith observability_session(adapter.name):_run_adapter_setup(adapter)(callsadapter.setup(framework_tag=adapter.name))adapter.run(...)/await adapter.arun(...)
- AgentOps init guard:
agentops.init(...)only fires if both (a)is_agentops_available()returns true, and (b)AGENTOPS_API_KEYis set in the env. Init is centralised here —agents_generatorno longer double-inits AgentOps (PR #2062). - Failure mode:
ImportError(no agentops) is logged atDEBUG; any other exception is logged atWARNINGand never propagated is_agentops_available()lazy function — prefer over the removed eagerAGENTOPS_AVAILABLEconstant in this module
finalize_observability(_framework_tag, *, status=...) closes observability sessions symmetrically:
- Auto-call site:
AgentsGenerator.generate_crew_and_kickoff(sync) andagenerate_crew_and_kickoff(async) bracket the entire adapter lifecycle —setup+run/arun— inobservability_session(adapter.name). On context exit the session finalizes withstatus="Failure"when an exception is propagating andstatus="Success"otherwise. Adapters must not finalize themselves. This closes the AutoGen leak: the AutoGen adapter previously never calledfinalize_observability, so every AutoGen run left anObservabilityRun, a_swapped_runsentry, and sink handles alive. - AgentOps end guard:
agentops.end_session(...)only fires ifagentopsis importable - Failure mode:
ImportErrorreturns silently; any other exception is logged atWARNINGand never propagated - Why symmetric calls matter: without
finalize_observability, AgentOps dashboard sessions stay stuck “in progress”
_init_langfuse and _init_wandb), so users may want to know the surface area.
Configuration
init_observability
finalize_observability
observability_session
Returns a context manager (
ContextManager[None]). Status is auto-derived from sys.exc_info() — no status kwarg needed.
Concurrent runs
Two overlappingobservability_session(...) blocks (parallel agents, nested crews) get their own AgentOps session each — tags don’t leak, and finalizing one no longer ends the other. finalize_observability resolves the owning ObservabilityRun before it calls agentops.end_session(...), so a run whose start_session returned no handle ends nothing instead of tearing down the package-global session a concurrent run still depends on. (PraisonAI #3492)
discover_observability_sinks
Third-party sink plugins
Third-party packages can register an observability sink factory under thepraisonai.observability_sinks entry-point group. PraisonAI discovers them lazily via discover_observability_sinks() — broken plugins are logged at DEBUG and never break a run.
Register a sink (plugin authors)
TraceSinkProtocol.
Discover registered sinks
Invalidating the cache
The factory list is memoized after the first discovery, so a dynamic plugin install or a test needs to invalidate it.Best Practices
Use observability_session for custom adapters called directly
Use observability_session for custom adapters called directly
For custom adapters that are called directly (not via
AgentsGenerator), observability_session is required. It guarantees finalize_observability always runs — on success and on any failure — with the correct status derived from sys.exc_info(). This prevents AgentOps/other sessions from being orphaned in an “in progress” state on error, KeyboardInterrupt, or rate-limit paths.When invoked via AgentsGenerator, the generator’s own session already covers the run — an adapter that opens its own inner observability_session will nest / double-init and should not.Status convention
Status convention
Use
status="Success" for the happy path and status="Failure" in exception cases. The string is passed verbatim to agentops.end_session(...); future providers may map other values. When using observability_session, status is derived automatically.Don't import agentops directly
Don't import agentops directly
Don’t import
agentops at the top of your adapter — gate it behind is_agentops_available() or rely on the hook to no-op silently:AgentOps sessions are per-run
AgentOps sessions are per-run
You no longer need to serialize concurrent runs to keep tags clean. Each
observability_session(...) starts its own AgentOps session via start_session and ends only that run’s handle. If you were adding a wait/lock around parallel AgentsGenerator.generate_crew_and_kickoff() calls specifically to avoid AgentOps cross-contamination, you can remove it.Future-proof for new providers
Future-proof for new providers
New providers (Langfuse, W&B, etc.) will be added inside
_init_<provider> helpers in praisonai/observability/hooks.py — calling init_observability(...) will automatically pick them up; you don’t need to update adapter code:Related
AgentOps
AgentOps integration documentation
Framework Adapter Plugins
How to create custom framework adapters
Custom Tracing
ContextTraceSink protocol and third-party sink plugins
Gateway Tracing Hook
Emit OpenTelemetry spans across each gateway pipeline stage

