Skip to content

Observability · Guide

Python ​

What the PyPI package actually ships: a standard-library-only core, one extra per framework, lazy imports that fail with a sentence rather than a traceback, and a decorator that handles all four function shapes. The wiring for each framework has its own guide.

bash
pip install cognipeer-observability
Packagecognipeer-observability
Python3.9 through 3.13
DependenciesNone, except typing_extensions on Python below 3.11
TypingShips py.typed; the public surface is annotated
LicenceMIT

The core ships on the standard library alone on purpose: an observability package must never force a dependency resolution on the application it is watching.

Install and extras ​

Extras pull in only what you use. Install the one matching the framework you already run.

CommandPulls inGuide
pip install cognipeer-observabilitynothingManual instrumentation
pip install "cognipeer-observability[langchain]"langchain-core>=0.1.0LangChain
pip install "cognipeer-observability[langgraph]"langgraph>=0.1.0, langchain-coreLangGraph
pip install "cognipeer-observability[openai-agents]"openai-agents>=0.0.1OpenAI Agents SDK
pip install "cognipeer-observability[claude-agent-sdk]"claude-agent-sdk>=0.1.0Claude Agent SDK
pip install "cognipeer-observability[otel]"opentelemetry-sdk>=1.20.0OpenTelemetry
pip install "cognipeer-observability[all]"every integration above—

There is also a dev extra (pytest, pytest-asyncio, mypy, ruff) for working on the package itself — see Contributing.

Modules ​

ModuleWhat it holdsGuide
cognipeer_observabilityinit, get_client, observe, trace, TraceSession, flush, shutdownAPI reference
cognipeer_observability.langchainCognipeerCallbackHandler, cognipeer_config, install_langchain_tracingLangChain
cognipeer_observability.langgraphgraph_config, trace_graphLangGraph
cognipeer_observability.openai_agentsCognipeerTracingProcessor, install_openai_agents_tracingOpenAI Agents SDK
cognipeer_observability.claude_agent_sdkClaudeMessageTracer, trace_queryClaude Agent SDK
cognipeer_observability.otelCognipeerSpanExporterOpenTelemetry

Every framework import is lazy: importing the core pulls in none of them. Importing a framework module without its dependency installed raises a clear ImportError naming the extra to install —

Install it with `pip install cognipeer-observability[langchain]`.

— rather than a bare traceback from three frames deep inside the framework.

The four forms of @observe ​

The decorator records one event per call and works on sync functions, async def coroutines, generators and async generators. Nesting is automatic: a decorated function called from inside another becomes its child span, and when no session is active the outermost call opens one and closes it when it returns.

python
import cognipeer_observability as cognipeer
from cognipeer_observability import observe

cognipeer.init(agent={"name": "research-agent"})

@observe(type="tool_call", tool_name="search")
def search(query: str) -> list[str]:
    return index.query(query)

@observe(type="retrieval")
async def fetch_context(topic: str) -> str:
    ...

@observe(name="stream_answer")
async def stream_answer(prompt: str):
    async for chunk in model.stream(prompt):
        yield chunk

For the two generator forms the event closes when the generator is exhausted, and the yielded chunks are collected into the Output section — so a streaming helper produces one event carrying the whole stream, not one event per chunk.

The event label defaults to the function's __name__ rather than its __qualname__: a qualified name drags in <locals> and the enclosing class, which makes a noisy timeline row for no gain. Pass name= to override. The full option list is in the API reference.

Ambient session context ​

trace() is a context manager. It opens a session, binds it as the ambient one for the block, and closes it on the way out — marking it error and re-raising untouched if the block throws.

python
from cognipeer_observability import trace

with trace(name="research-agent", thread_id="conv-42") as session:
    context = fetch_context("quarterly results")
    answer = summarize(context)

Everything inside lands in that session: nested @observe calls, framework handlers, direct session calls. When you need to steer that binding by hand — a worker thread, a callback that arrives outside the block, a session you created yourself — four context helpers are exported:

HelperUse
use_session(session)Bind session as the ambient one for the block
use_span(key)Bind key as the parent span for nested work in the block
get_current_session()The session enclosing the current execution, or None
get_current_span_key()The span key enclosing the current execution, or None

The context is carried in contextvars, so it survives await boundaries and keeps concurrent runs apart.

Delivery and exit ​

Requests run on a background daemon thread (named cognipeer-observability) behind a bounded queue, so recording an event never blocks the traced code and a Console outage cannot back up into your process.

That thread is why a short-lived process has to wait before exiting:

python
import cognipeer_observability as cognipeer

def handler(event, context):
    with cognipeer.trace(name="lambda-agent"):
        ...
    cognipeer.flush(timeout=10.0)   # or shutdown() to also close open sessions

flush(timeout=10.0) waits for what is queued; shutdown(timeout=10.0) ends every still-open session first, then flushes. Both are safe to call more than once. An atexit hook is registered automatically and covers an ordinary interpreter exit, but a platform that freezes the process — as serverless runtimes do between invocations — can cut it off, so call flush() explicitly there.

Calling init() a second time replaces the process-wide client and flushes the previous one, which is convenient in tests. reset_client() drops it entirely.

Building an integration yourself ​

TraceSession is the primitive every shipped integration is built on, and it is exported. Where a framework gives you paired start and end callbacks, open_span(key, ...) and close_span(key, ...) turn one pair into exactly one Console event carrying both sides, with the duration measured for you. See the session API for a worked example, and Contributing for the rules — including how to swap the transport so an integration test asserts on the payload instead of hitting the network.

Next ​

Studio · Pulse · Console · Agent SDK and more — the Cognipeer documentation hub