solvi.otel¶
Traces as OpenTelemetry spans: through the OpenTelemetry API or as OTLP/JSON.
Traces as OpenTelemetry spans: one decision → a root span "solvi.decision" with one child span per step of the trace (and one per answer), so solvi decisions show up next to the rest of a service's traces (Jaeger, Tempo, Honeycomb, …).
export(res_or_store, tracer=None, **filters) # through the OpenTelemetry API (the `otel` extra: opentelemetry-api/sdk)
to_otlp_json(res_or_store, **filters) # OTLP/JSON (ExportTraceServiceRequest) as a dict, without OpenTelemetry
Attributes of a step span (only those that apply): solvi.step, solvi.kind, solvi.fact, solvi.provenance, solvi.value (a short repr), solvi.confidence, solvi.error, solvi.producer, solvi.tried, solvi.quote.source / .start / .end, solvi.model.type / .id / .fingerprint, solvi.probs (JSON), solvi.safeguard (the kinds that fired on this fact), solvi.inputs (the facts it read), solvi.hash and solvi.prev (the hash chain). An answer span: solvi.question, solvi.answer, solvi.status, solvi.confidence, solvi.why, solvi.guard, solvi.provenance, solvi.source, solvi.safeguard. The root: solvi.questions, solvi.trace.init_hash / .head, solvi.catalog.fingerprint, solvi.stored_id, solvi.ms, and an event per step skipped at run time.
A step that failed or whose output was rejected has status ERROR with the reason; an abstention is not an error (its span
says status abstain and the guard). Times: a trace records each step's run time, not its start, so the step spans are laid
end to end from the decision's start — their durations are measured, their start times are not (and parallel steps are
shown one after another). A stored decision starts at its stored time minus its run time; a fresh response ends when it is
exported, unless end_ns= is given. In the OTLP JSON the ids are derived from the trace's hashes and the stored id (the
same stored decision exports the same ids); an unstored response adds a random nonce of its own (kept on the response),
so two identical unstored decisions never share ids; through the API the SDK assigns them.
spans ¶
One decision as neutral span dicts (the root first): {"name", "span_id", "parent", "trace_id", "start", "end", "attributes", "status": None or ("ERROR", message), "events": [(name, time, attributes)]}.
export ¶
Export decisions as OpenTelemetry spans: a Response, a TraceStorage (every stored decision, or those matching the
query filters) or a list of them. tracer: an opentelemetry Tracer (default: trace.get_tracer("solvi") of the global
provider). Each root span is a child of the span current in the caller's context, if any. → the root spans.
Needs the otel extra (pip install "solvi[otel]"); without it, use to_otlp_json.
to_otlp_json ¶
Decisions as OTLP/JSON — the body of an ExportTraceServiceRequest (POST it to a collector's /v1/traces with Content-Type application/json) — as a dict; no OpenTelemetry package needed. The same spans as export(); trace and span ids are derived from the traces' hashes.