Skip to content

Python: Add WebSearchDisplayObserver to harness console - #6572

Merged
westey (westey-m) merged 2 commits into
microsoft:mainfrom
westey-m:python-harness-console-websearch-observer
Jun 18, 2026
Merged

Python: Add WebSearchDisplayObserver to harness console#6572
westey (westey-m) merged 2 commits into
microsoft:mainfrom
westey-m:python-harness-console-websearch-observer

Conversation

@westey-m

Copy link
Copy Markdown
Contributor

Motivation & Context

The Python harness console did not display web search activity (search queries, page opens, find-in-page actions) when using models with web search tools. The .NET harness console already has a WebSearchDisplayObserver that shows this activity, but the Python port was missing this observer. Users running the harness_research.py sample saw no indication of web search tool calls in the output stream.

Description & Review Guide

  • What are the major changes?

    • Added WebSearchDisplayObserver in python/samples/02-agents/harness/console/observers/web_search_display.py
    • Registered the observer in both build_default_observers() and build_observers_with_planning() in __init__.py
  • What is the impact of these changes?

    • Web search activity now displays in the harness console with a 🌐 prefix, showing:
      • Search actions: queries and sources in a tree-view format
      • Open page actions: the URL being opened
      • Find-in-page actions: the search pattern and URL
    • The observer reads from search_tool_result content (emitted on response.output_item.done) rather than search_tool_call (emitted on response.output_item.added), because the action details are only fully populated when the search completes.
  • What do you want reviewers to focus on?

    • Whether the action dict field access (result["action"]["type"], action["queries"], etc.) correctly matches the serialized OpenAI SDK ActionSearch / ActionOpenPage / ActionFind models.
    • Whether the observer should also handle search_tool_call for an early "searching..." indicator.

Related Issue

No linked issue — this is a feature addition to the Python harness console port (related to PR #6312).

Contribution Checklist

  • The code builds clean without any errors or warnings
  • All unit tests pass, and I have added new tests where possible
  • The PR follows the Contribution Guidelines
  • This PR is linked to an issue and there is no other open PR for this issue (see Related Issue above).
  • This is not a breaking change. If it is a breaking change, add the breaking change label (or add "[BREAKING]" to the title prefix, before or after any language prefix) — a workflow keeps the label and title prefix in sync automatically.

Copilot AI review requested due to automatic review settings June 17, 2026 16:22
@moonbox3 Evan Mattson (moonbox3) added the python Usage: [Issues, PRs], Target: Python label Jun 17, 2026

Copilot AI left a comment

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.

Pull request overview

Adds a Python harness-console observer to surface web search activity (queries, open-page, find-in-page) in the streaming console output, bringing parity with the existing .NET harness behavior.

Changes:

  • Introduces WebSearchDisplayObserver to render completed search_tool_result items for web_search in a tree-style console format.
  • Registers the observer in the default and planning observer pipelines.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.

File Description
python/samples/02-agents/harness/console/observers/web_search_display.py New observer that formats and prints completed web-search actions to the console.
python/samples/02-agents/harness/console/observers/init.py Wires the new observer into the standard observer lists and exports it.

Comment thread python/samples/02-agents/harness/console/observers/web_search_display.py Outdated
Comment thread python/samples/02-agents/harness/console/observers/web_search_display.py Outdated
Comment thread python/samples/02-agents/harness/console/observers/web_search_display.py Outdated
Comment thread python/samples/02-agents/harness/console/observers/web_search_display.py Outdated

@github-actions github-actions Bot left a comment

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.

Automated Code Review

Reviewers: 5 | Confidence: 91% | Result: All clear

Reviewed: Correctness, Security Reliability, Test Coverage, Failure Modes, Design Approach


Automated review by westey-m's agents

@westey-m
westey (westey-m) marked this pull request as ready for review June 17, 2026 16:27
…bserver

Apply rich.markup.escape() to all user/tool-provided strings (queries, URLs,
titles, patterns) before interpolation into Rich-markup-enabled output. This
prevents characters like '['/']' from being interpreted as Rich markup tags.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

@github-actions github-actions Bot left a comment

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.

Automated Code Review

Reviewers: 5 | Confidence: 91%

✓ Correctness

The code logic, data access patterns, and observer registration are all correct. The only issue is the missing Rich markup escaping of dynamic content (queries, URLs, titles), which is already comprehensively covered by the existing unresolved review comments. All other observers in this codebase (reasoning_display, planning_output, text_output) use from rich.markup import escape before passing dynamic text to ux.append_info_line(). No new issues found beyond the existing comments.

✗ Security Reliability

The PR adds a WebSearchDisplayObserver that renders dynamic, untrusted content (search queries, URLs, source titles) into a Rich-markup-enabled panel without escaping. Other observers in the same package (TextOutputObserver, ReasoningDisplayObserver, PlanningOutputObserver) all use from rich.markup import escape before passing dynamic text to ux.append_info_line or ux.write_text. The new observer omits this escaping at every interpolation point. If a search query or URL contains [ or ] characters (common in URLs with query parameters), Rich will attempt to parse them as markup tags, causing rendering corruption or exceptions. The existing unresolved review comments correctly identify all affected lines; I concur with those findings and have no additional issues to report.

✓ Test Coverage

The new WebSearchDisplayObserver adds non-trivial formatting logic (multiple action types, tree-view rendering, truncation, source formatting) but includes no unit tests. While this is consistent with the existing pattern — no other observer in this samples package has tests — the observer has enough branching logic and edge-case handling that lightweight unit tests would catch regressions cheaply. The helper functions (_truncate, _format_source) and the various action-type code paths are easily testable in isolation without complex mocking.

✓ Failure Modes

The only concrete failure mode in this diff is the Rich markup injection issue already covered by the 5 existing unresolved review comments. The scroll panel uses markup=True (scroll_panel.py:39), and the _format_text method wraps content in color tags (scroll_panel.py:118-119), meaning any '['/']' in dynamic strings (queries, URLs, titles, patterns) will be interpreted as Rich markup — causing either rendering corruption or a MarkupError crash that propagates up the observer pipeline. Other observers (reasoning_display.py:9,45; text_output.py:9,41) correctly import and use rich.markup.escape for dynamic content. No additional failure modes beyond the existing comments were found.

✓ Design Approach

The new observer covers the Foundry/OpenAI search-result shape, but it is registered in the generic harness observer lists while only listening for search_tool_result. That means Anthropic-backed web search sessions still produce no web-search UI, so the feature is only partially implemented across the harness.

Flagged Issues

  • All dynamic content (queries, URLs, source titles/patterns) is interpolated into Rich-markup strings without escaping. The RichLog uses markup=True and _format_text wraps content in [color]...[/color] tags, so any brackets in tool-provided data will be misinterpreted as markup, causing rendering corruption or MarkupError crashes. All other observers in this package consistently use from rich.markup import escape for dynamic content.

Automated review by westey-m's agents

@github-actions

Copy link
Copy Markdown
Contributor

Flagged issue

All dynamic content (queries, URLs, source titles/patterns) is interpolated into Rich-markup strings without escaping. The RichLog uses markup=True and _format_text wraps content in [color]...[/color] tags, so any brackets in tool-provided data will be misinterpreted as markup, causing rendering corruption or MarkupError crashes. All other observers in this package consistently use from rich.markup import escape for dynamic content.


Source: automated DevFlow PR review

@westey-m
westey (westey-m) added this pull request to the merge queue Jun 18, 2026
Merged via the queue into microsoft:main with commit 699916d Jun 18, 2026
36 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

python Usage: [Issues, PRs], Target: Python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants