[AAASM-1442] 🔌 (python-sdk): Bridge AuditEvent dataclass into Rust encoder + wire round-trip test#45
Merged
Chisanan232 merged 8 commits intoMay 20, 2026
Conversation
Bridges the Python dataclass's open-ended `str` API to the proto's enum-typed `ActionType` / `Decision` wire format. Used by the forthcoming AuditEvent py↔proto converter (AAASM-1664). Refs AAASM-1662.
`call_stack_node_from_py` reads the dataclass fields via PyO3 reflection and walks children recursively. `latency_ms == None` ↔ proto's int64 default of 0. `call_stack_node_to_py` imports `agent_assembly.types.CallStackNode` and constructs an instance per node. `latency_ms == 0` on the wire projects back to `None` on the dataclass, preserving the "producer did not record" semantic. Refs AAASM-1663.
`audit_event_from_py` reflects on `event_id` / `agent_id` / `action_type` / `decision` / `trace_id` / `span_id` / `parent_span_id` / `labels` / `call_stack`. The dataclass's flat `agent_id: str` wraps to an `AgentId` message with empty `org_id`/`team_id` (round-trip preserves the string). `audit_event_to_py` unwraps `AgentId.agent_id` back to the flat string and applies the inverse enum mappings. Refs AAASM-1664.
Two free `#[pyfunction]`s on `agent_assembly._core`: * `audit_event_to_wire_bytes(event)` — converts the dataclass to `aa_proto::AuditEvent` and returns prost-encoded `bytes`. * `audit_event_from_wire_bytes(data)` — decodes wire bytes back into a `agent_assembly.types.AuditEvent`. Raises `ValueError` on malformed input. Refs AAASM-1665.
Public Python surface for the AAASM-1442 bridge. Methods import `agent_assembly._core` lazily so `types.py` stays usable in pure-Python installs. When the native crate is absent, both raise `ImportError` with an actionable message pointing at `maturin develop`. Refs AAASM-1666.
Builds an AuditEvent with an LLM → tool → result call_stack, encodes via to_wire_bytes(), decodes via from_wire_bytes(), and asserts structural equality with the original. Covers the deferred test case AAASM-1435 scope item #4 — verifying no data loss through the PyO3 + prost pipeline. Skips when the native _core extension is absent, matching the test/bench/test_report_llm_call_roundtrip.py convention. Refs AAASM-1667.
Asserts the back-compat path: an AuditEvent with call_stack=[] encodes to bytes that match what a pre-AAASM-1419 producer would emit, and the decoded dataclass surfaces the empty list — not None or a missing attribute. Refs AAASM-1668.
The CallStackNode `kind` field is proto `string`, not enum — the Python `Literal` narrowing is author-side only. Asserts the bridge preserves an out-of-Literal kind value verbatim, leaving room for future producers to add categories without a wire-layer schema bump. Refs AAASM-1669.
|
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Description
Bridges the pure-Python
agent_assembly.AuditEvent/CallStackNodedataclasses (from AAASM-1435) into the Rustaa_protoencoder via PyO3, then ships the three deferred unit tests AAASM-1435 left behind.Public surface added to the dataclass:
Both methods import
agent_assembly._corelazily and raise an actionableImportErrorin pure-Python installs, sotypes.pystays usable without the native crate.Implementation lives in
rust/aa-ffi-python/src/lib.rs:action_type_from/to_str+decision_from/to_str— string ↔ proto-enum mapping so the dataclass's open-endedstrfields round-trip cleanly through the enum-typed wire format.call_stack_node_from/to_py— recursive PyO3 ↔ proto converter;latency_ms == 0↔Nonepreserves the "producer did not record" semantic.audit_event_from/to_py— top-level converter. The flatagent_id: strwraps to anAgentId { agent_id, org_id: "", team_id: "" }(org/team not surfaced on the dataclass today).#[pyfunction]s —audit_event_to_wire_bytesandaudit_event_from_wire_bytes— registered on the_coremodule.Type of Change
Breaking Changes
The proto contract is unchanged; only new methods are added to
AuditEvent. No existing call-sites or wire bytes are altered.Related Issues
action_type/decisionstring↔enum helpersCallStackNodepy↔proto convertersAuditEventpy↔proto convertersaudit_event_to/from_wire_bytesPyO3 functionsAuditEvent.to_wire_bytes()/from_wire_bytes()Python helperscall_stackround-trip testcall_stack) decode testkindround-trip testTesting
test/unit/test_audit_event_wire_roundtrip.py:latency_ms+ populatedlabelsround-trips with no data loss.call_stack(orcall_stack=[]) decode to dataclass withcall_stack == [].kindround-trip —kindis protostring(open-ended), so a value outside theCallStackNodeKindLiteralsurvives the wire layer unchanged.pytest.importorskip("agent_assembly._core"), matchingtest/bench/test_report_llm_call_roundtrip.pyconvention.cargo check -p aa-ffi-pythonclean (5 pre-existing pyo3 0.20 warnings unchanged).maturin develop --releasebuilds the native crate; new tests pass with native module loaded.pytest test/unit test/integration— 317 passed, 7 skipped (no regressions).mypy agent_assembly— 13 pre-existing baseline errors, zero new errors in changed files.ruff checkclean on changed files;ruff formatapplied.Out of scope
agent_idfield on the dataclass is a flatstr; the proto'sAgentIdcarriesorg_id+team_idthat the dataclass does not surface today. The bridge wraps with emptyorg_id/team_idand unwraps theagent_idstring back — preserving the round-trip for what the dataclass actually exposes.aa-protorev (ed4aa11a) already carries thecall_stackfield added in AAASM-1419; no pin bump needed (per DoD).test/bench/test_report_llm_call_roundtrip.py(event_type"LlmCall"no longer matches the currentaa_core::AuditEntryschema) is unrelated to this PR and only manifests locally when_coreis built. CI's pure-Python install skips that test.Checklist
🤖 Generated with Claude Code