nat.atof.scripts.atof_to_atif_converter#
ATOF-to-ATIF converter.
Converts a list of ATOF events (JSON-Lines wire format from agent runtime subscriber callbacks) into an ATIF Trajectory using NAT’s native models.
Event model: 2 event kinds (ScopeEvent / MarkEvent) per ATOF spec
v0.1. Dispatch keys on (kind, scope_category, category). Category-specific
typed fields live inside the category_profile sub-object (spec §4.4) —
model_name for llm, tool_call_id for tool.
Output conforms to ATIF v1.7. See the conversion rules in
atif-alignment/docs/atof-to-atif-mapping.md; rule identifiers (R1-R12)
referenced inline map to that document.
Producer-specific payload parsing is delegated to pluggable extractors
(nat.atof.extractors) keyed on the event’s declared data_schema.
Events without a matching registered extractor fall back to built-in
OpenAI-chat-completions / generic extractors. Two fail-fast guardrails
catch producers that would otherwise silently lose content:
DataSchemaViolationError— when the producer declares adata_schemaregistered innat.atof.schemasandevent.datafails JSON-Schema validation against it. Fires in the pre-pass.ShapeMismatchError— whenevent.datais non-empty but the resolved extractor yields nothing usable (payload would drop).
Attributes#
Exceptions#
Raised when an event's non-empty |
|
Raised when an event declares a registered |
Functions#
|
Validate |
|
Build a v1.7 ancestry dict for embedding in |
|
Build producer-scoped invocation info for step.extra (not part of ATIF v1.7 core). |
|
Tier-1 boundary-step message serializer. |
|
|
|
|
|
UUID → category lookup from scope-start events. |
|
UUID → parent_uuid for all unique UUIDs in the stream. |
|
Find agent scope-starts whose parent is a dispatcher scope (R7). |
|
Events whose ancestry chain reaches root_uuid (inclusive of events with uuid == root_uuid). |
|
Convert typed ATOF events to ATIF v1.7 step dicts. |
|
Build validated Step instances from raw step dicts. |
|
Convert a list of ATOF events to an ATIF v1.7 Trajectory. |
|
Internal converter supporting recursion on subagent sub-streams. |
|
Read an ATOF JSON-Lines file and convert to an ATIF Trajectory. |
Walk a dumped ATIF trajectory dict and ensure every |
Module Contents#
- logger#
- exception ShapeMismatchError( )#
Bases:
ValueErrorRaised when an event’s non-empty
dataproduced empty extraction.The resolved
LlmPayloadExtractorfor an event’sdata_schemacould not pull any usable content out of a non-empty payload. The would-be-emitted content is silently dropped — this exception surfaces that case as a hard failure so callers can either (a) fix the producer to emit the expected shape, (b) declare a matchingdata_schemaand register a profile-specific extractor viaregister_llm_extractor(), or (c) wrap the call and handle the drop explicitly.- Attributes:
kind:
"llm_input"or"llm_output"— which extraction missed. uuid: UUID of the offending event. data_schema: The producer-declareddata_schema, if any. data_keys: Sorted top-level keys observed indata.
Initialize self. See help(type(self)) for accurate signature.
- kind#
- uuid#
- data_schema#
- data_keys#
- exception DataSchemaViolationError( )#
Bases:
ValueErrorRaised when an event declares a registered
data_schemabut itsdatafails JSON-Schema validation against it.Producers declaring a schema enter a contract: their payload MUST conform. A violation here either reveals a producer bug or signals that the declared schema is wrong. Either way, downstream extraction would likely drop content, so the converter fails fast with actionable context — the offending event UUID, the declared schema identifier, the JSON-pointer path to the validation failure, and the underlying validator message.
Events whose
data_schemais NOT in the registry skip validation entirely (aWARNINGis logged instead).- Attributes:
uuid: UUID of the offending event. data_schema: The producer-declared
{name, version}identifier. path: JSON-pointer segments to the offending value. message: The underlyingjsonschemavalidator message.
Initialize self. See help(type(self)) for accurate signature.
- uuid#
- data_schema#
- path#
- message#
- _validate_event_data_schema(event: nat.atof.events.Event) None#
Validate
event.dataagainst its declared, registereddata_schema.Events without a
data_schemapass through untouched (the schema field is optional per spec §2).Events with a
data_schemanot innat.atof.schemas.SCHEMA_REGISTRYemit aWARNINGand pass through; producers can register custom schemas vianat.atof.schemas.register_schema().Events with a registered schema raise
DataSchemaViolationErroron validation failure.
- _build_ancestry( ) dict#
Build a v1.7 ancestry dict for embedding in
Step.extra["ancestry"]orToolCall.extra["ancestry"]. Matchesnat.atif.atif_step_extra.AtifAncestryshape:parent_id/parent_nameare null at the root.
- _build_invocation_info( ) dict#
Build producer-scoped invocation info for step.extra (not part of ATIF v1.7 core).
- _serialize_root_data(data: Any) str | None#
Tier-1 boundary-step message serializer.
Used to lift an opaque root scope’s
datapayload into the ATIF user/agent boundary steps emitted by Branch A (root scope-start → user step) and Branch B (root scope-end → agent step) of the main converter loop.Rules (locked-in by 260501-1ko quick plan brief):
str→ return as-is.dictwith exactly one entry whose value is astr→ return that string (single-key-dict lift heuristic — covers the common{"query": "..."}/{"result": "..."}shapes).dictwith anything else (multi-key, or single-key whose value is non-str and non-empty) →json.dumps(data, separators=(",", ":"))(compact JSON).Noneor empty dict →None(caller skips emission entirely; no boundary step is produced).Any other type → fall through to compact JSON for safety so we never silently drop content.
- _build_category_map(
- events: list[nat.atof.events.Event],
UUID → category lookup from scope-start events.
- _build_parent_map(
- events: list[nat.atof.events.Event],
UUID → parent_uuid for all unique UUIDs in the stream.
- _find_subagent_roots( ) list[nat.atof.events.ScopeEvent]#
Find agent scope-starts whose parent is a dispatcher scope (R7).
A dispatcher scope is a
toolscope (regular delegation) or acontextscope (R10 context-management subagent, e.g. a compaction subagent that summarizes prior turns).
- _collect_descendants( ) list[nat.atof.events.Event]#
Events whose ancestry chain reaches root_uuid (inclusive of events with uuid == root_uuid).
eventspreserves the caller’s order; the returned list preserves it too.
- _events_to_step_dicts(
- events: list[nat.atof.events.Event],
- subagent_ref_by_tc_id: dict[str, dict] | None = None,
- subagent_ref_by_context_uuid: dict[str, dict] | None = None,
Convert typed ATOF events to ATIF v1.7 step dicts.
subagent_ref_by_tc_idmaps atool_call_idto aSubagentTrajectoryRef-shaped dict (R7 tool-wraps-agent).subagent_ref_by_context_uuidmaps acontext-scope UUID to aSubagentTrajectoryRef-shaped dict (R10 context-wrapped subagent, e.g. a compaction subagent). Either map MAY be empty.- Raises:
- DataSchemaViolationError: if an event declares a registered
data_schemaand itsdatafails validation.- ShapeMismatchError: if an
llmscope event’s non-emptydata yields no extractable content (would drop payload silently).
- _materialize_steps(step_dicts: list[dict]) list[nat.atif.step.Step]#
Build validated Step instances from raw step dicts.
ATIF v1.7: ancestry is no longer a typed top-level field — it’s embedded in
Step.extra["ancestry"]andToolCall.extra["ancestry"]as plain dicts (AtifAncestryshape, seenat.atif.atif_step_extra). No model conversion is needed here; the dicts pass through to theextrafield unchanged.
- convert(
- events: list[nat.atof.events.Event],
Convert a list of ATOF events to an ATIF v1.7 Trajectory.
- Raises:
- DataSchemaViolationError: if an event declares a registered
data_schema(seenat.atof.schemas) and itsdatafails JSON-Schema validation.- ShapeMismatchError: if an
llmscope event carries non-empty datathat the reference extractors cannot parse. Silently dropping such a payload would lose producer content, so the converter fails fast instead.
- _convert_impl( ) nat.atif.trajectory.Trajectory#
Internal converter supporting recursion on subagent sub-streams.
When
explicit_root_uuidis provided (recursive call), the root agent metadata is taken from the event withuuid == explicit_root_uuidrather than by searching forparent_uuid is None.
- convert_file(
- input_path: str | pathlib.Path,
- output_path: str | pathlib.Path | None = None,
Read an ATOF JSON-Lines file and convert to an ATIF Trajectory.
- Raises:
ShapeMismatchError: see
convert().
- _ensure_subagent_trajectory_path_explicit(obj: Any) None#
Walk a dumped ATIF trajectory dict and ensure every
subagent_trajectory_ref[i]entry hastrajectory_pathexplicitly present (null for embedded refs).model_dump(exclude_none=True)strips optional None-valued fields, which produces valid ATIF v1.7 but loses back-compat visual alignment with ATIF v1.6 consumers that expect the key. Keeping the field explicit asnullis spec-allowed (the field is optional, andnullis a valid value) and aids consumer-side inspection.