Skip to content

Add Windows browser path checks to Chromium detection#1014

Merged
ryanhoangt merged 41 commits intoOpenHands:mainfrom
SmartManoj:chromium
Dec 5, 2025
Merged

Add Windows browser path checks to Chromium detection#1014
ryanhoangt merged 41 commits intoOpenHands:mainfrom
SmartManoj:chromium

Conversation

@SmartManoj
Copy link
Contributor

Enhanced _check_chromium_available to check common Windows installation paths for Chrome and Edge executables. Also added support for Playwright cache detection on Windows using LOCALAPPDATA.

Enhanced _check_chromium_available to check common Windows installation paths for Chrome and Edge executables. Also added support for Playwright cache detection on Windows using LOCALAPPDATA.
@blacksmith-sh
Copy link
Contributor

blacksmith-sh bot commented Nov 6, 2025

[Automatic Post]: I have assigned @simonrosenberg as a reviewer based on git blame information. Thanks in advance for the help!

enyst
enyst previously requested changes Nov 6, 2025
Copy link
Collaborator

@enyst enyst left a comment

Choose a reason for hiding this comment

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

Thank you for your contributions to Windows compatibility!

I'm not sure this the way we need to go on this... I think if branches in various tools or other code on this issue are error-prone, destined to become many, reduce visibility of other code thus maintainability.

I do understand that in the browser tool case it seems hard to avoid... but then that's always the case. Idk, I'd love to give it some thought. One small question: could we at least, if we are going to do special cases throughout the codebase, make sure to recognize them in exactly the same conditions?

IMHO though, we'll be happier in time if we don't do special cases throughout the codebase...

Removed redundant os.name check and always define Windows Chromium and Edge paths. Simplified Playwright cache candidate logic by unconditionally including the Windows path.
@SmartManoj SmartManoj requested a review from enyst November 6, 2025 22:06
Copy link
Collaborator

@enyst enyst left a comment

Choose a reason for hiding this comment

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

Thank you, I see... I guess that's just how this one goes, we have Linux paths, Mac paths, Windows paths... 🤔

I kinda liked this one too, in another PR... it's an if (unfortunately 😅), but it's in __init__.py, and it makes sure to expose exactly the necessary classes
image

The separation of the implementation in WindowsTerminal is great IMHO. It's the ideal, if we could keep it that way.

One reason, it seems to me, is that usually, not always but usually, it's not the same people working with one or the other. It matters, IMHO, for a developer reading the code, to be able to focus on code relevant to their work. It helps maintainability, ease of debugging, code understanding.

While I'm not fully sure yet how the end solution looks on the whole issue, IMHO separated code is better than hybrid, regarding Windows (generally speaking, not in this PR). In other places, like this, I really appreciate when these pieces are at least very clear.

@enyst enyst dismissed their stale review November 6, 2025 23:02

Thank you, I see there's no super obvious solution here, the code asks for system-specific paths, and it's the case for Linux and Mac too.

@simonrosenberg
Copy link
Collaborator

@enyst would you approve that PR?

Simplifies and generalizes the construction of Windows browser executable paths by iterating over environment variables and browser definitions. This reduces code duplication and improves maintainability.
@SmartManoj SmartManoj requested a review from enyst November 20, 2025 05:00
Cleaned up unnecessary trailing whitespace in the _check_chromium_available function for improved code style.
@OpenHands OpenHands deleted a comment from blacksmith-sh bot Nov 20, 2025
@OpenHands OpenHands deleted a comment from blacksmith-sh bot Nov 20, 2025
Remove incorrect skip logic that prevented checking %LOCALAPPDATA% for
Microsoft Edge installations. While Edge is typically installed system-wide,
it can also be installed per-user in enterprise environments at
%LOCALAPPDATA%\Microsoft\Edge\Application\msedge.exe. This fix ensures
the browser detection can find Edge in both installation scenarios.
@SmartManoj SmartManoj requested a review from enyst November 20, 2025 07:11
]
browsers = [
("Google", "Chrome", "Application", "chrome.exe"),
("Microsoft", "Edge", "Application", "msedge.exe"),
Copy link
Collaborator

Choose a reason for hiding this comment

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

Actually, why do we need browsers on Windows, doesn't the browser tool work with playwright?

Copy link
Contributor Author

@SmartManoj SmartManoj Nov 20, 2025

Choose a reason for hiding this comment

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

To use existing browsers instead of installing browsers using playwright (uvx playwright install chromium)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

def _check_chromium_available() -> str | None:
"""Check if a Chromium/Chrome binary is available in PATH."""
for binary in ("chromium", "chromium-browser", "google-chrome", "chrome"):
if path := shutil.which(binary):
return path
# Check Playwright-installed Chromium
playwright_cache_candidates = [
Path.home() / ".cache" / "ms-playwright",
Path.home() / "Library" / "Caches" / "ms-playwright",
]
for playwright_cache in playwright_cache_candidates:
if playwright_cache.exists():
chromium_dirs = list(playwright_cache.glob("chromium-*"))
for chromium_dir in chromium_dirs:
# Check platform-specific paths
possible_paths = [
chromium_dir / "chrome-linux" / "chrome", # Linux
chromium_dir
/ "chrome-mac"
/ "Chromium.app"
/ "Contents"
/ "MacOS"
/ "Chromium", # macOS
chromium_dir / "chrome-win" / "chrome.exe", # Windows
]
for p in possible_paths:
if p.exists():
return str(p)
return None

Line 34, in WIndows, browser is not in Path by default,

image

Without checking for existing browser, it will work if it is installed from Playwright.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I see, thank you.

Tiny nit: could we call them windows_browsers?

Is LOCALAPPDATA a common env var used on Windows? Is it set by default?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is LOCALAPPDATA a common env var used on Windows? Is it set by default?

Yes. Source: https://devblogs.microsoft.com/oldnewthing/20231212-00/?p=109137

Copy link
Collaborator

Choose a reason for hiding this comment

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

For discussion here, #1210 (comment)

WDYT about we can inherit this tool, create a new Windows Browser tool, and add windows specific handling there?

Copy link
Collaborator

Choose a reason for hiding this comment

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

That sounds great! It seems we have more code coming. And thanks to inheritance, we can reuse a bunch of code anyway.

Copy link
Collaborator

Choose a reason for hiding this comment

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

@SmartManoj are you ok w/ implementing this? 👀

enyst
enyst previously requested changes Nov 20, 2025
Copy link
Collaborator

@enyst enyst left a comment

Choose a reason for hiding this comment

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

Could you please add a screenshot or logs, that show if the browser tool is working?

I also added a small question inline, sorry, I don't understand, does it work on chrome or edge?

@SmartManoj
Copy link
Contributor Author

Without this PR,

Traceback (most recent call last):
  File "c:\Users\smart\Desktop\GD\OpenHands-SDK\examples\01_standalone_sdk\01_hello_world.py", line 28, in <module>
    conversation = Conversation(agent=agent, workspace=cwd)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\smart\Desktop\GD\OpenHands-SDK\openhands-sdk\openhands\sdk\conversation\conversation.py", line 114, in __new__
    return LocalConversation(
           ^^^^^^^^^^^^^^^^^^
  File "C:\Users\smart\Desktop\GD\OpenHands-SDK\openhands-sdk\openhands\sdk\conversation\impl\local_conversation.py", line 150, in __init__
    self.agent.init_state(self._state, on_event=self._on_event)
  File "C:\Users\smart\Desktop\GD\OpenHands-SDK\openhands-sdk\openhands\sdk\agent\agent.py", line 95, in init_state
    super().init_state(state, on_event=on_event)
  File "C:\Users\smart\Desktop\GD\OpenHands-SDK\openhands-sdk\openhands\sdk\agent\base.py", line 219, in init_state
    self._initialize(state)
  File "C:\Users\smart\Desktop\GD\OpenHands-SDK\openhands-sdk\openhands\sdk\agent\base.py", line 239, in _initialize
    tools.extend(resolve_tool(tool_spec, state))
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\smart\Desktop\GD\OpenHands-SDK\openhands-sdk\openhands\sdk\tool\registry.py", line 156, in resolve_tool
    return resolver(tool_spec.params, conv_state)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\smart\Desktop\GD\OpenHands-SDK\openhands-sdk\openhands\sdk\tool\registry.py", line 103, in _resolve
    created = create(conv_state=conv_state, **params)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\smart\Desktop\GD\OpenHands-SDK\openhands-tools\openhands\tools\browser_use\definition.py", line 555, in create
    executor = BrowserToolExecutor(**executor_config)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\smart\Desktop\GD\OpenHands-SDK\openhands-tools\openhands\tools\browser_use\impl.py", line 165, in __init__
    run_with_timeout(init_logic, init_timeout_seconds)
  File "C:\Users\smart\Desktop\GD\OpenHands-SDK\openhands-tools\openhands\tools\utils\timeout.py", line 12, in run_with_timeout
    return func_timeout(timeout, func, args=args, kwargs=kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Python312\Lib\site-packages\func_timeout\dafunc.py", line 108, in func_timeout
    raise_exception(exception)
  File "C:\Python312\Lib\site-packages\func_timeout\py3_raise.py", line 7, in raise_exception
    raise exception[0] from None
  File "C:\Users\smart\Desktop\GD\OpenHands-SDK\openhands-tools\openhands\tools\browser_use\impl.py", line 149, in init_logic
    executable_path = _ensure_chromium_available()
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\smart\Desktop\GD\OpenHands-SDK\openhands-tools\openhands\tools\browser_use\impl.py", line 114, in _ensure_chromium_available
    raise Exception(error_msg)
Exception: Chromium is required for browser operations but is not installed.

To install Chromium, run one of the following commands:
  1. Using uvx (recommended): uvx playwright install chromium --with-deps --no-shell
  2. Using pip: pip install playwright && playwright install chromium
  3. Using system package manager:
     - Ubuntu/Debian: sudo apt install chromium-browser
     - macOS: brew install chromium
     - Windows: winget install Chromium.Chromium

After installation, restart your application to use the browser tool.

@SmartManoj
Copy link
Contributor Author

SmartManoj commented Nov 20, 2025

does it work on chrome or edge?

script checks for chrome first and then edge and then chrome in playwright dir.

@enyst
Copy link
Collaborator

enyst commented Nov 20, 2025

Sorry I'm a bit dense, so if it finds Edge, does agent-sdk work with the browser tool on Windows?

@SmartManoj
Copy link
Contributor Author

SmartManoj commented Nov 20, 2025

if it finds Edge, does agent-sdk work with the browser tool on Windows?

It will as edge is based on chromium.


Browser-use test. Edge opened and navigated to the URL.
https://docs.browser-use.com/customize/browser/real-browser

image

@enyst
Copy link
Collaborator

enyst commented Nov 20, 2025

@SmartManoj I see in your screenshot that browser-use seems to navigate to the page. We know we have adapted browser-use for the browser tool in agent-sdk. So I would guess the tool works, but I don't know.

For example, this is supposed to be able to take screenshots:

Is it... able to take screenshots? 😅

What I mean by my question is, does agent-sdk with browser tool work on Windows after this PR, or is there more to do for it to work? Is there some way we could see it work?

@ryanhoangt ryanhoangt added the test-examples Run all applicable "examples/" files. Expensive operation. label Dec 3, 2025
@ryanhoangt ryanhoangt added test-examples Run all applicable "examples/" files. Expensive operation. and removed test-examples Run all applicable "examples/" files. Expensive operation. labels Dec 3, 2025
@ryanhoangt ryanhoangt removed the test-examples Run all applicable "examples/" files. Expensive operation. label Dec 3, 2025
ryanhoangt and others added 9 commits December 3, 2025 15:32
BrowserToolExecutor now checks common Linux and macOS installation paths for Chromium and Chrome if not found in PATH. Corresponding tests were added to verify detection via these standard paths.
Renamed the example script for consistency or to reflect updated numbering in the standalone SDK examples directory.
Adds a print statement to display the accumulated cost from the LLM metrics at the end of the Tom agent consultation example.
Changed references from 30_windows.py to 31_windows.py in the exemption list and workflow configuration to reflect the new filename.
Copy link
Collaborator

@ryanhoangt ryanhoangt left a comment

Choose a reason for hiding this comment

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

Thank you, this LGTM. I added a new example script (31_windows) for e2e testing without terminal tool. I also tested it on a Windows machine and via test-examples label and they seem to run without issues. Would love final 👀 from @xingyaoww and @enyst regarding the Windows code separation.

Copy link
Collaborator

@xingyaoww xingyaoww left a comment

Choose a reason for hiding this comment

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

Seems good to me - @enyst feel free to merge this if you are happy w/ it

Copy link
Collaborator

Choose a reason for hiding this comment

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

Shall we rename this to "windows_browsing_support"

Copy link
Collaborator

Choose a reason for hiding this comment

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

@OpenHands please rename to 31_windows_browsing.py

Copy link

Choose a reason for hiding this comment

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

I'm on it! xingyaoww can track my progress at all-hands.dev

Copy link
Collaborator

Choose a reason for hiding this comment

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

actually i don't think we need this example? shouldn't examples/01_standalone_sdk/15_browser_use.py do the same thing? maybe we should remove it

Copy link

Choose a reason for hiding this comment

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

Summary

I've successfully completed the file rename as requested in PR #1014. Here's what was done:

Changes Made:

  1. Renamed the example file: examples/01_standalone_sdk/31_windows.pyexamples/01_standalone_sdk/31_windows_browsing.py using git mv to preserve the file history

  2. Updated all references to the old filename in:

    • .github/scripts/check_documented_examples.py - Updated the EXEMPT_EXAMPLES set
    • .github/workflows/run-examples.yml - Updated both test filters (exclusion filter and Windows-specific test filter)

Verification:

  • ✅ All references confirmed updated
  • ✅ Pre-commit hooks passed (YAML format, Ruff format & lint, pycodestyle, pyright, import dependency rules, tool registration check)
  • ✅ Changes committed with proper co-authorship attribution
  • ✅ Changes pushed to the fork's chromium branch

The PR #1014 has been updated with commit 058dc504 which includes the file rename and all necessary reference updates.

View full conversation

Copy link

Choose a reason for hiding this comment

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

I'm on it! xingyaoww can track my progress at all-hands.dev

Copy link

Choose a reason for hiding this comment

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

Summary

I've successfully removed the 31_windows_browsing.py example from PR #1014 as requested. The changes have been committed and pushed to update the pull request.

Changes Made:

Removed the example file:

  • examples/01_standalone_sdk/31_windows_browsing.py

Updated CI workflow (.github/workflows/run-examples.yml):

  • Removed the entire test-examples-windows job (which was created specifically for this example)
  • Removed the dependency on test-examples-windows from the main test-examples job
  • Removed the step that downloads Windows example results
  • Removed the pytest exclusion filter -k "not 31_windows_browsing.py"
  • Removed the step that copies Windows example results to the main job

Updated documentation check script (.github/scripts/check_documented_examples.py):

  • Cleared the EXEMPT_EXAMPLES set (removed the 31_windows_browsing.py entry)

All pre-commit hooks passed successfully (YAML format, Ruff format/lint, pycodestyle, pyright, dependency checks, and tool registration checks).

Commit: 8131ee23

The PR has been updated and is ready for review. The example was redundant since 15_browser_use.py already demonstrates browser functionality.

View full conversation

Copy link
Collaborator

@ryanhoangt ryanhoangt Dec 4, 2025

Choose a reason for hiding this comment

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

actually i don't think we need this example? shouldn't examples/01_standalone_sdk/15_browser_use.py do the same thing? maybe we should remove it

@xingyaoww this example removed the terminal tool from 15_browser_use.py, since it won't work right now. Also I made this test run only on Windows runner and we also need some custom UTF-8 handling, which isn't required on Linux runner. I think we should keep it for now, and after #1012 we can remove it and use 15_browser_use for both. WDYT?

Copy link
Collaborator

Choose a reason for hiding this comment

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

hmm, can we convert this to a test rather than an example here? I feel putting this as an example could be quite confusing (especially we want to remove them later).

Copy link
Collaborator

Choose a reason for hiding this comment

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

Then let's do it altogether in #1012.

Updated references in workflow and script files to reflect the new filename.

Co-authored-by: openhands <openhands@all-hands.dev>
As discussed in PR comments, this example is redundant since examples/01_standalone_sdk/15_browser_use.py already demonstrates browser functionality.

Changes:
- Remove examples/01_standalone_sdk/31_windows_browsing.py
- Remove test-examples-windows job from CI workflow
- Remove Windows example exclusions and result handling from CI
- Clear EXEMPT_EXAMPLES set in check_documented_examples.py

Co-authored-by: openhands <openhands@all-hands.dev>
SmartManoj and others added 5 commits December 5, 2025 08:26
Co-authored-by: Engel Nyst <engel.nyst@gmail.com>
Renamed test methods to use 'testcheck_chromium_available_*' and updated calls from '_check_chromium_available' to 'check_chromium_available' to reflect changes in the BrowserToolExecutor API.
@ryanhoangt ryanhoangt enabled auto-merge (squash) December 5, 2025 15:37
@ryanhoangt ryanhoangt merged commit 9a097ce into OpenHands:main Dec 5, 2025
15 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.

6 participants

Comments