# #region Test.AgentChat.LangGraph.Setup [C:3] [TYPE Module] [SEMANTICS test,agent,langgraph,setup] # @BRIEF Tests for agent/langgraph_setup.py — configure_from_api, create_agent. # @RELATION BINDS_TO -> [AgentChat.LangGraph.Setup] from pathlib import Path import sys sys.path.insert(0, str(Path(__file__).parent.parent.parent / "src")) from unittest.mock import AsyncMock, MagicMock, patch import pytest @pytest.fixture def anyio_backend(): return "asyncio" # #region test_configure_from_api [C:2] [TYPE Function] # @BRIEF Test configure_from_api updates global config. class TestConfigureFromApi: def test_sets_llm_config(self): import src.agent.langgraph_setup as ls ls._llm_config = None # Reset ls.configure_from_api({"configured": True, "api_key": "sk-test", "default_model": "gpt-4o"}) assert ls._llm_config is not None assert ls._llm_config["configured"] is True # Reset for other tests ls.configure_from_api(None) ls._llm_config = None def test_overwrites_previous_config(self): import src.agent.langgraph_setup as ls ls._llm_config = None # Reset ls.configure_from_api({"configured": True, "api_key": "sk-1"}) ls.configure_from_api({"configured": False}) assert ls._llm_config["configured"] is False ls._llm_config = None # #endregion test_configure_from_api # #region test_create_agent [C:2] [TYPE Function] # @BRIEF Test create_agent with various LLM config states. class TestCreateAgent: @pytest.mark.anyio async def test_creates_agent_with_api_config(self): import src.agent.langgraph_setup as ls ls._llm_config = None # Reset ls.configure_from_api({ "configured": True, "api_key": "sk-api-config", "base_url": "https://custom.api.com/v1", "default_model": "gpt-4o-mini", }) with patch("src.agent.langgraph_setup.ChatOpenAI") as mock_llm, \ patch("src.agent.langgraph_setup.create_react_agent") as mock_create, \ patch("src.agent.langgraph_setup._fetch_llm_config", new=AsyncMock(return_value=ls._llm_config)): mock_create.return_value = MagicMock() result = await ls.create_agent([MagicMock()]) assert result is mock_create.return_value call_kwargs = mock_llm.call_args[1] assert call_kwargs["api_key"] == "sk-api-config" assert call_kwargs["base_url"] == "https://custom.api.com/v1" assert call_kwargs["model"] == "gpt-4o-mini" ls._llm_config = None @pytest.mark.anyio async def test_creates_agent_with_env_fallback(self): import src.agent.langgraph_setup as ls ls._llm_config = None # Reset with patch("src.agent.langgraph_setup.ChatOpenAI") as mock_llm, \ patch("src.agent.langgraph_setup.create_react_agent") as mock_create, \ patch("src.agent.langgraph_setup._fetch_llm_config", new=AsyncMock(return_value=None)), \ patch("src.agent.langgraph_setup.os.getenv") as mock_getenv: mock_getenv.side_effect = lambda key, default=None: { "LLM_API_KEY": "sk-env-key", "LLM_BASE_URL": "https://env.api.com", "LLM_MODEL": "gpt-4", }.get(key, default) mock_create.return_value = MagicMock() result = await ls.create_agent([]) assert result is mock_create.return_value call_kwargs = mock_llm.call_args[1] assert call_kwargs["api_key"] == "sk-env-key" assert call_kwargs["model"] == "gpt-4" ls._llm_config = None @pytest.mark.anyio async def test_creates_agent_with_partial_api_config(self): import src.agent.langgraph_setup as ls ls._llm_config = None ls.configure_from_api({ "configured": True, "api_key": "sk-key-only", }) with patch("src.agent.langgraph_setup.ChatOpenAI") as mock_llm, \ patch("src.agent.langgraph_setup.create_react_agent") as mock_create, \ patch("src.agent.langgraph_setup._fetch_llm_config", new=AsyncMock(return_value=ls._llm_config)): mock_create.return_value = MagicMock() result = await ls.create_agent([]) assert result is mock_create.return_value call_kwargs = mock_llm.call_args[1] assert call_kwargs["api_key"] == "sk-key-only" assert call_kwargs["base_url"] == "https://api.openai.com/v1" assert call_kwargs["model"] == "gpt-4o-mini" ls._llm_config = None @pytest.mark.anyio async def test_uses_inmemory_saver(self): import src.agent.langgraph_setup as ls ls._llm_config = None with patch("src.agent.langgraph_setup.ChatOpenAI") as mock_llm, \ patch("src.agent.langgraph_setup.create_react_agent") as mock_create, \ patch("src.agent.langgraph_setup._fetch_llm_config", new=AsyncMock(return_value=None)), \ patch("src.agent.langgraph_setup.os.getenv", return_value=None): mock_create.return_value = MagicMock() await ls.create_agent([]) call_kwargs = mock_create.call_args[1] from langgraph.checkpoint.memory import InMemorySaver assert isinstance(call_kwargs["checkpointer"], InMemorySaver) ls._llm_config = None @pytest.mark.anyio async def test_uses_empty_interrupt_list_by_default(self): import src.agent.langgraph_setup as ls ls._llm_config = None with patch("src.agent.langgraph_setup.ChatOpenAI"), \ patch("src.agent.langgraph_setup.create_react_agent") as mock_create, \ patch("src.agent.langgraph_setup._fetch_llm_config", new=AsyncMock(return_value=None)), \ patch("src.agent.langgraph_setup.os.getenv", return_value=None): mock_create.return_value = MagicMock() await ls.create_agent([]) assert mock_create.call_args[1]["interrupt_before"] == [] ls._llm_config = None @pytest.mark.anyio async def test_confirm_tools_env_interrupts_before_tools_node(self): import src.agent.langgraph_setup as ls ls._llm_config = None with patch("src.agent.langgraph_setup.ChatOpenAI"), \ patch("src.agent.langgraph_setup.create_react_agent") as mock_create, \ patch("src.agent.langgraph_setup._fetch_llm_config", new=AsyncMock(return_value=None)), \ patch("src.agent.langgraph_setup.os.getenv") as mock_getenv: mock_getenv.side_effect = lambda key, default=None: { "AGENT_CONFIRM_TOOLS": "true", }.get(key, default) mock_create.return_value = MagicMock() await ls.create_agent([]) assert mock_create.call_args[1]["interrupt_before"] == ["tools"] ls._llm_config = None @pytest.mark.anyio async def test_uses_env_configured_interrupt_nodes(self): import src.agent.langgraph_setup as ls ls._llm_config = None with patch("src.agent.langgraph_setup.ChatOpenAI"), \ patch("src.agent.langgraph_setup.create_react_agent") as mock_create, \ patch("src.agent.langgraph_setup._fetch_llm_config", new=AsyncMock(return_value=None)), \ patch("src.agent.langgraph_setup.os.getenv") as mock_getenv: mock_getenv.side_effect = lambda key, default=None: { "AGENT_INTERRUPT_BEFORE": "tools", }.get(key, default) mock_create.return_value = MagicMock() await ls.create_agent([]) assert mock_create.call_args[1]["interrupt_before"] == ["tools"] ls._llm_config = None @pytest.mark.anyio async def test_interrupt_override_bypasses_env_guardrail(self): import src.agent.langgraph_setup as ls ls._llm_config = None with patch("src.agent.langgraph_setup.ChatOpenAI"), \ patch("src.agent.langgraph_setup.create_react_agent") as mock_create, \ patch("src.agent.langgraph_setup._fetch_llm_config", new=AsyncMock(return_value=None)), \ patch("src.agent.langgraph_setup.os.getenv") as mock_getenv: mock_getenv.side_effect = lambda key, default=None: { "AGENT_CONFIRM_TOOLS": "true", }.get(key, default) mock_create.return_value = MagicMock() await ls.create_agent([], interrupt_before=[]) assert mock_create.call_args[1]["interrupt_before"] == [] ls._llm_config = None # #endregion test_create_agent # #endregion Test.AgentChat.LangGraph.Setup