Skip to content

Slim PageIndex search hits and list payloads for observation budgets. - #141

Open
Tharickv75 wants to merge 6 commits into
mainfrom
pageindex-lean-hits
Open

Slim PageIndex search hits and list payloads for observation budgets.#141
Tharickv75 wants to merge 6 commits into
mainfrom
pageindex-lean-hits

Conversation

@Tharickv75

Copy link
Copy Markdown
Contributor

Keep citation fields first in node_to_result, omit bulk text by default, and compact pageindex__list so large catalogs fit tool observation limits.

Summary

This PR slims down PageIndex search hits and list payloads so larger document catalogs and tool observations stay under observation limits and prevent orchestrator middle-elision:

  • Reorder & Trim Search Result Fields (node_to_result): Citation/identity keys (doc_name, title, start_page, end_page, node_id) are placed first in search result dicts so middle-elision keeps citation metadata intact. Omits text, physical_index, and enabled by default, and caps large body text/summary content to _MAX_CONTENT_CHARS.
  • Compact List Payloads (_t_list_docs): Switches summary parameter default to True and formats pageindex__list output into a compact {count, documents} JSON payload under 4,000 characters. Automatically truncates long descriptions and strips non-essential fields (like chunks when over capacity) while preserving document names.
  • Scrub/Egress Fixes: Fixes false positives in _detect_peel_closers by preventing interrogative sentences ending in ? from being stripped as closing remarks, and adds debug logging for egress scrub length reductions.
  • Alias Page Keys: Updates format_page_range to support fallback lookups for start_page and end_page.

Related issues

Type of change

  • Bug fix (non-breaking)
  • New feature (non-breaking)
  • Breaking change
  • Documentation
  • Chore / tooling / CI

Checklist

  • I read CONTRIBUTING.md and (for subsystem work) the local CLAUDE.md.
  • pre-commit run --all-files passes.
  • pytest tests/ passes; I added/updated tests for new behavior.
  • Bug fixes cite file:line in the description.
  • Docs / CHANGELOG.md updated where relevant (entry under [Unreleased]).
  • No ADR was modified in place (changes of decision are written as a new, superseding ADR).

Notes for reviewers

  • jvagent/action/parameters.py:909-914: Fixes an issue where valid question sentences (e.g., "Could you let me know if you need a quote?") were incorrectly stripped by the closer peeling logic.
  • Observation budgets in _t_list_docs prioritize retaining full document names and counts; descriptions progressively truncate down to 40 chars before dropping optional chunk metadata if payload exceeds 4,000 characters.

Keep citation fields first in node_to_result, omit bulk text by default, and
compact pageindex__list so large catalogs fit tool observation limits.
@Tharickv75 Tharickv75 self-assigned this Aug 5, 2026
@Tharickv75
Tharickv75 requested a review from eldonm as a code owner August 5, 2026 15:52
Review follow-ups on the lean-payload work. The slimming is right; these
close the gaps it opened.

Keep start_index/end_index alongside start_page/end_page. These rows are
returned verbatim by the public search endpoint (endpoints.py returns
{"results": results} straight from search_documents), so renaming them
broke every existing API client -- and bought nothing, since two small
ints are not what exceeds an observation budget. Bulk text is, and that
stays omitted. Update the endpoint response docstring and OpenAPI example,
which still described the pre-change shape, and document the
include=["text"] escape hatch there.

Import DEFAULT_OBSERVATION_MAX_CHARS rather than hardcoding 4000. The
orchestrator budget is a configurable attribute whose default is that
constant, so a duplicated literal silently diverges from any agent that
tunes it.

Bound the list payload for real. Description shrinking bottoms out at 40
chars; past that a large catalog was still returned oversized, which is
the middle-elision the change exists to prevent. Drop whole entries at
that point and report shown + truncated: true while keeping the true
count -- a model told it has 60 of 200 can ask for the rest; one silently
handed 60 cannot.

Smaller fixes: _is_question's docstring claimed a mid-sentence check it
never implemented, and it missed trailing quotes/brackets, so
'Need a hand?"' was still stripped as a closer. format_page_range's
dict.get default only fires on a missing key, not an explicit None. The
ellipsis character serializes to … (six chars) under json.dumps'
default ensure_ascii, so truncation cost more budget than it saved.

Adjusts one existing assertion: the pageindex__list log line now reports
"N of M document(s)" so the dropped-entry case is visible in logs.
@eldonm

eldonm commented Aug 5, 2026

Copy link
Copy Markdown
Member

Reviewed this and pushed follow-ups in e216829 rather than leaving a list of asks — full reasoning is in the commit message. Summary of what changed and why:

Kept start_index / end_index alongside the new start_page / end_page. These rows go out the public search endpoint verbatim (endpoints.py returns {"results": results} straight from search_documents), so the rename would have broken existing API clients — and for no saving, since two small ints aren't what exceeds an observation budget. Bulk text is, and that stays omitted. Also updated the endpoint response docstring and OpenAPI example, which still described the pre-change shape, and documented the include=["text"] escape hatch there.

Imported DEFAULT_OBSERVATION_MAX_CHARS instead of the hardcoded 4000. That budget is a configurable orchestrator attribute whose default is the constant, so a literal silently diverges for any agent that tunes it.

Bounded the list payload for real. Description shrinking bottoms out at 40 chars — past that a large catalog was still returned oversized, which is the middle-elision this PR exists to prevent. It now drops whole entries at that point and reports shown + truncated: true while keeping the true count. A model told it has 60 of 200 can ask for the rest; one silently handed 60 cannot. Test covers a 400-doc catalog.

Smaller ones: _is_question's docstring described a mid-sentence check it never implemented, and it missed trailing quotes/brackets, so Need a hand?" was still stripped; format_page_range's dict.get default only fires on a missing key, not an explicit None; and serializes to (six chars) under json.dumps' default ensure_ascii, so truncation was costing more budget than it saved.

I adjusted one of your assertions — the pageindex__list log line now reads N of M document(s) so the dropped-entry case is visible in logs. Flagging it explicitly rather than leaving you to find it in the diff.

Two things I deliberately did not touch, because they're your call and not defects:

  1. _t_search's default going 5Noneself.limit (10). It resolves correctly, but it doubles default hits in a PR about shrinking payloads, and each hit now carries capped content and summary. Intended?

  2. Dropping the \blet me know if\b closer pattern. The new _is_question guard alone fixes the reported false positive ("…if you need a quote?"). Without the pattern, declarative closers like "Let me know if that works." now survive scrubbing too. Intended, or belt-and-braces alongside the question check?

Nice work on the core of this — the citation-first ordering and the include=["text"] escape hatch are the right shape, and the log assertion in the list test is a good touch. Local gate is green (pre-commit --all-files, full pytest at 0 failures against jvspatial==0.0.15, the pin this branch targets), and CI is green on the branch.

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.

2 participants