Skip to content

feat(web_search): enable parallel execution for read-only search tool#2509

Open
Implementist wants to merge 1 commit into
Hmbown:mainfrom
Implementist:feat/web-search-parallel
Open

feat(web_search): enable parallel execution for read-only search tool#2509
Implementist wants to merge 1 commit into
Hmbown:mainfrom
Implementist:feat/web-search-parallel

Conversation

@Implementist
Copy link
Copy Markdown
Contributor

@Implementist Implementist commented Jun 1, 2026

Summary

Override supports_parallel() to return true in WebSearchTool, allowing the engine to batch multiple concurrent web_search calls into a FuturesUnordered parallel group instead of serializing them.

The tool is already read-only, auto-approved, and non-interactive — parallel-safe by all other criteria. This change removes the final gate (supports_parallel() -> false default) so co-issued searches run concurrently rather than one-at-a-time.

Closes the ~55s serial wall-clock for 3 simultaneous web searches (now ~20s, the slowest individual call).

Background

Why parallel execution matters for Volcengine users

Volcengine's web search uses an LLM-summarized pipeline (Ark Responses API) where the model performs the search and returns structured JSON. A single call takes ~20 seconds — acceptable on its own, but when the agent issues 3 co-occurring searches, serial execution stacks them to ~55s total.

Prior to this change, observing the third search returning at ~50s led to the mistaken conclusion that individual Volcengine responses could take 50+ seconds, which drove the existing 90s timeout floor. In reality, a single call completes in ~20s; the 50s was accumulated serial latency from three sequential calls.

For users in regions where DuckDuckGo/Bing work reliably and return results quickly, this change is a nice-to-have. For Volcengine users who rely on the LLM-summarized pipeline, it is critical — without parallel execution, multi-search turns are painfully slow despite each individual call being reasonably fast.

Root cause

ToolSpec::supports_parallel() defaults to false. WebSearchTool did not override it. tool_plan_is_parallel_safe() in dispatch.rs requires all four conditions:

  • read_only (true)
  • supports_parallel (was false — the blocker)
  • !approval_required (true)
  • !interactive (true)

Changes

  • crates/tui/src/tools/web_search.rs: add supports_parallel() -> true override in impl ToolSpec for WebSearchTool (4 lines)

Testing

  • cargo fmt --all -- --check
  • cargo clippy --workspace --all-targets --all-features
  • cargo test --workspace --all-features (4101 passed, 0 failed, 8 ignored)

Checklist

  • Updated docs or comments as needed — (N/A, self-documenting)
  • Added or updated tests where relevant — (N/A, existing tests cover the contract; web_search tests all pass)
  • Verified TUI behavior manually if UI changes — (N/A, no UI changes)

Greptile Summary

This PR enables parallel execution for WebSearchTool by overriding supports_parallel() to return true, unlocking the engine's FuturesUnordered batching path for concurrent web_search calls. The tool already satisfied the other three tool_plan_is_parallel_safe conditions (read_only, !approval_required, !interactive), making this a minimal and targeted unlock.

  • Adds a 4-line supports_parallel() -> true override in impl ToolSpec for WebSearchTool; no logic changes to any provider path.
  • Particularly impactful for Volcengine users, where the LLM-summarized pipeline takes ~20 s per call — three concurrent searches now resolve in ~20 s instead of ~55 s.

Confidence Score: 4/5

Safe to merge — the change is a single boolean method override with no side effects on any execution path.

The change is minimal and correct: WebSearchTool was already read-only, auto-approved, and non-interactive, so enabling parallel execution requires no defensive guards. All providers create independent reqwest::Client instances per call and share no mutable state. The only open items are style-level: a missing regression test to pin the supports_parallel contract, and a code comment about the Volcengine 90 s timeout floor whose original motivation no longer fully applies.

No files require special attention; the single changed file touches only the ToolSpec trait impl.

Important Files Changed

Filename Overview
crates/tui/src/tools/web_search.rs Adds supports_parallel() -> true override to WebSearchTool; the tool is stateless, read-only, and auto-approved — all four tool_plan_is_parallel_safe conditions are now satisfied.

Sequence Diagram

sequenceDiagram
    participant Engine as turn_loop
    participant Dispatcher as dispatch.rs
    participant S1 as WebSearch #1
    participant S2 as WebSearch #2
    participant S3 as WebSearch #3

    Engine->>Dispatcher: plan_tool_execution_batches([search1, search2, search3])
    Note over Dispatcher: tool_plan_is_parallel_safe:<br/>read_only=true, supports_parallel=true,<br/>approval_required=false, interactive=false
    Dispatcher-->>Engine: ToolExecutionBatch::Parallel([s1, s2, s3])

    par FuturesUnordered
        Engine->>S1: execute(query1)
        Engine->>S2: execute(query2)
        Engine->>S3: execute(query3)
    end

    S1-->>Engine: results (~20s)
    S2-->>Engine: results (~20s)
    S3-->>Engine: results (~20s)
    Note over Engine: Total wall-clock: ~20s (was ~55s serial)
Loading

Comments Outside Diff (1)

  1. crates/tui/src/tools/web_search.rs, line 777-779 (link)

    P2 The PR description acknowledges that the 90 s floor was originally motivated by the observation that the third serial search completed at ~50 s, not because individual calls take that long. Now that calls run concurrently, the comment "is inherently slower than simple search-API round-trips" is still valid, but the floor itself is based on a premise that parallel execution removes. The value is harmlessly conservative, but the inline comment no longer explains the real reasoning. Consider revising it (or adding a note) so future readers aren't confused about why 90 s was chosen.

    Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

    Fix in Codex Fix in Claude Code Fix in Cursor

Fix All in Codex Fix All in Claude Code Fix All in Cursor

Reviews (1): Last reviewed commit: "feat(web_search): enable parallel execut..." | Re-trigger Greptile

Greptile also left 1 inline comment on this PR.

Override `supports_parallel()` to return `true` in `WebSearchTool`,
allowing the engine to batch multiple concurrent web_search calls
into a `FuturesUnordered` parallel group instead of serializing them.

The tool is already read-only, auto-approved, and non-interactive —
parallel-safe by all other criteria. This change removes the final
gate (`supports_parallel() -> false` default) so co-issued searches
run concurrently rather than one-at-a-time.

Closes the ~55s serial wall-clock for 3 simultaneous web searches
(now ~20s, the slowest individual call).

Co-authored-by: Cursor <cursoragent@cursor.com>
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the WebSearchTool implementation of the ToolSpec trait in crates/tui/src/tools/web_search.rs to support parallel execution by implementing the supports_parallel method and returning true. There are no review comments, and I have no additional feedback to provide.

Comment on lines +189 to +191
fn supports_parallel(&self) -> bool {
true
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 No regression test pins supports_parallel() to true. A future refactor that accidentally removes or moves this override would silently re-serialize all web searches with no failing test to catch it. Adding assert!(WebSearchTool.supports_parallel()) in the existing #[cfg(test)] block would lock the contract.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Codex Fix in Claude Code Fix in Cursor

@Hmbown
Copy link
Copy Markdown
Owner

Hmbown commented Jun 1, 2026

Thanks @Implementist. I reviewed this after CI and Greptile were green, including Windows, and harvested the narrow supports_parallel() change into the v0.8.50 draft harvest PR #2504 as a09af2024.

Local verification on the harvest branch:

  • cargo fmt --all -- --check
  • cargo test -p codewhale-tui --all-features --locked web_search

I am leaving this PR open until #2504 lands or a maintainer closes it as superseded, but the contributor credit is now preserved in #2504.

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