Skip to content

Commit 13a21c7

Browse files
nikosbosseclaude
authored andcommitted
Strip heavy fields from LLM-facing result data (#4886)
Goal is basically to make sure that the agent can actually see all relevant data when calling report_result. To not pollute its context window, we clamp the return content, but previously the return content also included the source bank, for example. CLAUDE: ## Summary - Strip `_source_bank`, `research`, and `provenance_and_notes` fields from LLM-facing result data while keeping them in `structuredContent` (widget) - Update `clamp_page_to_budget` to estimate tokens on stripped records, matching what the LLM actually receives These fields contain full citation databases and research notes (25-30k chars per row). For a 7-row agent task, this caused the token budget (10k) to clamp results from 7 rows down to 1-4, producing messages like "I can only see 1 of the 7 rows". After stripping: ~16k tokens → ~1k tokens (94% reduction). Users still see all fields in the viz pane. ## Test plan - [ ] Run an agent task with 7+ rows and verify all rows appear in the LLM response (no "I can only see X of Y" message for small datasets) - [ ] Verify research notes and citations are still visible in the viz pane widget - [ ] Verify CSV download still contains all fields 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Sourced from commit 63d0ba0dabf0374e982f89b41daddd5465db11cf
1 parent 71efe48 commit 13a21c7

2 files changed

Lines changed: 19 additions & 5 deletions

File tree

everyrow-mcp/src/everyrow_mcp/result_store.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,18 +57,31 @@ def _estimate_tokens(text: str) -> int:
5757
return litellm.token_counter(model=_TOKEN_MODEL, text=text)
5858

5959

60+
# Fields stripped from LLM-facing data (user sees them in the viz pane).
61+
_LLM_STRIP_FIELDS = {"_source_bank", "research", "provenance_and_notes"}
62+
63+
64+
def _strip_for_llm(records: list[dict[str, Any]]) -> list[dict[str, Any]]:
65+
"""Remove heavy fields that the user can see in the viz pane."""
66+
return [
67+
{k: v for k, v in row.items() if k not in _LLM_STRIP_FIELDS} for row in records
68+
]
69+
70+
6071
def clamp_page_to_budget(
6172
preview_records: list[dict[str, Any]],
6273
page_size: int,
6374
) -> tuple[list[dict[str, Any]], int]:
64-
estimated = _estimate_tokens(json.dumps(preview_records))
75+
# Estimate tokens on stripped records (what the LLM actually sees).
76+
stripped = _strip_for_llm(preview_records)
77+
estimated = _estimate_tokens(json.dumps(stripped))
6578
if estimated <= settings.token_budget:
6679
return preview_records, page_size
6780

6881
# Pre-compute per-row token sizes and build a prefix sum so the binary
6982
# search doesn't need to re-serialize on every iteration.
7083
# Overhead per-row is ~2 tokens for the JSON array wrapper/commas.
71-
row_sizes = [_estimate_tokens(json.dumps(r)) + 2 for r in preview_records]
84+
row_sizes = [_estimate_tokens(json.dumps(r)) + 2 for r in stripped]
7285
prefix = [0] * (len(row_sizes) + 1)
7386
for i, s in enumerate(row_sizes):
7487
prefix[i + 1] = prefix[i] + s
@@ -188,8 +201,9 @@ def _build_result_response(
188201
if artifact_id:
189202
summary += f"\nOutput artifact_id (use to chain into next tool): {artifact_id}"
190203

191-
# Append the actual data rows so the LLM can reason about them.
192-
data_text = json.dumps(preview_records)
204+
# Append data rows for the LLM, stripping heavy fields that the user
205+
# can already see in the viz pane (source_bank, research notes).
206+
data_text = json.dumps(_strip_for_llm(preview_records))
193207
summary += f"\n\nData:\n{data_text}"
194208

195209
return CallToolResult(

everyrow-mcp/tests/test_result_store.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -859,7 +859,7 @@ def wide_df(self) -> pd.DataFrame:
859859
return pd.DataFrame(
860860
{
861861
"id": range(10),
862-
"research": [f"Long research text {'x' * 2000}" for _ in range(10)],
862+
"details": [f"Long details text {'x' * 2000}" for _ in range(10)],
863863
"summary": [f"Summary {'y' * 500}" for _ in range(10)],
864864
}
865865
)

0 commit comments

Comments
 (0)