| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314 |
- """Tests for agency_swarm.utils.files module.
- Tests the get_external_caller_directory() function which resolves the directory
- of the first caller outside the agency_swarm package. This is used to resolve
- relative paths like "./instructions.md" or "./tools" against the user's module file.
- """
- import os
- import subprocess
- import sys
- import textwrap
- from pathlib import Path
- from agency_swarm.utils.files import _get_package_root, get_external_caller_directory
- class TestGetExternalCallerDirectory:
- """Tests for get_external_caller_directory()."""
- def test_returns_directory_when_called_from_external_file(self, tmp_path: Path):
- """When called from user code, returns the directory of that user file."""
- # Create a user script that imports and calls get_external_caller_directory
- user_dir = tmp_path / "my_agency"
- user_dir.mkdir()
- user_script = user_dir / "create_agent.py"
- user_script.write_text(
- textwrap.dedent("""
- from agency_swarm.utils.files import get_external_caller_directory
- result = get_external_caller_directory()
- print(result)
- """)
- )
- # Execute the user script and capture output
- result = subprocess.run(
- [sys.executable, str(user_script)],
- capture_output=True,
- text=True,
- cwd=str(tmp_path),
- )
- assert result.returncode == 0, f"Script failed: {result.stderr}"
- output_path = result.stdout.strip()
- assert output_path == str(user_dir)
- def test_agent_instructions_resolve_relative_to_agent_file(self, tmp_path: Path):
- """
- Real use case: Agent created with instructions="./instructions.md"
- should load instructions from the agent's directory, not CWD.
- """
- # Create directory structure mimicking user's project
- agent_dir = tmp_path / "agents" / "ceo"
- agent_dir.mkdir(parents=True)
- # Create instructions file in agent directory
- instructions_file = agent_dir / "instructions.md"
- instructions_file.write_text("You are the CEO agent. Lead with vision.")
- # Create user's agent file
- agent_script = agent_dir / "ceo_agent.py"
- agent_script.write_text(
- textwrap.dedent("""
- import sys
- # Add src to path for agency_swarm import
- sys.path.insert(0, sys.argv[1])
- from agency_swarm import Agent
- agent = Agent(
- name="CEO",
- instructions="./instructions.md",
- model="gpt-5.4-mini",
- )
- print(agent.instructions)
- """)
- )
- # Execute from a DIFFERENT directory (not the agent's directory)
- # This proves paths resolve relative to file location, not CWD
- src_path = str(Path(__file__).parent.parent.parent / "src")
- result = subprocess.run(
- [sys.executable, str(agent_script), src_path],
- capture_output=True,
- text=True,
- cwd=str(tmp_path), # CWD is parent, not agent_dir
- )
- assert result.returncode == 0, f"Script failed: {result.stderr}"
- assert "You are the CEO agent" in result.stdout
- def test_agent_tools_folder_resolves_relative_to_agent_file(self, tmp_path: Path):
- """
- Real use case: Agent with tools_folder="./tools" should load tools
- from the agent's tools subdirectory.
- """
- # Create directory structure
- agent_dir = tmp_path / "agents" / "researcher"
- tools_dir = agent_dir / "tools"
- tools_dir.mkdir(parents=True)
- # Create a tool file
- tool_file = tools_dir / "search_tool.py"
- tool_file.write_text(
- textwrap.dedent("""
- from agents import function_tool
- @function_tool
- def search_web(query: str) -> str:
- \"\"\"Search the web for information.\"\"\"
- return f"Results for: {query}"
- """)
- )
- # Create user's agent file
- agent_script = agent_dir / "researcher.py"
- agent_script.write_text(
- textwrap.dedent("""
- import sys
- sys.path.insert(0, sys.argv[1])
- from agency_swarm import Agent
- agent = Agent(
- name="Researcher",
- instructions="Research things",
- tools_folder="./tools",
- model="gpt-5.4-mini",
- )
- tool_names = [t.name for t in agent.tools]
- print(",".join(tool_names))
- """)
- )
- src_path = str(Path(__file__).parent.parent.parent / "src")
- result = subprocess.run(
- [sys.executable, str(agent_script), src_path],
- capture_output=True,
- text=True,
- cwd=str(tmp_path), # Different CWD
- )
- assert result.returncode == 0, f"Script failed: {result.stderr}"
- assert "search_web" in result.stdout
- def test_agency_shared_instructions_resolve_relative_to_agency_file(self, tmp_path: Path):
- """
- Real use case: Agency with shared_instructions="agency_manifesto.md"
- should load from the agency's directory.
- """
- agency_dir = tmp_path / "my_agency"
- agency_dir.mkdir()
- # Create manifesto file
- manifesto_file = agency_dir / "agency_manifesto.md"
- manifesto_file.write_text("Our mission: Be helpful and accurate.")
- # Create agency script
- agency_script = agency_dir / "agency.py"
- agency_script.write_text(
- textwrap.dedent("""
- import sys
- sys.path.insert(0, sys.argv[1])
- from agency_swarm import Agency, Agent
- ceo = Agent(name="CEO", instructions="Lead", model="gpt-5.4-mini")
- agency = Agency(ceo, shared_instructions="agency_manifesto.md")
- print(agency.shared_instructions)
- """)
- )
- src_path = str(Path(__file__).parent.parent.parent / "src")
- result = subprocess.run(
- [sys.executable, str(agency_script), src_path],
- capture_output=True,
- text=True,
- cwd=str(tmp_path),
- )
- assert result.returncode == 0, f"Script failed: {result.stderr}"
- assert "Our mission:" in result.stdout
- def test_fallback_to_cwd_when_no_external_caller(self):
- """When no file-backed external caller exists, returns os.getcwd()."""
- # This tests the fallback when called from within the package
- # or when the stack has no external callers
- result = get_external_caller_directory()
- # Since this test file IS outside agency_swarm package,
- # it should return THIS file's directory
- expected = str(Path(__file__).parent)
- assert result == expected
- def test_fallback_to_cwd_when_package_root_not_found(self):
- """When internal_package doesn't exist, returns os.getcwd()."""
- result = get_external_caller_directory(internal_package="nonexistent_package_xyz")
- assert result == os.getcwd()
- class TestGetPackageRoot:
- """Tests for _get_package_root() helper function."""
- def test_returns_path_for_valid_package(self):
- """Returns the package root path for a valid package."""
- result = _get_package_root("agency_swarm")
- assert result is not None
- assert result.name == "agency_swarm"
- assert result.is_dir()
- def test_returns_none_for_invalid_package(self):
- """Returns None for a package that doesn't exist."""
- result = _get_package_root("this_package_does_not_exist_xyz")
- assert result is None
- def test_returns_none_for_builtin_module(self):
- """Returns None for built-in modules without __file__."""
- # sys is a built-in module that might not have __file__
- # We test by providing a package name that imports but has no file
- result = _get_package_root("builtins")
- assert result is None
- def test_caches_results(self):
- """Results are cached via lru_cache."""
- # Clear the cache first
- _get_package_root.cache_clear()
- # Call twice
- result1 = _get_package_root("agency_swarm")
- result2 = _get_package_root("agency_swarm")
- # Results should be identical (same object due to cache)
- assert result1 is result2
- # Check cache info shows hit
- cache_info = _get_package_root.cache_info()
- assert cache_info.hits >= 1
- class TestSpecialFilenameFiltering:
- """Tests for filtering special Python filenames like <stdin>, <string>."""
- def test_code_from_exec_falls_back_to_cwd(self, tmp_path: Path):
- """
- Code executed via exec() has filename '<string>' and should be filtered.
- The function should skip such frames and find the real caller or fall back.
- """
- # Create a script that uses exec() to call get_external_caller_directory
- test_script = tmp_path / "exec_test.py"
- script_content = """\
- import sys
- sys.path.insert(0, sys.argv[1])
- from agency_swarm.utils.files import get_external_caller_directory
- # This code will be executed with filename='<string>'
- code = '''
- result = get_external_caller_directory()
- print(result)
- '''
- exec(code)
- """
- test_script.write_text(script_content)
- src_path = str(Path(__file__).parent.parent.parent / "src")
- result = subprocess.run(
- [sys.executable, str(test_script), src_path],
- capture_output=True,
- text=True,
- cwd=str(tmp_path),
- )
- assert result.returncode == 0, f"Script failed: {result.stderr}"
- output_path = result.stdout.strip()
- # Should return the outer script's directory (tmp_path), not error out
- # because the <string> frame is skipped and the outer exec_test.py is found
- assert output_path == str(tmp_path)
- def test_handles_mixed_stack_with_special_frames(self, tmp_path: Path):
- """
- When the call stack has both special frames and real file frames,
- the function should skip special frames and find the first real external caller.
- """
- # Create nested structure where inner code uses eval/exec
- outer_dir = tmp_path / "outer"
- outer_dir.mkdir()
- outer_script = outer_dir / "outer.py"
- outer_script.write_text(
- textwrap.dedent("""
- import sys
- sys.path.insert(0, sys.argv[1])
- def call_via_exec():
- from agency_swarm.utils.files import get_external_caller_directory
- # This exec adds a <string> frame to the stack
- code = "result = get_external_caller_directory(); print(result)"
- exec(code)
- call_via_exec()
- """)
- )
- src_path = str(Path(__file__).parent.parent.parent / "src")
- result = subprocess.run(
- [sys.executable, str(outer_script), src_path],
- capture_output=True,
- text=True,
- cwd=str(tmp_path),
- )
- assert result.returncode == 0, f"Script failed: {result.stderr}"
- output_path = result.stdout.strip()
- # Should find outer.py as the external caller (skipping <string>)
- assert output_path == str(outer_dir)
|