Skip to content

harness: size token estimation over all content blocks, not just text#35

Merged
senamakel merged 2 commits into
tinyhumansai:mainfrom
M3gA-Mind:fix/message-token-estimate
Jul 8, 2026
Merged

harness: size token estimation over all content blocks, not just text#35
senamakel merged 2 commits into
tinyhumansai:mainfrom
M3gA-Mind:fix/message-token-estimate

Conversation

@M3gA-Mind

@M3gA-Mind M3gA-Mind commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Token estimation for context-window gating counted only ContentBlock::Text, so a transcript dominated by images, large tool-result JSON, or model reasoning under-counted by orders of magnitude — SummarizationPolicy::should_summarize and the MaxTokens trim would silently never fire even as the real window overflowed.

Change

  • Add ContentBlock::estimated_char_weight() / Message::estimated_char_weight() that account for all blocks: text, JSON (serialized length), reasoning (Thinking/RedactedThinking), provider extensions, and a flat per-image weight (IMAGE_CHAR_WEIGHT ≈ 1024 tokens; images tokenize to a roughly fixed count regardless of encoded byte length).
  • Route message_token_estimate (summarization) through it.
  • The visible-text accessors (as_text/text/char_len) are unchanged, so reasoning still never leaks into visible assistant output.

Tests

estimated_char_weight_counts_non_text_blocks, estimated_char_weight_costs_images_that_carry_no_visible_text. cargo fmt --check, cargo clippy --lib --tests -- -D warnings, and cargo test --lib all pass.

Closes tinyhumansai/openhuman#4633

Summary by CodeRabbit

  • New Features

    • Improved message size estimation to account for images, JSON, reasoning blocks, and other non-visible content.
    • Added more accurate token budgeting for conversation trimming and summarization.
  • Bug Fixes

    • Reduced undercounting of multimodal or tool-heavy messages, helping keep trimming and summarization decisions more reliable.
  • Tests

    • Added coverage for mixed-content messages and image-only messages to verify the new estimation behavior.

Images, structured JSON (large tool results), and model reasoning occupy real
context, but char_len counts only Text blocks — so a multimodal or tool-heavy
transcript under-counts and should_summarize / MaxTokens trim silently never
fire. Add Message::estimated_char_weight over all content blocks and route the
token estimator through it; the visible-text accessors (as_text/text/char_len)
are left unchanged so reasoning still never leaks into visible output.
@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 35 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f0d85af0-30ff-4c00-bd27-ccea0b9fea12

📥 Commits

Reviewing files that changed from the base of the PR and between c28f347 and 94b0d99.

📒 Files selected for processing (2)
  • src/harness/model/mod.rs
  • src/harness/stream/mod.rs
📝 Walkthrough

Walkthrough

Adds estimated_char_weight() methods to ContentBlock and Message that compute an approximate character weight covering text, JSON, images, thinking/reasoning, and provider extension blocks. Routes message_token_estimate in the summarization module to use this new weight instead of char_len(), and adds unit tests.

Changes

Weighted Token Estimation

Layer / File(s) Summary
Estimation contract and constant
src/harness/message/mod.rs
Adds IMAGE_CHAR_WEIGHT constant and ContentBlock::estimated_char_weight() computing per-block weight across text, JSON, images, thinking, redacted reasoning, and provider extensions.
Message-level aggregation and tests
src/harness/message/mod.rs, src/harness/message/test.rs
Adds Message::estimated_char_weight() summing per-block weights across content, plus tests verifying JSON/thinking contributions and non-zero weight for image-only, zero-visible-text messages.
Summarization heuristic wiring
src/harness/summarization/mod.rs
Switches message_token_estimate to compute the chars/4 token estimate from estimated_char_weight() instead of char_len(), updating related comments.

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

Sequence Diagram(s)

Not applicable — this change is a localized heuristic/calculation update without multi-component control flow.

Possibly related issues

Poem

A rabbit counted chars with care,
"Just text?" I sniffed, "that's not quite fair!"
Images, JSON, thoughts unseen —
now all weighed in, precise and clean.
🐰📏 Tokens estimated true,
compaction fires when it's due!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: token estimation now considers all content blocks, not just text.
Linked Issues check ✅ Passed The PR matches #4633 by adding all-block size estimation and routing message_token_estimate through it while leaving visible-text accessors unchanged.
Out of Scope Changes check ✅ Passed The reported changes stay focused on token-estimation weighting and related tests/docs, with no obvious unrelated scope.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (1)
src/harness/message/mod.rs (1)

68-90: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Repeated .to_string() serialization on hot path.

ContentBlock::Json/ProviderExtension weight requires a full serialization of the value on every call. Since this feeds message_token_estimate, which is invoked per-message on every should_summarize/trim_messages pass over the transcript, large tool-result JSON payloads will be re-serialized repeatedly as the conversation grows. This is an intentional accuracy tradeoff per the PR's design, but worth keeping in mind if this becomes a hot path bottleneck for large transcripts with big JSON blocks.

Consider caching the computed weight alongside the message (e.g., in a wrapper struct) if profiling shows this is a bottleneck.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/harness/message/mod.rs` around lines 68 - 90, The hot path in
estimated_char_weight on ContentBlock::Json and ContentBlock::ProviderExtension
repeatedly calls to_string(), which can re-serialize large payloads during
message_token_estimate/should_summarize/trim_messages passes. Keep the existing
accuracy behavior, but avoid repeated serialization by caching the computed
weight with the message or wrapper type used by the transcript, and have
estimated_char_weight reuse that cached value when available.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/harness/message/mod.rs`:
- Around line 68-90: The hot path in estimated_char_weight on ContentBlock::Json
and ContentBlock::ProviderExtension repeatedly calls to_string(), which can
re-serialize large payloads during
message_token_estimate/should_summarize/trim_messages passes. Keep the existing
accuracy behavior, but avoid repeated serialization by caching the computed
weight with the message or wrapper type used by the transcript, and have
estimated_char_weight reuse that cached value when available.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f120c9dc-5ca1-421c-baca-a744b00321ff

📥 Commits

Reviewing files that changed from the base of the PR and between b1f0d82 and c28f347.

📒 Files selected for processing (3)
  • src/harness/message/mod.rs
  • src/harness/message/test.rs
  • src/harness/summarization/mod.rs

collapsible_if in StreamAccumulator::push_tool_chunk and a duplicated
#[cfg(test)] on the stream test module both fail CI's
cargo clippy --all-targets -- -D warnings under rust 1.96. Behavior-neutral.
@M3gA-Mind

Copy link
Copy Markdown
Contributor Author

CI guardian: the Rust SDK check was red on a pre-existing main clippy failure under stable rust 1.96 (a collapsible_if in StreamAccumulator::push_tool_chunk and a duplicated #[cfg(test)] on the stream test module), inherited by this branch. Pushed a behavior-neutral lint fix so CI can go green; no change to this PR's own logic.

@senamakel senamakel merged commit 91bbffb into tinyhumansai:main Jul 8, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[TinyAgents][T1] Token estimator ignores non-text blocks — compaction/trim silently never trigger

3 participants