From 5c9590ada73789c16619be2222f6c85221108211 Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Wed, 8 Apr 2026 21:14:54 -0400 Subject: [PATCH] Custom Deserialize for NodeLeaf: recompute tokens on deserialization token_ids are not serialized (serde skip), so deserialized nodes had 0 tokens. The custom Deserialize impl recomputes tokens from the body text, restoring the invariant at the reconstruction boundary. No separate recompute step needed. Co-Authored-By: Proof of Concept --- src/agent/context.rs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/agent/context.rs b/src/agent/context.rs index e550885..79ca030 100644 --- a/src/agent/context.rs +++ b/src/agent/context.rs @@ -63,7 +63,8 @@ pub enum NodeBody { } /// A leaf node: typed content with cached token IDs. -#[derive(Debug, Clone, Serialize, Deserialize)] +/// Token IDs are not serialized — they're recomputed on deserialization. +#[derive(Debug, Clone, Serialize)] pub struct NodeLeaf { body: NodeBody, #[serde(skip)] @@ -71,6 +72,23 @@ pub struct NodeLeaf { timestamp: Option>, } +impl<'de> Deserialize<'de> for NodeLeaf { + fn deserialize>(deserializer: D) -> Result { + #[derive(Deserialize)] + struct Raw { + body: NodeBody, + timestamp: Option>, + } + let raw = Raw::deserialize(deserializer)?; + let token_ids = if raw.body.is_prompt_visible() { + tokenizer::encode(&raw.body.render()) + } else { + vec![] + }; + Ok(NodeLeaf { body: raw.body, token_ids, timestamp: raw.timestamp }) + } +} + /// A node in the context AST. #[derive(Debug, Clone, Serialize, Deserialize)] pub enum AstNode {