feat(atif): add Harbor ATIF trajectory format support#11
Open
jfeldstein wants to merge 2 commits into
Open
Conversation
Add Vitest + npm test script; cover atif-trajectory (validation, parse, serialize, ISO timestamps, ContentPart rules) and local-data-parser (dispatch table, ATIF-before-Codex guard). These tests fail until the implementation commit.
Implement ATIF v1 schema/types, detection, parse/serialize/validate, <euphony-atif> viewer, app and worker integration with extracted local-data-parser for testability. Adds uv.lock.
37a0f61 to
0d27cb7
Compare
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.
Currently
Euphony renders Harmony conversations and Codex JSONL session logs, with a dedicated
<euphony-codex>viewer for the latter. Harbor's Agent Trajectory Interchange Format (ATIF) — a single-document JSON format that's structurally distinct from both — is not detected and falls through to the generic JSON viewer, which loses the trajectory-specific rendering (tool-call ↔ observation pairing, agent metadata, step metrics, etc.). Loading a*.trajectory.jsonfromharbor/tests/golden/...today shows raw JSON with no conversational structure.Change Summary
src/utils/atif-trajectory.ts: schema types,isAtifTrajectorydetector,parseAtifTrajectory(ATIF → Conversation),serializeAtifTrajectoryround-trip, andvalidateAtifTrajectory. Steps map to one primary dialogue message plus optional reasoning, tool-call, and observation messages. Tool calls and their matching observations correlate byfunction_name(built from a per-steptool_call_id → function_namemap) so the viewer renders the call/output pair under a single tool label.<euphony-atif>Lit shell that mirrors<euphony-codex>and delegates to<euphony-conversation>. Read-only by design (nois-editableproperty forwarded).src/components/app/local-data-parser.ts: extracted the inline parser out oflocal-data-worker.tsso it's unit-testable. ATIF detection runs before the Codex JSONL check.src/components/app/app.ts:DataType.ATIF, state field, render branch, and URL-load branch.step_id, agent-only fields, ContentPart shape (text/image required fields, imagemedia_typeliteral), tool-call ↔ observationsource_call_idreferences, strict ISO-8601 timestamps,tool_definitionsenvelope shape.src/utils/atif-trajectory.test.ts(12 cases) andsrc/components/app/local-data-parser.test.ts(6 cases) — both run under vitest.Acceptance Criteria
pnpm test— vitest passes (18/18)pnpm build— clean?path=https://...) → renders with Session/Agent/Schema/Steps chips and tool-call/output pairs sharing a labelexamples/simple-harmony-convos.jsonl→dataType=conversation→<euphony-conversation>)session_meta/event_msg/response_itemJSONL →dataType=codex→<euphony-codex>; ATIF detection runs first but only matches single-element arrays whose first element looks like an ATIF document)serializeAtifTrajectory(parseAtifTrajectory(x).conversation)deep-equalsxfor all harbor goldens (8/8 terminus_2 fixtures + 2/2 openhands*.trajectory.jsonfixtures, all v1.5/v1.6, deep-equal)*.trajectory.jsoninharbor/tests/golden/{terminus_2,openhands}routes toDataType.ATIFand mounts<euphony-atif>(10/10); both*.traces.jsonfiles (raw OpenHands replay events, not ATIF) correctly fall back toDataType.JSONNotes
<euphony-atif>does not exposeis-editable, and?disable-editing-mode-save-button=${true}is set as belt-and-suspenders.serializeAtifTrajectoryreturns the originally-parsed document verbatim — it does not synthesize ATIF from edits to the in-memory Conversation, because the schema requires sequential step IDs, agent name/version, and tool-call/observation ID references that arbitrary Harmony conversations don't reliably preserve. If lossy export of edited trajectories ever becomes a requirement, build a separate writer rather than changing this one.JSON.parse), so the discriminator is cheap and unambiguous (schema_version: "ATIF-*"+agent.{name,version}+ non-emptysteps). Putting it first prevents a future ATIF version with an event-shaped extra field from being misclassified as Codex.tool_definitionsonly aslist[dict[str, Any]]but the RFC text mandates the OpenAI{type, function}envelope; we enforce the RFC. Imagemedia_typeis restricted to the four-MIME Literal harbor uses. Strict ISO-8601 timestamp regex matchesStep.validate_timestampinharbor/src/harbor/models/trajectories/step.py; the parser uses the same check so a timestamp that fails validation also yieldscreate_time === undefinedrather than aDate.parse-coerced value.