Skip to content

feat(dialect): typed replay messages (to_typed_messages) - #41

Merged
senamakel merged 2 commits into
mainfrom
fu-typed-row-dialect
Oct 1, 2026
Merged

senamakel merged 2 commits into
mainfrom
fu-typed-row-dialect

Conversation

@senamakel

@senamakel senamakel commented Oct 1, 2026 •

Copy link
Copy Markdown
Member

Design note

Current state. ToolDialect::to_provider_messages returns DialectMessage { role, content, extra_metadata }. For the native dialect a tool round is packed into content (the {content, tool_calls} / {tool_call_id, content} envelopes), so every host row bridge has to parse the envelope back out (the follow-up to #40 moved that codec here; hosts still round-trip through it).

Target. DialectMessage gains typed fields (tool_calls, tool_call_id, reasoning_content; serde-default, skipped when empty) and ToolDialect gains to_typed_messages: same pairing/order/metadata as to_provider_messages, but a native tool round keeps its structure in the fields and content is plain text. The default implementation returns the packed form, so the XML/P-Format/code dialects are unchanged (they have no structure to unpack). NativeDialect overrides it.

Compatibility. to_provider_messages is untouched, so every existing caller and byte of the packed form is identical. New fields default empty; new constructors DialectMessage::assistant_with_calls / tool_result. A struct literal of DialectMessage outside this crate needs the three new fields (use DialectMessage::new).

Proof. native_typed_replay_matches_the_packed_replay (same rows; re-encoding the typed message gives exactly the packed row), native_typed_replay_drops_unpaired_cycles_like_the_packed_replay, text_dialects_keep_the_packed_form_as_their_typed_form. cargo test -p tinytools-agent 386 passed; clippy -D warnings clean.

Part of the OpenHuman typed in-memory transcript row cut (tinyagents session PR + OpenHuman PR follow).

Summary by CodeRabbit

  • New Features
    • Conversation history can now be replayed with assistant tool calls and their results represented as typed messages, preserving call details, reasoning content, metadata, and result pairing.
    • Existing packed message formats remain available, and dialects without typed-message support continue using their established format.

senamakel and others added 2 commits October 1, 2026 14:00
Introduce a new native dialect module that provides foundational type definitions and a native implementation for the agent's dialect system. This change establishes the core type infrastructure needed to support dialect-specific behavior in the tinytools agent.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a `to_typed_messages` method to the native dialect that unpacks the envelope format into structured fields, and verify it produces the same output as the existing packed format. This enables downstream consumers to work with the message content directly without parsing the envelope string.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
@tinysweeper

tinysweeper Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Tiny Sweeper review

Tiny Sweeper reviewed this change across 6 lane(s) and found 3 active actionable finding(s). Detailed lane evidence and any incomplete work are listed below.

State: Incomplete
Priority: medium
Reviewed head: 2ce3d9c29f7f
Updated: 1790855209 (Unix time)

Review snapshot

Change surface Files Review signal Count
Production 3 Active findings 3
Tests 1 Noted findings 0
Documentation 0 Resolved findings 0
Configuration 0 Pending checks/questions 8

Completeness: Incomplete
Test assessment: No supported feature-to-test mapping was available; this does not mean tests are absent or passed.

What changed

The review could not produce a supported behavioral summary; inspect the cited changed surface and lane details below.

Features

None identified with supported citations.

Tests

No supported feature-to-test mapping was produced. Test execution is not inferred.

Findings

  • medium · tests · Pin the serde representation of the new DialectMessage fields — `DialectMessage` is a public payload type that is serialized/deserialized. The diff adds three new fields (`tool_calls`, `tool_call_id`, `reasoning_content`) with `#[serde(default, (crates/tinytools\-agent/src/dialect/types\.rs:60)
  • medium · tests · Verify that the native dialect's packed path also drops unpaired cycles — The test asserts that `to_typed_messages` and `to_provider_messages` return the same result for an unpaired tool call, but it does not independently verify that the packed path (`t (crates/tinytools\-agent/src/dialect/test\.rs:618)
  • medium · description · Propagate extra_metadata from ToolResultEntry in typed messages — The `to_typed_messages` override for `NativeDialect` propagates `extra_metadata` for assistant tool calls (line 201) but not for tool results. The PR description states that typed (\(pull request description\))

Could not review: crates/tinytools-agent/src/dialect/mod.rs, crates/tinytools-agent/src/dialect/native.rs, crates/tinytools-agent/src/dialect/test.rs, crates/tinytools-agent/src/dialect/types.rs

Before merge

  • Complete the critique review for crates/tinytools-agent/src/dialect/mod.rs, crates/tinytools-agent/src/dialect/native.rs, crates/tinytools-agent/src/dialect/test.rs, crates/tinytools-agent/src/dialect/types.rs.
  • Complete the security review for crates/tinytools-agent/src/dialect/mod.rs, crates/tinytools-agent/src/dialect/native.rs, crates/tinytools-agent/src/dialect/types.rs, crates/tinytools-agent/src/dialect/test.rs.

How this fits together

flowchart LR
  n0["NativeDialect<br/>changed"]:::changed
  n1["DialectMessage<br/>changed<br/>1 finding"]:::flagged
  n2["DialectRole<br/>changed<br/>1 finding"]:::flagged
  n3["response"]:::impacted
  n4["ToolDialect"]:::impacted
  n5["TranscriptEntry"]:::impacted
  n6["...ect_values_fallback_and_protocol_metadata"]:::impacted
  n7["one"]:::impacted
  n0 -->|implements| n4
  n1 -->|uses| n2
  n4 -->|uses| n1
  n4 -->|uses| n5
  n5 -->|uses| n1
  n6 -->|calls| n3
  n6 -->|tests| n3
  n6 -->|calls| n7
  n6 -->|tests| n7
  n7 -->|uses| n5
  classDef changed fill:#0d4429,stroke:#238636,color:#e6edf3
  classDef impacted fill:#161b22,stroke:#6e7681,color:#c9d1d9
  classDef flagged fill:#5a1e02,stroke:#d93f0b,color:#ffffff
  classDef blocking fill:#67060c,stroke:#f85149,color:#ffffff
Loading
Agent review details

critique

  • Conclusion: Neutral
  • Scope reviewed: incomplete; unanswered: crates/tinytools-agent/src/dialect/mod.rs, crates/tinytools-agent/src/dialect/native.rs, crates/tinytools-agent/src/dialect/test.rs, crates/tinytools-agent/src/dialect/types.rs
  • Lane summary: Reviewed 0 files; 0 findings. 4 files could not be reviewed: crates/tinytools-agent/src/dialect/mod.rs, crates/tinytools-agent/src/dialect/native.rs, crates/tinytools-agent/src/dialect/test.rs, crates/tinytools-agent/src/dialect/types.rs.

security

  • Conclusion: Neutral
  • Scope reviewed: incomplete; unanswered: crates/tinytools-agent/src/dialect/mod.rs, crates/tinytools-agent/src/dialect/native.rs, crates/tinytools-agent/src/dialect/types.rs, crates/tinytools-agent/src/dialect/test.rs
  • Lane summary: Reviewed 0 files; 0 findings. 4 files could not be reviewed: crates/tinytools-agent/src/dialect/mod.rs, crates/tinytools-agent/src/dialect/native.rs, crates/tinytools-agent/src/dialect/types.rs, crates/tinytools-agent/src/dialect/test.rs.

tests

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: The change adds a typed message path for the native dialect, preserving structured tool calls, tool results, and reasoning content in dedicated fields instead of packing them into JSON envelopes. The tests cover the typed path, its equivalence to the packed path, and the fallback for text dialects. However, the new `DialectMessage` fields lack a serde-representation pinning test as required by the repository's coding rules, and one test asserts equivalence that depends on shared pairing logic without verifying that the packed path actually uses that logic. _The code index is behind this pull request (indexed at `e4eda5cfd0b2`), so retrieved context may be out of date._ _Memory was unavailable (model: cortex: v1/recall: timed out after 10s), so this review ran without it._
  • Evidence: crates/tinytools\-agent/src/dialect/types\.rs — Pin the serde representation of the new DialectMessage fields
  • Evidence: crates/tinytools\-agent/src/dialect/test\.rs — Verify that the native dialect's packed path also drops unpaired cycles

commits

  • Conclusion: Neutral
  • Scope reviewed: all assigned evidence
  • Lane summary: Nothing sensitive found in what this pull request commits.

description

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: The PR adds typed replay messages to the dialect, with a new trait method `to_typed_messages` and new fields on `DialectMessage`. The implementation looks sound except that `NativeDialect::to_typed_messages` does not propagate `extra_metadata` from `ToolResultEntry` objects, violating the stated guarantee of preserving metadata. The rest of the changes are clean and well-tested. Merge after fixing the metadata propagation. _The code index is behind this pull request (indexed at `e4eda5cfd0b2`), so retrieved context may be out of date._ _Memory was unavailable (model: cortex: v1/recall: timed out after 10s), so this review ran without it._
  • Evidence: \(pull request description\) — Propagate extra_metadata from ToolResultEntry in typed messages

e2e

  • Conclusion: Neutral
  • Scope reviewed: all assigned evidence
  • Lane summary: No end-to-end harness in this repository: no e2e test files and no e2e workflow.
Evidence and run details
  • Models: ladder/vectors, deepseek/deepseek-v4-flash
  • Spend: $0.003426
  • Tokens: 26096 input · 9952 output · 9984 cached · 529 embedding
Head State Pass summary
2ce3d9c29f7f incomplete 3 active finding(s), 0 resolved finding(s) (at 1790855209)

tinysweeper 0.1.0

@coderabbitai

coderabbitai Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

🧰 Additional context used
📚 Code guidelines (2)
README.md — configured
AGENTS.md — auto-discovered

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 2984305a-92ec-4e91-a203-fd8668e1cefa

📥 Commits

Reviewing files that changed from the base of the PR and between 48c1d5e and 2ce3d9c.

📒 Files selected for processing (4)
  • crates/tinytools-agent/src/dialect/mod.rs
  • crates/tinytools-agent/src/dialect/native.rs
  • crates/tinytools-agent/src/dialect/test.rs
  • crates/tinytools-agent/src/dialect/types.rs

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The dialect API now supports replaying transcript history as typed messages. DialectMessage carries assistant tool calls, reasoning content, and tool-result call IDs. NativeDialect builds typed messages from paired transcript entries, while other dialects use the existing packed provider-message representation by default.

Changes

Typed dialect replay

Layer / File(s) Summary
Typed replay message contract
crates/tinytools-agent/src/dialect/types.rs, crates/tinytools-agent/src/dialect/mod.rs
DialectMessage adds fields and constructors for typed assistant tool calls and tool results. ToolDialect adds to_typed_messages, which defaults to to_provider_messages.
Native typed replay
crates/tinytools-agent/src/dialect/native.rs, crates/tinytools-agent/src/dialect/test.rs
NativeDialect converts paired transcript entries into typed messages. Tests cover message fields, re-encoding, unpaired tool cycles, and the XML dialect’s packed-message default.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~15 minutes

Change: Feature

Merge Risk: ⚪ Minimal · up to 2ce3d

The change adds structured native replay while retaining existing packed replay behavior. No actionable merge-blocking issue is established; it is mergeable subject to normal checks.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 2ce3d

The new API changes message representation rather than tool permissions. Existing packed replay and call/result pairing checks remain available. Downstream adapter behavior is not fully established, particularly for absent versus empty assistant text.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The direct design exposure is the public dialect contract and hosts consuming native transcript rows. Any privileged or cross-tenant effect depends on downstream adapters; their effective authority and exposure cannot be sized from the available library evidence.

Trust Boundaries and Controls

  • observed — Pairing checks adjacency and equality of nonempty call-ID sets for structured transcript cycles. Chat entries bypass those cycle checks and are cloned, including any typed fields they already carry. Pairing is therefore a structural control, not provenance validation or authorization of host-provided messages.

Resilience and Maintainability Implications

  • observed — The shared pairing helper compares sets rather than ID multiplicity, so duplicate IDs are not independently rejected. This behavior predates the PR and is shared by packed and typed replay; it is not an introduced concern.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 12 functions across 4 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding typed replay messages through to_typed_messages.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


A rabbit checks the message trail,
Typed calls and results set sail.
Plain text stays clear, IDs align,
Reasoning joins the typed design.
The replay hops from turn to turn,
While packed forms keep their old return.

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-01T11:47:19.675320Z 2ce3d9c PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2ce3d9c29f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

/// The native tool calls an assistant message made. Empty everywhere except
/// a typed native assistant turn.
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub tool_calls: Vec<NativeToolCall>,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Bump the minor version for the breaking fields

Adding fields to the public, non-non_exhaustive DialectMessage is source-incompatible because every downstream struct literal must now initialize three additional fields. The workspace still advertises version 0.5.0, so this breaking public-surface change needs a 0.6.0 version bump or a design that does not extend the existing public struct.

AGENTS.md reference: AGENTS.md:L264-L266

Useful? React with 👍 / 👎.

Comment on lines +75 to +76
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub tool_calls: Vec<NativeToolCall>,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Pin the expanded message wire representation

Add a serialization regression test for DialectMessage covering the three new field names, their emitted values, and their default omission during serialization. The new replay test only examines Rust fields and manually repacks envelopes, so it would not catch an accidental serde rename or removal of a skip_serializing_if rule even though this type is a persisted/provider-facing payload whose wire representation must be pinned.

AGENTS.md reference: AGENTS.md:L175-L177

Useful? React with 👍 / 👎.

@tinysweeper tinysweeper Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tinysweeper found nothing blocking, but could not review everything, so this is not an approval: crates/tinytools-agent/src/dialect/mod.rs, crates/tinytools-agent/src/dialect/native.rs, crates/tinytools-agent/src/dialect/test.rs, crates/tinytools-agent/src/dialect/types.rs.

             $0.0034 · 26,096 in / 9,952 out · 9,984 cached (38%) · ladder/vectors, deepseek/deepseek-v4-flash · 529 embedded
tests:       $0.0019 · 15,113 in / 4,102 out · 3,072 cached (20%) · deepseek/deepseek-v4-flash
description: $0.0010 · 6,250 in  / 3,222 out · 2,304 cached (37%) · deepseek/deepseek-v4-flash

/// [`ToolDialect::to_typed_messages`](super::ToolDialect::to_typed_messages)
/// emits the same round with the structure in the typed fields below and
/// `content` holding plain text only.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

priority medium tests confident

Pin the serde representation of the new DialectMessage fields

DialectMessage is a public payload type that is serialized/deserialized. The diff adds three new fields (tool_calls, tool_call_id, reasoning_content) with #[serde(default, skip_serializing_if = ...)] to maintain backward compatibility, but there is no test that asserts the JSON shape of a DialectMessage with and without these fields remains stable. The repository's coding rules require payload types to pin their serde representation in a unit test. Without such a test, a future change that accidentally alters the wire format (e.g., changes the skip_serializing_if condition or adds a rename) would go undetected.

[RULE] missing-serde-pin-test ·

}

#[test]
fn native_typed_replay_drops_unpaired_cycles_like_the_packed_replay() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

priority medium tests likely

Verify that the native dialect's packed path also drops unpaired cycles

The test asserts that to_typed_messages and to_provider_messages return the same result for an unpaired tool call, but it does not independently verify that the packed path (to_provider_messages) actually drops the cycle. If the packed path were changed to keep unpaired cycles (or if it never dropped them in the first place), this test would still pass because both paths return the same (wrong) output. The test should either assert an expected length (e.g., assert_eq!(typed.len(), 1)) or explicitly verify that the assistant turn is absent, to pin the dropping behavior independently of the equivalence assertion.

[RULE] unverified-assumption ·

@senamakel
senamakel merged commit 9291cad into main Oct 1, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant