Skip to main content
The read/query half of the monitoring contract. Implement only if the backend has a read API. A read-incapable backend still exposes a reader object, but its methods raise MonitoringReadNotSupportedError on call (never a silent no-op). OpenTelemetry has no read/query standard — each backend invents its own read surface — so this face is the abstraction’s real value.

MonitoringReader

tai42_contract.monitoring.reader.MonitoringReader
Query totals/analytics and the runtime span window.

Members

query_metrics

tai42_contract.monitoring.reader.MonitoringReader.query_metrics
Totals / analytics screen — server-side aggregation. Counts (runs, tags), total cost, total tokens, latency, grouped by the requested dimensions. Parameters

list_spans_in_window

tai42_contract.monitoring.reader.MonitoringReader.list_spans_in_window
The smallest spans that ran in the half-open window [t0, t1). CONTRACT GUARANTEE: exactly one item per tool/node execution in the window. Every reader performs this tool-granularity selection — it is not an optional filter. run scopes to a single run (one trace); None = all runs in the window. kind narrows WITHIN the tool-granularity set (never widens or overrides the guarantee). filter applies the neutral MonitoringFilter clauses (tags live here, not as a discrete param). order_by sorts the result; omitted ⇒ newest-first (start desc). An unsupported order_by.field or filter clause raises MonitoringReadNotSupportedError. Parameters

get_trace

tai42_contract.monitoring.reader.MonitoringReader.get_trace
Fetch one COMPLETE trace (top-level attrs + every observation, with full input/output). Used for replay / evaluation / normalization. Always returns a trace or raises — never None. An absent trace raises TraceNotFoundError; a transient/backend failure (e.g. a timeout) propagates its error so the caller sees the failure and may retry. Distinct from list_spans_in_window (trimmed dashboard units). Parameters

list_traces

tai42_contract.monitoring.reader.MonitoringReader.list_traces
List run SUMMARIES matching the filters: one row per trace. Built from the backend’s list surface plus its batched aggregates — never a per-trace body. get_trace is the only body door. A malformed backend row fails the page loudly (never kept as a partial row nor silently skipped). from_timestamp / to_timestamp bound the trace timestamp to the half-open window [from, to); either may be omitted for an open end. filter applies the neutral MonitoringFilter clauses (tags live here, not as a discrete param). order_by sorts the result; omitted ⇒ newest-first (timestamp desc). Sortable fields: timestamp, total_cost, name, id, latency, total_tokens — of these total_cost / latency / total_tokens rank globally (see OrderBy); the rest sort natively. An unsupported order_by.field or filter clause raises MonitoringReadNotSupportedError. Parameters

RUN_ATTRIBUTION_TRACE_NAME

tai42_contract.monitoring.writer.RUN_ATTRIBUTION_TRACE_NAME

RUN_VERSION_METADATA_KEY

tai42_contract.monitoring.writer.RUN_VERSION_METADATA_KEY

MonitoringWriter

tai42_contract.monitoring.writer.MonitoringWriter
Emit traces/spans/events, supply callbacks, and manage lifecycle. Every backend implements this face.

Members

start_span

tai42_contract.monitoring.writer.MonitoringWriter.start_span
Open a span for the duration of the block, yielding a Span handle. model / model_parameters carry GENERATION-span detail. model_parameters is open-time only (known before the call); model and usage_details can be amended later via the handle’s Span.update. A no-op under an active disable() block. Parameters

record_span

tai42_contract.monitoring.writer.MonitoringWriter.record_span
Record an already-closed span with EXPLICIT start / end times. For a span whose execution window cannot be timed live in-process — work that ran across a pause and is reported afterwards. start / end are wall-clock datetime; the backend converts as needed. trace_context.trace_id is REQUIRED — the explicit-time path has no ambient context to fall back to, so a missing trace_id RAISES (a caller bug); parent_span_id nests the span. Backend emission failures are caught + logged like the other emit methods. Parameters

create_event

tai42_contract.monitoring.writer.MonitoringWriter.create_event
Record a point-in-time event, carrying input / output. Parameters

update_current_span

tai42_contract.monitoring.writer.MonitoringWriter.update_current_span
Mutate the CURRENT (ambient) span without opening a new one. Targets the span opened by the nearest enclosing start_span block (or the callback-handler span if none). Per-generation token/cost is recorded via the held Span.update handle, not here. Parameters

trace_attributes

tai42_contract.monitoring.writer.MonitoringWriter.trace_attributes
Set trace-level name/tags/metadata on the ambient trace for the block. user_id / session_id set the backend’s native identity dimensions when supplied; None leaves each unset. They are optional and keyword-only, so an existing caller that sets only name/tags/metadata is unaffected. A ROOT version is carried inside metadata under RUN_VERSION_METADATA_KEY (not a discrete parameter) so a backend without a native version dimension keeps it as ordinary metadata. Parameters

current_trace_id

tai42_contract.monitoring.writer.MonitoringWriter.current_trace_id
The active ambient trace id, or None if no trace is active. Used to gate conditional emits (if writer.current_trace_id():) so none fire outside a trace.

inject_context

tai42_contract.monitoring.writer.MonitoringWriter.inject_context
Build the opaque downstream-propagation blob merged into the langgraph RunnableConfig. Carries ctx.tags + ctx.metadata. Parameters

get_monitoring_callbacks

tai42_contract.monitoring.writer.MonitoringWriter.get_monitoring_callbacks
The LangChain/LangGraph callback handlers appended to the langgraph config["callbacks"]. The caller builds ctx (keeping the auto-generated-trace-id fallback); the impl reads its fields to construct the vendor handler. Parameters

scope

tai42_contract.monitoring.writer.MonitoringWriter.scope
Bind tracing to a project (public_key) for the block. Mirrors a single-backend multi-project switch. OTel-native backends may no-op it; a real failure must raise (cross-project leak risk). Parameters

disable

tai42_contract.monitoring.writer.MonitoringWriter.disable
Suppress emission within the block. The backend’s tracing-producing methods (and the handlers from get_monitoring_callbacks) MUST honor this. A backend that never emits may no-op it; an emitting backend may not.

flush

tai42_contract.monitoring.writer.MonitoringWriter.flush
Flush any buffered telemetry.

shutdown

tai42_contract.monitoring.writer.MonitoringWriter.shutdown
Tear the client down so the next use rebuilds clean (fork-safety). Must fully evict the underlying client, not merely flush — a forked child inherits dead background threads otherwise.

attribute_run

tai42_contract.monitoring.writer.attribute_run
Return the context manager that stamps attribution on the ambient trace for the WRAPPED block. A free function composing writer over MonitoringWriter.trace_attributes, kept off the Protocol so structural writers conform without inheriting it. The caller ENTERS the returned manager AROUND the whole run so the run’s spans are created INSIDE the attribution scope. It RETURNS the manager rather than entering it — a one-shot enter/exit here would tear the scope down before any span exists and silently no-op the stamp. It does NOT gate on MonitoringWriter.current_trace_id. A stamp deposited BEFORE any trace is open is NOT lost: trace_attributes is context-scoped (langfuse’s propagate_attributes writes the attributes into the ambient OTel context — see propagation.py’s _set_propagated_attribute — and the span processor lifts them onto the trace ROOT when a span is subsequently opened inside the scope, span_processor.py). Short-circuiting to a nullcontext when current_trace_id() is None therefore DROPPED the attribution on the common case — a door that deposits then opens the run’s first span — so the guard is removed. This helper is NOT itself fail-safe against a non-conforming writer: it CALLS trace_attributes here, so a writer whose signature omits these keywords raises a TypeError at THIS call — before any enter/exit guard inside the writer could run — and a writer that raises on enter/exit surfaces likewise. A caller that must not be broken by a monitoring-writer fault guards entering the returned manager (the skeleton run seams do, logging the fault and continuing the run unattributed). Parameters