Skip to content

Bounded tool I/O and a smaller default tool surface - #173

Merged
chauncygu merged 5 commits into
SAIL-Research-Lab:mainfrom
huytg2610:perf/bounded-io-hardening
Jul 20, 2026
Merged

Bounded tool I/O and a smaller default tool surface#173
chauncygu merged 5 commits into
SAIL-Research-Lab:mainfrom
huytg2610:perf/bounded-io-hardening

Conversation

@huytg2610

Copy link
Copy Markdown
Contributor

Motivation

High-volume tools could do substantial work before their output hit the truncation layer — a large file, PDF, or slow HTTP response wasted memory, latency, and context budget. Every turn also exposed the full tool catalog, inflating schema/prompt size for ordinary coding tasks. This moves limits and tool-selection to the source of the data.

What changed

Bounded input

  • Read streams files incrementally under byte limits, preserving logical line boundaries and CRLF.
  • WebFetch/WebSearch enforce a response byte limit, a bounded redirect chain, identity-only encoding, and one cancellable deadline spanning connect + redirects + body streaming.
  • PDF extraction and large-file summarization cap source size, extracted text, page count, chunk output, and reduce-stage input.

Tool profiles

  • Default standard profile = common coding tools only; research/orchestration/full are explicit opt-ins.
  • The profile is applied consistently across provider schemas, dispatch, prompt, web settings, /config validation, and cache keys. Configs without tool_profile default to standard; full restores the complete surface.

Prompt & cache correctness

  • System prompt lists the active tool surface instead of a static catalog.
  • Planning guidance shown only when EnterPlanMode/ExitPlanMode are active; tmux guidance only when its tool is active.
  • Read-only cache entries invalidated before and after file mutations; output truncation bounded even for very long paths.

Correctness fixes

Sev Area Fix
High files.py · SummarizeLargeFile Summarize the bounded PDF text instead of erroring on the truncation marker (large PDFs are the tool's main use case).
High fs.py · _read_logical_line A CRLF ending on the 8 KiB read boundary swallowed a following blank line and shifted line numbers; now probes only on a bare trailing \r.
Med files.py · summarize reduce Skip a failed (None) chunk instead of aborting the merge; report chunks actually merged and warn on incomplete coverage (incl. a clipped last chunk).
Med web.py · DuckDuckGo parser Skip HTML void tags (<img>, <br>) in both tag handlers so title/snippet depth counters stay balanced.

Validation

  • CI on Python 3.10–3.13; smoke tests on 3.10 and 3.13.
  • Regression coverage: bounded reads + CRLF boundaries, oversized/compressed responses, redirect + slow-drip deadlines, PDF/summarization limits, profile filtering + cache isolation, prompt assembly, Web API profile config.
  • Fixes verified with targeted reproductions plus the bounded-I/O, summarize, tool-registry, and prompt-assembly suites. compileall and git diff --check pass.

Four defects found by independent review of the bounded-I/O work:

- files.py (SummarizeLargeFile): the PDF branch of _read_file_for_summary
  returned an Error whenever the bounded extractor appended its truncation
  marker, so summarizing any large PDF (papers/books — the tool's main use
  case) hard-failed and dead-ended ReadPDF's own redirect to it. Strip only
  the trailing marker and summarize the extracted text; genuine Error returns
  still propagate.

- fs.py (_read_logical_line): a complete CRLF whose LF landed on the read-chunk
  boundary re-entered the bare-CR probe and swallowed a following blank line,
  shifting every later line number (Read line numbers drive Edit targeting).
  Probe only when the last selected byte is a bare CR.

- files.py (_summarize_large_file reduce): a failed (None) chunk hit break and
  dropped all later chunk summaries; the reduce cap silently truncated the tail
  while the header still claimed all N chunks. Skip None chunks, report the
  chunks actually merged, and warn on incomplete coverage — including when the
  cap clips the last included chunk.

- web.py (_DuckDuckGoResultParser): HTML void elements (<img>, <br>) inside a
  result title/snippet left the depth counters permanently offset, bleeding
  text between fields. Skip void tags in both start/end handlers.

Adds no new deps. Verified with targeted repros and the bounded-I/O,
summarize, tool-registry and prompt-assembly test suites.
@chauncygu
chauncygu merged commit ee22e9b into SAIL-Research-Lab:main Jul 20, 2026
6 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.

2 participants