Adapters and the trace import contract

June 4, 2026 · View on GitHub

lessonweaver mines a single internal shape: the TraceBundle. Real traces arrive in many formats (sibling tools, OpenTelemetry spans, CI logs, custom loggers). The TraceImporter protocol (issue #52) defines one small, dependency-free contract every importer follows, so importers stay swappable and testable instead of being designed ad hoc.

The TraceImporter protocol

from typing import Any, Protocol, runtime_checkable

@runtime_checkable
class TraceImporter(Protocol):
    def can_import(self, source: dict[str, Any]) -> bool: ...
    def import_trace(self, source: dict[str, Any]) -> TraceBundle: ...
  • can_import — cheap recognition. Return True only for payloads this importer understands (a schema marker, or a structural fingerprint). It must not raise.
  • import_trace — convert a recognized payload into a TraceBundle. Raise ValueError with a human-readable message for a recognized-but-invalid payload.

Importers are deterministic and dependency-free: no network, no LLM, no importing the source system. They map a dict onto the schema.

Built-in importers (core)

ImporterInputNotes
DictTraceImportercanonical lessonweaver trace JSONload_trace_bundle delegates to it
FailureCaseImporterreplayable failure case artifactgoverned path for issue #82

DictTraceImporter makes the canonical loader a special case of the protocol: load_trace_bundle reads the JSON file, then calls import_trace.

When writing an adapter for a new format:

  1. Identify the trace id, task, and source. Every bundle needs a stable trace_id.
  2. Map events. Translate each source record to a TraceEventType. The conservative detector keys on these in particular:
    • a human fix → human_correction
    • a failed grade/check → evaluation_result with status="failed"
    • a failure then a recovery → error followed by retry (with a successful outcome), or tool_call failure then success
    • a workflow misstep → workflow_step (+ a following error)
  3. Set the outcome. Use corrected_by_human when a human fix is present, otherwise success / failure / unknown.
  4. Preserve provenance. Put source-specific identifiers in metadata (on the bundle and/or events). Unknown source fields can be dropped or carried in metadata; lessonweaver ignores unknown keys.
  5. Keep it optional. Concrete adapters for external systems live in examples/, never in core (see interoperability).

Required vs optional bundle fields

FieldRequiredDefault if omitted
trace_idyes
sourceyes (canonical)importer-supplied
taskrecommended""
eventsyes (canonical)[]
outcomerecommended"unknown"
metadatano{}

The failure case artifact (issue #82)

FailureCaseImporter accepts a replayable failure artifact — mirroring the planned weaver-spec FailureCaseArtifact (dgenio/weaver-spec#72) — and maps it to a bundle so the normal loop applies. Recognized keys:

KeyMeaning
failure_id (or id)stable id → trace_id (required)
failure.summary / failure.detailbecomes a failed evaluation_result event
failure.severitypreserved in provenance
correction.summarybecomes a human_correction event
replay.ref / replay.reproduciblepreserved in provenance
task, source, outcome, schemaoptional

Provenance is stored under metadata["failure_case"] and propagated onto every resulting candidate by candidates_from_failure_case. See examples/failure_cases/.

Known future adapter candidates