refactor(agent): extract agent+shared into standalone packages with full GRACE semantic markup
- Move agent code from backend/src/agent/ to agent/src/ss_tools/agent/ - Extract shared stdlib-only utilities to shared/src/ss_tools/shared/ - Add #region/#endregion contracts to all ~140 functions (INV_1 compliance) - Update docker files, entrypoint, build scripts for new package layout - Backend now imports ss_tools.shared._llm_health (no gradio/langchain deps) - Add specs for 036-039 feature plans
This commit is contained in:
183
agent/tests/test_agent/test_langgraph_setup.py
Normal file
183
agent/tests/test_agent/test_langgraph_setup.py
Normal file
@@ -0,0 +1,183 @@
|
||||
# #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 ss_tools.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 ss_tools.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 ss_tools.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("ss_tools.agent.langgraph_setup.ChatOpenAI") as mock_llm, \
|
||||
patch("ss_tools.agent.langgraph_setup.create_react_agent") as mock_create, \
|
||||
patch("ss_tools.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_raises_error_when_no_llm_configured(self):
|
||||
import ss_tools.agent.langgraph_setup as ls
|
||||
ls._llm_config = None # Reset
|
||||
with patch("ss_tools.agent.langgraph_setup._fetch_llm_config", new=AsyncMock(return_value=None)):
|
||||
with pytest.raises(RuntimeError, match="No LLM provider configured in backend"):
|
||||
await ls.create_agent([])
|
||||
ls._llm_config = None
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_creates_agent_with_partial_api_config(self):
|
||||
import ss_tools.agent.langgraph_setup as ls
|
||||
ls._llm_config = None
|
||||
ls.configure_from_api({
|
||||
"configured": True,
|
||||
"api_key": "sk-key-only",
|
||||
})
|
||||
with patch("ss_tools.agent.langgraph_setup.ChatOpenAI") as mock_llm, \
|
||||
patch("ss_tools.agent.langgraph_setup.create_react_agent") as mock_create, \
|
||||
patch("ss_tools.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"] is None
|
||||
assert call_kwargs["model"] is None
|
||||
ls._llm_config = None
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_uses_inmemory_saver(self):
|
||||
import ss_tools.agent.langgraph_setup as ls
|
||||
ls._llm_config = None
|
||||
ls.configure_from_api({
|
||||
"configured": True,
|
||||
"api_key": "sk-test",
|
||||
"base_url": "",
|
||||
"default_model": "gpt-4o-mini",
|
||||
})
|
||||
with patch("ss_tools.agent.langgraph_setup.ChatOpenAI") as mock_llm, \
|
||||
patch("ss_tools.agent.langgraph_setup.create_react_agent") as mock_create, \
|
||||
patch("ss_tools.agent.langgraph_setup._fetch_llm_config", new=AsyncMock(return_value=ls._llm_config)):
|
||||
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 ss_tools.agent.langgraph_setup as ls
|
||||
ls._llm_config = None
|
||||
ls.configure_from_api({
|
||||
"configured": True,
|
||||
"api_key": "sk-test",
|
||||
})
|
||||
with patch("ss_tools.agent.langgraph_setup.ChatOpenAI"), \
|
||||
patch("ss_tools.agent.langgraph_setup.create_react_agent") as mock_create, \
|
||||
patch("ss_tools.agent.langgraph_setup._fetch_llm_config", new=AsyncMock(return_value=ls._llm_config)):
|
||||
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 ss_tools.agent.langgraph_setup as ls
|
||||
ls._llm_config = None
|
||||
ls.configure_from_api({
|
||||
"configured": True,
|
||||
"api_key": "sk-test",
|
||||
})
|
||||
with patch("ss_tools.agent.langgraph_setup.ChatOpenAI"), \
|
||||
patch("ss_tools.agent.langgraph_setup.create_react_agent") as mock_create, \
|
||||
patch("ss_tools.agent.langgraph_setup._fetch_llm_config", new=AsyncMock(return_value=ls._llm_config)), \
|
||||
patch("ss_tools.agent.langgraph_setup.AGENT_CONFIRM_TOOLS", True):
|
||||
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 ss_tools.agent.langgraph_setup as ls
|
||||
ls._llm_config = None
|
||||
ls.configure_from_api({
|
||||
"configured": True,
|
||||
"api_key": "sk-test",
|
||||
})
|
||||
with patch("ss_tools.agent.langgraph_setup.ChatOpenAI"), \
|
||||
patch("ss_tools.agent.langgraph_setup.create_react_agent") as mock_create, \
|
||||
patch("ss_tools.agent.langgraph_setup._fetch_llm_config", new=AsyncMock(return_value=ls._llm_config)), \
|
||||
patch("ss_tools.agent.langgraph_setup._INTERRUPT_BEFORE", "tools"):
|
||||
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 ss_tools.agent.langgraph_setup as ls
|
||||
ls._llm_config = None
|
||||
ls.configure_from_api({
|
||||
"configured": True,
|
||||
"api_key": "sk-test",
|
||||
})
|
||||
with patch("ss_tools.agent.langgraph_setup.ChatOpenAI"), \
|
||||
patch("ss_tools.agent.langgraph_setup.create_react_agent") as mock_create, \
|
||||
patch("ss_tools.agent.langgraph_setup._fetch_llm_config", new=AsyncMock(return_value=ls._llm_config)), \
|
||||
patch("ss_tools.agent.langgraph_setup.AGENT_CONFIRM_TOOLS", True):
|
||||
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
|
||||
Reference in New Issue
Block a user