Spaces:
Running
Running
Download tests/test_tool_spaces.py from stephenslee/peachy: direct link, hf CLI and curl.
- Browser
- Download file 19.7 kB
-
https://huggingface.co/spaces/stephenslee/peachy/resolve/main/tests/test_tool_spaces.py
- Command line
-
hf download hf://spaces/stephenslee/peachy/tests/test_tool_spaces.py
-
curl -L -o test_tool_spaces.py https://huggingface.co/spaces/stephenslee/peachy/resolve/main/tests/test_tool_spaces.py
19.7 kB
| from __future__ import annotations | |
| import os | |
| import sys | |
| import json | |
| from types import SimpleNamespace | |
| from pathlib import Path | |
| from argparse import Namespace | |
| import httpx | |
| import pytest | |
| from huggingface_hub.errors import RepositoryNotFoundError | |
| import peachy.config as config_mod | |
| import peachy.tool_spaces as tool_spaces_mod | |
| from peachy.main import main | |
| from peachy.mcp_client import RemoteToolSpec | |
| from peachy.tool_spaces import ( | |
| resolve_tool_space_sync, | |
| handle_tool_spaces_command, | |
| read_installed_tool_spaces, | |
| ) | |
| SEARCH_SPACE_SLUG = "example/search-tool" | |
| COLLIDING_SEARCH_SPACE_SLUG = "example/search_tool" | |
| PRIVATE_SPACE_SLUG = "example/private-space" | |
| SEARCH_ALIAS = "example_search_tool" | |
| SEARCH_REMOTE_NAME = "search_tool_search_web" | |
| SEARCH_TOOL_ID = f"{SEARCH_ALIAS}__search_web" | |
| SEARCH_CLIENT_TOOL_ID = f"{SEARCH_ALIAS}__{SEARCH_REMOTE_NAME}" | |
| def _isolate_peachy_state(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: | |
| monkeypatch.setenv("PEACHY_STATE_DIR", str((tmp_path / "peachy-state").resolve())) | |
| def _test_state_root() -> Path: | |
| return Path(os.environ["PEACHY_STATE_DIR"]) | |
| def _mock_public_space_info(slug: str) -> SimpleNamespace: | |
| return SimpleNamespace( | |
| id=slug, | |
| private=False, | |
| disabled=False, | |
| sdk="gradio", | |
| host=None, | |
| subdomain=slug.replace("/", "-"), | |
| tags=["reachy-mini-tool", "mcp"], | |
| ) | |
| def _mock_private_space_info(slug: str) -> SimpleNamespace: | |
| info = _mock_public_space_info(slug) | |
| info.private = True | |
| return info | |
| async def _mock_list_tool_specs(self: object) -> list[RemoteToolSpec]: | |
| return [ | |
| RemoteToolSpec( | |
| server_alias=SEARCH_ALIAS, | |
| remote_name=SEARCH_REMOTE_NAME, | |
| namespaced_name=SEARCH_CLIENT_TOOL_ID, | |
| description="Search the web", | |
| parameters_schema={ | |
| "type": "object", | |
| "properties": {"query": {"type": "string"}}, | |
| "required": ["query"], | |
| }, | |
| ) | |
| ] | |
| def _run_cli(monkeypatch: pytest.MonkeyPatch, argv: list[str]) -> int: | |
| monkeypatch.setattr(sys, "argv", argv) | |
| with pytest.raises(SystemExit) as exc: | |
| main() | |
| return int(exc.value.code) | |
| def test_tool_spaces_add_list_remove_round_trip( | |
| tmp_path: Path, | |
| monkeypatch: pytest.MonkeyPatch, | |
| ) -> None: | |
| """The CLI should install, list, and remove a public Space tool source cleanly.""" | |
| monkeypatch.chdir(tmp_path) | |
| monkeypatch.setattr( | |
| "peachy.tool_spaces.HfApi.space_info", | |
| lambda self, slug, **kwargs: _mock_public_space_info(slug), | |
| ) | |
| monkeypatch.setattr( | |
| "peachy.tool_spaces.RemoteMcpToolClient.list_tool_specs", | |
| _mock_list_tool_specs, | |
| ) | |
| assert ( | |
| _run_cli( | |
| monkeypatch, | |
| [ | |
| "reachy-mini-conversation-app", | |
| "tool-spaces", | |
| "add", | |
| SEARCH_SPACE_SLUG, | |
| "--install-only", | |
| ], | |
| ) | |
| == 0 | |
| ) | |
| manifest_path = _test_state_root() / "installed_tool_spaces.json" | |
| assert manifest_path.is_file() | |
| written = json.loads(manifest_path.read_text(encoding="utf-8")) | |
| assert written["version"] == 2 | |
| added_entry = next(space for space in written["spaces"] if space["slug"] == SEARCH_SPACE_SLUG) | |
| assert added_entry == { | |
| "slug": SEARCH_SPACE_SLUG, | |
| "alias": SEARCH_ALIAS, | |
| "mcp_url": "https://example-search-tool.hf.space/gradio_api/mcp/", | |
| "private": False, | |
| "tools": [ | |
| { | |
| "local_name": SEARCH_TOOL_ID, | |
| "client_tool_name": SEARCH_CLIENT_TOOL_ID, | |
| "remote_name": SEARCH_REMOTE_NAME, | |
| "description": "Search the web", | |
| "parameters_schema": { | |
| "type": "object", | |
| "properties": {"query": {"type": "string"}}, | |
| "required": ["query"], | |
| }, | |
| } | |
| ], | |
| } | |
| assert _run_cli(monkeypatch, ["reachy-mini-conversation-app", "tool-spaces", "list"]) == 0 | |
| assert _run_cli(monkeypatch, ["reachy-mini-conversation-app", "tool-spaces", "remove", SEARCH_SPACE_SLUG]) == 0 | |
| assert SEARCH_SPACE_SLUG not in [space.slug for space in read_installed_tool_spaces(_test_state_root()).spaces] | |
| def test_tool_spaces_add_installs_private_space_with_token( | |
| tmp_path: Path, | |
| monkeypatch: pytest.MonkeyPatch, | |
| ) -> None: | |
| """A private Space resolves and installs when an HF token is available.""" | |
| monkeypatch.chdir(tmp_path) | |
| monkeypatch.setattr( | |
| "peachy.tool_spaces.HfApi.space_info", | |
| lambda self, slug, **kwargs: _mock_private_space_info(slug), | |
| ) | |
| monkeypatch.setattr("peachy.tool_spaces.get_token", lambda: "hf_test_token") | |
| monkeypatch.setattr( | |
| "peachy.tool_spaces.RemoteMcpToolClient.list_tool_specs", | |
| _mock_list_tool_specs, | |
| ) | |
| assert _run_cli(monkeypatch, ["app", "tool-spaces", "add", PRIVATE_SPACE_SLUG, "--install-only"]) == 0 | |
| assert PRIVATE_SPACE_SLUG in [space.slug for space in read_installed_tool_spaces(_test_state_root()).spaces] | |
| def test_resolve_tool_space_attaches_auth_header_for_private_space(monkeypatch: pytest.MonkeyPatch) -> None: | |
| """A private Space's MCP client must carry the HF bearer token from the hf-login fallback.""" | |
| monkeypatch.setattr( | |
| "peachy.tool_spaces.HfApi.space_info", | |
| lambda self, slug, **kwargs: _mock_private_space_info(slug), | |
| ) | |
| monkeypatch.setattr(config_mod.config, "HF_TOKEN", None) | |
| monkeypatch.setattr("peachy.tool_spaces.get_token", lambda: "hf_test_token") | |
| monkeypatch.setattr( | |
| "peachy.tool_spaces.RemoteMcpToolClient.list_tool_specs", | |
| _mock_list_tool_specs, | |
| ) | |
| resolved = resolve_tool_space_sync(PRIVATE_SPACE_SLUG) | |
| assert resolved.client.server.headers["Authorization"] == "Bearer hf_test_token" | |
| def test_resolve_tool_space_prefers_app_config_hf_token(monkeypatch: pytest.MonkeyPatch) -> None: | |
| """The token must come from the app config (loaded from .env), not only from get_token().""" | |
| monkeypatch.setattr( | |
| "peachy.tool_spaces.HfApi.space_info", | |
| lambda self, slug, **kwargs: _mock_private_space_info(slug), | |
| ) | |
| monkeypatch.setattr(config_mod.config, "HF_TOKEN", "hf_env_token") | |
| monkeypatch.setattr("peachy.tool_spaces.get_token", lambda: None) | |
| monkeypatch.setattr( | |
| "peachy.tool_spaces.RemoteMcpToolClient.list_tool_specs", | |
| _mock_list_tool_specs, | |
| ) | |
| resolved = resolve_tool_space_sync(PRIVATE_SPACE_SLUG) | |
| assert resolved.client.server.headers["Authorization"] == "Bearer hf_env_token" | |
| def test_resolve_tool_space_omits_auth_header_for_public_space(monkeypatch: pytest.MonkeyPatch) -> None: | |
| """A public Space must never receive the HF token, even when one is set.""" | |
| monkeypatch.setattr( | |
| "peachy.tool_spaces.HfApi.space_info", | |
| lambda self, slug, **kwargs: _mock_public_space_info(slug), | |
| ) | |
| monkeypatch.setattr(config_mod.config, "HF_TOKEN", None) | |
| monkeypatch.setattr("peachy.tool_spaces.get_token", lambda: "hf_test_token") | |
| monkeypatch.setattr( | |
| "peachy.tool_spaces.RemoteMcpToolClient.list_tool_specs", | |
| _mock_list_tool_specs, | |
| ) | |
| resolved = resolve_tool_space_sync(SEARCH_SPACE_SLUG) | |
| assert "Authorization" not in resolved.client.server.headers | |
| def test_tool_spaces_add_private_space_without_token_hints_at_auth( | |
| tmp_path: Path, | |
| monkeypatch: pytest.MonkeyPatch, | |
| capsys: pytest.CaptureFixture[str], | |
| ) -> None: | |
| """Without a token, adding a private Space fails cleanly and points at HF auth.""" | |
| monkeypatch.chdir(tmp_path) | |
| def _raise_not_found(self: object, slug: str, **kwargs: object) -> SimpleNamespace: | |
| raise RepositoryNotFoundError( | |
| "404 Client Error", response=httpx.Response(404, request=httpx.Request("GET", "https://hf.co")) | |
| ) | |
| monkeypatch.setattr("peachy.tool_spaces.HfApi.space_info", _raise_not_found) | |
| monkeypatch.setattr(config_mod.config, "HF_TOKEN", None) | |
| monkeypatch.setattr("peachy.tool_spaces.get_token", lambda: None) | |
| assert _run_cli(monkeypatch, ["app", "tool-spaces", "add", PRIVATE_SPACE_SLUG]) == 1 | |
| assert "hf auth login" in capsys.readouterr().err | |
| assert not (_test_state_root() / "installed_tool_spaces.json").exists() | |
| def test_tool_spaces_manifest_uses_instance_path_when_provided( | |
| tmp_path: Path, | |
| monkeypatch: pytest.MonkeyPatch, | |
| ) -> None: | |
| """Managed instance paths should store the manifest beside other instance-local state.""" | |
| monkeypatch.setattr( | |
| "peachy.tool_spaces.HfApi.space_info", | |
| lambda self, slug, **kwargs: _mock_public_space_info(slug), | |
| ) | |
| monkeypatch.setattr( | |
| "peachy.tool_spaces.RemoteMcpToolClient.list_tool_specs", | |
| _mock_list_tool_specs, | |
| ) | |
| args = Namespace( | |
| tool_spaces_command="add", | |
| space_slug=SEARCH_SPACE_SLUG, | |
| install_only=True, | |
| profile=None, | |
| ) | |
| assert handle_tool_spaces_command(args, instance_path=tmp_path) == 0 | |
| assert (tmp_path / "installed_tool_spaces.json").is_file() | |
| assert not (tmp_path / "external_content" / "installed_tool_spaces.json").exists() | |
| def test_read_installed_tool_spaces_raises_on_alias_collision_in_manifest(tmp_path: Path) -> None: | |
| """A manifest with two slugs that normalize to the same alias must be rejected on read.""" | |
| mcp_url = "https://example.hf.space/gradio_api/mcp/" | |
| payload = { | |
| "version": 2, | |
| "spaces": [ | |
| {"slug": "owner/my-tool", "alias": "owner_my_tool", "mcp_url": mcp_url, "private": False, "tools": []}, | |
| {"slug": "owner/my_tool", "alias": "owner_my_tool", "mcp_url": mcp_url, "private": False, "tools": []}, | |
| ], | |
| } | |
| (tmp_path / "installed_tool_spaces.json").write_text(json.dumps(payload), encoding="utf-8") | |
| with pytest.raises(RuntimeError, match="alias collision"): | |
| read_installed_tool_spaces(tmp_path) | |
| def test_tool_spaces_add_rejects_alias_collision( | |
| tmp_path: Path, | |
| monkeypatch: pytest.MonkeyPatch, | |
| ) -> None: | |
| """A second Space whose slug normalizes to the same alias must be rejected.""" | |
| monkeypatch.chdir(tmp_path) | |
| monkeypatch.setattr( | |
| "peachy.tool_spaces.HfApi.space_info", | |
| lambda self, slug, **kwargs: _mock_public_space_info(slug), | |
| ) | |
| monkeypatch.setattr( | |
| "peachy.tool_spaces.RemoteMcpToolClient.list_tool_specs", | |
| _mock_list_tool_specs, | |
| ) | |
| assert ( | |
| _run_cli( | |
| monkeypatch, | |
| ["app", "tool-spaces", "add", SEARCH_SPACE_SLUG, "--install-only"], | |
| ) | |
| == 0 | |
| ) | |
| # The owner separator style differs, but both slugs normalize to the same alias. | |
| assert ( | |
| _run_cli( | |
| monkeypatch, | |
| ["app", "tool-spaces", "add", COLLIDING_SEARCH_SPACE_SLUG, "--install-only"], | |
| ) | |
| == 1 | |
| ) | |
| def _setup_profile(tmp_path: Path, profile: str, existing_tools: list[str] | None = None) -> Path: | |
| """Create a profile directory with an optional tools.txt.""" | |
| profile_dir = tmp_path / profile | |
| profile_dir.mkdir(parents=True) | |
| tools_txt = profile_dir / "tools.txt" | |
| tools_txt.write_text("\n".join(existing_tools or []) + "\n" if existing_tools else "", encoding="utf-8") | |
| return tools_txt | |
| def _mock_add(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None: | |
| monkeypatch.chdir(tmp_path) | |
| monkeypatch.setattr( | |
| "peachy.tool_spaces.HfApi.space_info", | |
| lambda self, slug, **kwargs: _mock_public_space_info(slug), | |
| ) | |
| monkeypatch.setattr( | |
| "peachy.tool_spaces.RemoteMcpToolClient.list_tool_specs", | |
| _mock_list_tool_specs, | |
| ) | |
| def test_tool_spaces_add_enables_in_active_profile_by_default( | |
| tmp_path: Path, | |
| monkeypatch: pytest.MonkeyPatch, | |
| ) -> None: | |
| """Add without flags should enable tools in the active profile.""" | |
| _mock_add(monkeypatch, tmp_path) | |
| tools_txt = _setup_profile(tmp_path, "default") | |
| monkeypatch.setattr(config_mod.config, "PROFILES_DIRECTORY", tmp_path) | |
| monkeypatch.setattr(config_mod.config, "REACHY_MINI_CUSTOM_PROFILE", None) | |
| assert _run_cli(monkeypatch, ["app", "tool-spaces", "add", SEARCH_SPACE_SLUG]) == 0 | |
| assert SEARCH_TOOL_ID in tools_txt.read_text(encoding="utf-8") | |
| def test_tool_spaces_remove_disables_tools_in_profile( | |
| tmp_path: Path, | |
| monkeypatch: pytest.MonkeyPatch, | |
| ) -> None: | |
| """Removing a Space strips its tool IDs from the profile they were enabled in.""" | |
| _mock_add(monkeypatch, tmp_path) | |
| tools_txt = _setup_profile(_test_state_root() / "user_personalities", "default") | |
| monkeypatch.setattr(config_mod.config, "PROFILES_DIRECTORY", tmp_path) | |
| monkeypatch.setattr(config_mod.config, "REACHY_MINI_CUSTOM_PROFILE", "user_personalities/default") | |
| assert _run_cli(monkeypatch, ["app", "tool-spaces", "add", SEARCH_SPACE_SLUG]) == 0 | |
| assert SEARCH_TOOL_ID in tools_txt.read_text(encoding="utf-8") | |
| assert _run_cli(monkeypatch, ["app", "tool-spaces", "remove", SEARCH_SPACE_SLUG]) == 0 | |
| assert SEARCH_TOOL_ID not in tools_txt.read_text(encoding="utf-8") | |
| assert SEARCH_SPACE_SLUG not in [space.slug for space in read_installed_tool_spaces(_test_state_root()).spaces] | |
| def test_tool_spaces_add_install_only_skips_tools_txt( | |
| tmp_path: Path, | |
| monkeypatch: pytest.MonkeyPatch, | |
| ) -> None: | |
| """--install-only should not modify any profile's tools.txt.""" | |
| _mock_add(monkeypatch, tmp_path) | |
| tools_txt = _setup_profile(tmp_path, "default") | |
| monkeypatch.setattr(config_mod.config, "PROFILES_DIRECTORY", tmp_path) | |
| assert _run_cli(monkeypatch, ["app", "tool-spaces", "add", SEARCH_SPACE_SLUG, "--install-only"]) == 0 | |
| assert tools_txt.read_text(encoding="utf-8") == "" | |
| def test_tool_spaces_add_profile_flag_enables_in_specified_profile( | |
| tmp_path: Path, | |
| monkeypatch: pytest.MonkeyPatch, | |
| ) -> None: | |
| """--profile should enable tools in the named profile, not the active one.""" | |
| _mock_add(monkeypatch, tmp_path) | |
| default_tools_txt = _setup_profile(tmp_path, "default") | |
| canary_tools_txt = _setup_profile(tmp_path, "canary") | |
| monkeypatch.setattr(config_mod.config, "PROFILES_DIRECTORY", tmp_path) | |
| monkeypatch.setattr(config_mod.config, "REACHY_MINI_CUSTOM_PROFILE", "default") | |
| assert _run_cli(monkeypatch, ["app", "tool-spaces", "add", SEARCH_SPACE_SLUG, "--profile", "canary"]) == 0 | |
| assert SEARCH_TOOL_ID in canary_tools_txt.read_text(encoding="utf-8") | |
| assert default_tools_txt.read_text(encoding="utf-8") == "" | |
| def test_read_installed_tool_spaces_seeds_bundled_pollen_spaces( | |
| tmp_path: Path, | |
| monkeypatch: pytest.MonkeyPatch, | |
| ) -> None: | |
| """With no manifest, the three bundled Pollen Spaces are seeded with their tools cached offline.""" | |
| monkeypatch.chdir(tmp_path) | |
| spaces = read_installed_tool_spaces(_test_state_root()).spaces | |
| assert [space.slug for space in spaces] == [ | |
| "pollen-robotics/reachy-mini-search-tool", | |
| "pollen-robotics/reachy-mini-time-tool", | |
| "pollen-robotics/reachy-mini-weather-tool", | |
| ] | |
| search_tool = spaces[0].tools[0] | |
| assert search_tool.local_name == "pollen_robotics_reachy_mini_search_tool__search_web" | |
| assert search_tool.remote_name == "reachy_mini_search_tool_search_web" | |
| assert spaces[0].private is False | |
| def test_builtin_profile_cannot_be_mutated( | |
| tmp_path: Path, | |
| monkeypatch: pytest.MonkeyPatch, | |
| ) -> None: | |
| builtin = tmp_path / "package-profiles" | |
| tools_txt = _setup_profile(builtin, "default") | |
| monkeypatch.setattr(config_mod.config, "PROFILES_DIRECTORY", builtin) | |
| monkeypatch.setattr(tool_spaces_mod, "DEFAULT_PROFILES_DIRECTORY", builtin) | |
| with pytest.raises(RuntimeError, match="read-only"): | |
| tool_spaces_mod._append_tools_to_profile("default", [SEARCH_TOOL_ID]) | |
| assert tools_txt.read_text(encoding="utf-8") == "" | |
| def test_append_tools_rejects_profile_traversal( | |
| tmp_path: Path, | |
| monkeypatch: pytest.MonkeyPatch, | |
| ) -> None: | |
| external_profiles = tmp_path / "external-profiles" | |
| escaped_tools = _setup_profile(tmp_path, "static") | |
| monkeypatch.setattr(config_mod.config, "PROFILES_DIRECTORY", external_profiles) | |
| with pytest.raises(RuntimeError, match="Invalid profile selector"): | |
| tool_spaces_mod._append_tools_to_profile("../static", [SEARCH_TOOL_ID]) | |
| assert escaped_tools.read_text(encoding="utf-8") == "" | |
| def test_append_tools_rejects_tools_file_symlink_to_builtin_profile( | |
| tmp_path: Path, | |
| monkeypatch: pytest.MonkeyPatch, | |
| ) -> None: | |
| builtin = tmp_path / "package-profiles" | |
| builtin_tools = _setup_profile(builtin, "default", ["builtin_tool"]) | |
| external_profiles = tmp_path / "external-profiles" | |
| external_profile = external_profiles / "custom" | |
| external_profile.mkdir(parents=True) | |
| (external_profile / "tools.txt").symlink_to(builtin_tools) | |
| monkeypatch.setattr(config_mod.config, "PROFILES_DIRECTORY", external_profiles) | |
| monkeypatch.setattr(tool_spaces_mod, "DEFAULT_PROFILES_DIRECTORY", builtin) | |
| original_contents = builtin_tools.read_bytes() | |
| with pytest.raises(RuntimeError, match="not inside a writable profile root"): | |
| tool_spaces_mod._append_tools_to_profile("custom", [SEARCH_TOOL_ID]) | |
| assert builtin_tools.read_bytes() == original_contents | |
| def test_remove_space_only_rewrites_user_profiles( | |
| tmp_path: Path, | |
| monkeypatch: pytest.MonkeyPatch, | |
| ) -> None: | |
| builtin = tmp_path / "package-profiles" | |
| user = tmp_path / "state" / "user_personalities" | |
| builtin_tools = _setup_profile(builtin, "default", [SEARCH_TOOL_ID]) | |
| user_tools = _setup_profile(user, "custom", [SEARCH_TOOL_ID]) | |
| monkeypatch.setattr(config_mod.config, "PROFILES_DIRECTORY", builtin) | |
| monkeypatch.setattr(config_mod.config, "user_personalities_root", lambda: user) | |
| removed = tool_spaces_mod._disable_space_tools_in_profiles(SEARCH_ALIAS) | |
| assert removed == [("custom", [SEARCH_TOOL_ID])] | |
| assert SEARCH_TOOL_ID in builtin_tools.read_text(encoding="utf-8") | |
| assert SEARCH_TOOL_ID not in user_tools.read_text(encoding="utf-8") | |
| def test_remove_space_skips_symlinked_user_profile_that_escapes_state_root( | |
| tmp_path: Path, | |
| monkeypatch: pytest.MonkeyPatch, | |
| ) -> None: | |
| builtin = tmp_path / "package-profiles" | |
| builtin_tools = _setup_profile(builtin, "default", [SEARCH_TOOL_ID]) | |
| user_root = tmp_path / "state" / "user_personalities" | |
| user_root.mkdir(parents=True) | |
| (user_root / "escaped").symlink_to(builtin / "default", target_is_directory=True) | |
| monkeypatch.setattr(config_mod.config, "user_personalities_root", lambda: user_root) | |
| removed = tool_spaces_mod._disable_space_tools_in_profiles(SEARCH_ALIAS) | |
| assert removed == [] | |
| assert SEARCH_TOOL_ID in builtin_tools.read_text(encoding="utf-8") | |
| def test_remove_space_skips_user_root_symlink_to_builtin_profiles( | |
| tmp_path: Path, | |
| monkeypatch: pytest.MonkeyPatch, | |
| ) -> None: | |
| builtin = tmp_path / "package-profiles" | |
| builtin_tools = _setup_profile(builtin, "default", [SEARCH_TOOL_ID]) | |
| user_root_link = tmp_path / "state" / "user_personalities" | |
| user_root_link.parent.mkdir(parents=True) | |
| user_root_link.symlink_to(builtin, target_is_directory=True) | |
| monkeypatch.setattr(config_mod.config, "user_personalities_root", lambda: user_root_link) | |
| monkeypatch.setattr(tool_spaces_mod, "DEFAULT_PROFILES_DIRECTORY", builtin) | |
| removed = tool_spaces_mod._disable_space_tools_in_profiles(SEARCH_ALIAS) | |
| assert removed == [] | |
| assert SEARCH_TOOL_ID in builtin_tools.read_text(encoding="utf-8") | |