foley.obs.trace

The OpenTelemetry MIRROR seam for foley’s observability (#11).

foley’s reproducible run-artifact (the RunManifest) is built by the recorder from its own clock + id-factory, independent of any tracer — so a bare pip install foley still emits a complete manifest with zero dependencies. This module is only the optional OpenTelemetry mirror: when the foley[obs] extra is installed, foley additionally emits vendor-neutral OTel GenAI spans (consumable by Langfuse / Datadog / Honeycomb / …); when it is not, a stdlib NoOpTracer makes every instrumentation call a free no-op.

opentelemetry is imported lazily inside OTelTracer only, never at module top level, so importing this module (and import foley) stays dol-only. foley never configures an SDK / exporter — that is the host application’s job (trace.set_tracer_provider(...)); until it does, even the OTel-backed tracer is a free ProxyTracer no-op, so instrumentation is safe and ~zero-cost by default.

foley.obs.trace.GENAI = {'data_source_id': 'gen_ai.data_source.id', 'error_type': 'error.type', 'finish_reasons': 'gen_ai.response.finish_reasons', 'input_tokens': 'gen_ai.usage.input_tokens', 'operation': 'gen_ai.operation.name', 'output_tokens': 'gen_ai.usage.output_tokens', 'provider': 'gen_ai.provider.name', 'request_model': 'gen_ai.request.model', 'response_model': 'gen_ai.response.model'}

The GenAI semantic-convention attribute keys foley emits. HARDCODED as strings on purpose — the opentelemetry.semconv._incubating constants are Development- stability and shift across minor versions. This dict is the single place they are named.

class foley.obs.trace.NoOpSpan[source]

A zero-cost span: every method does nothing; trace_id is always None.

class foley.obs.trace.NoOpTracer[source]

The default Tracer — yields the shared _NOOP_SPAN, zero deps.

start_as_current_span(name: str, *, kind: str | None = None, attributes: dict | None = None)[source]

Yield the no-op span (context-manager protocol; nothing is recorded).

class foley.obs.trace.OTelTracer[source]

An OpenTelemetry-backed Tracer (lazy import; requires foley[obs]).

start_as_current_span(name: str, *, kind: str | None = None, attributes: dict | None = None)[source]

Open a real OTel span, mapping kind to SpanKind and yielding a mirror.

class foley.obs.trace.Span(*args, **kwargs)[source]

The minimal mirror-span surface foley code calls (structural).

class foley.obs.trace.Tracer(*args, **kwargs)[source]

Starts mirror spans; the DI seam (default no-op, OTel-backed when present).

foley.obs.trace.get_tracer(*, prefer_otel: bool = True) Tracer[source]

Return the effective Tracer (OTel-backed when available, else no-op).

Parameters:

prefer_otel – When True (default) and opentelemetry is importable, return an OTelTracer (itself a free no-op until the host configures an SDK); any construction failure falls back to the no-op. False forces the stdlib NoOpTracer — the hermetic-test lever (this dev env may have otel).

Returns:

A Tracer (never raises).