import sys import importlib from types import ModuleType from pathlib import Path import pytest import anima_conversation_app.config as config_mod def _reload_core_tools() -> ModuleType: """Reload core_tools after config object has been patched.""" for module_name in list(sys.modules): if module_name.startswith("anima_conversation_app.tools."): sys.modules.pop(module_name, None) # External file-loaded modules are registered by bare tool name. sys.modules.pop("ext_ping", None) sys.modules.pop("anima_conversation_app.tools.core_tools", None) core_tools_mod = importlib.import_module("anima_conversation_app.tools.core_tools") return core_tools_mod def test_external_profile_can_use_builtin_tools( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: """External profile tools.txt can reference built-in src tools.""" profile_name = "ext_profile_test" external_profiles_root = tmp_path / "external_profiles" profile_dir = external_profiles_root / profile_name profile_dir.mkdir(parents=True) (profile_dir / "instructions.txt").write_text("hello\n", encoding="utf-8") (profile_dir / "tools.txt").write_text("dance\n", encoding="utf-8") monkeypatch.setattr(config_mod.config, "REACHY_MINI_CUSTOM_PROFILE", profile_name) monkeypatch.setattr(config_mod.config, "PROFILES_DIRECTORY", external_profiles_root) monkeypatch.setattr(config_mod.config, "TOOLS_DIRECTORY", None) monkeypatch.setattr(config_mod.config, "AUTOLOAD_EXTERNAL_TOOLS", False) core_tools_mod = _reload_core_tools() assert "dance" in core_tools_mod.ALL_TOOLS def test_external_tools_can_be_loaded_without_external_profile( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: """External tools can be loaded with built-in profile via autoload mode.""" external_tools_root = tmp_path / "external_tools" external_tools_root.mkdir(parents=True) (external_tools_root / "ext_ping.py").write_text( "\n".join( [ "from typing import Any, Dict", "from anima_conversation_app.tools.core_tools import Tool, ToolDependencies", "", "class ExtPingTool(Tool):", " name = \"ext_ping\"", " description = \"External ping tool\"", " parameters_schema = {\"type\": \"object\", \"properties\": {}, \"required\": []}", "", " async def __call__(self, deps: ToolDependencies, **kwargs: Any) -> Dict[str, Any]:", " return {\"status\": \"ok\"}", "", ] ), encoding="utf-8", ) monkeypatch.setattr(config_mod.config, "REACHY_MINI_CUSTOM_PROFILE", "default") monkeypatch.setattr(config_mod.config, "PROFILES_DIRECTORY", config_mod.DEFAULT_PROFILES_DIRECTORY) monkeypatch.setattr(config_mod.config, "TOOLS_DIRECTORY", external_tools_root) monkeypatch.setattr(config_mod.config, "AUTOLOAD_EXTERNAL_TOOLS", True) core_tools_mod = _reload_core_tools() assert "ext_ping" in core_tools_mod.ALL_TOOLS