Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/refactor backend tests env dependency #72

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 0 additions & 20 deletions .github/workflows/test-backend.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,26 +24,6 @@ jobs:
pip install -r requirements.txt
pip install pytest-md pytest-emoji

# some tests require an .env file to run.
# file should contain all the required properties.
- name: Create environment file
run: |
echo "Create environment file"
cat <<EOF > .env
MODEL=my-model
MISTRAL_KEY=123456
NEO4J_URI=bolt://localhost:1234
NEO4J_USERNAME=neo4j
NEO4J_PASSWORD=my-password
ANSWER_AGENT_LLM=mistral
INTENT_AGENT_LLM=mistral
VALIDATOR_AGENT_LLM=mistral
DATASTORE_AGENT_LLM=mistral
MATHS_AGENT_LLM=mistral
ROUTER_LLM=mistral
EOF
cat .env

- name: Run tests
uses: pavelzw/pytest-action@v2
with:
Expand Down
37 changes: 26 additions & 11 deletions backend/src/agents/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from typing import List
from src.utils import Config
from .agent import Agent, agent
from .datastore_agent import DatastoreAgent
Expand All @@ -10,26 +11,40 @@
config = Config()


validator_agent = ValidatorAgent(config.validator_agent_llm)
intent_agent = IntentAgent(config.intent_agent_llm)
answer_agent = AnswerAgent(config.answer_agent_llm)
def get_validator_agent() -> Agent:
return ValidatorAgent(config.validator_agent_llm)


def get_agent_details(agent):
def get_intent_agent() -> Agent:
return IntentAgent(config.intent_agent_llm)


def get_answer_agent() -> Agent:
return AnswerAgent(config.answer_agent_llm)


def agent_details(agent) -> dict:
return {"name": agent.name, "description": agent.description}


agents = [DatastoreAgent(config.datastore_agent_llm), MathsAgent(config.maths_agent_llm)]
agents_details = [get_agent_details(agent) for agent in agents]
def get_available_agents() -> List[Agent]:
return [DatastoreAgent(config.datastore_agent_llm), MathsAgent(config.maths_agent_llm)]


def get_agent_details():
agents = get_available_agents()
return [agent_details(agent) for agent in agents]


__all__ = [
"agent",
"Agent",
"agents_details",
"agents",
"answer_agent",
"intent_agent",
"agent_details",
"get_agent_details",
"get_answer_agent",
"get_intent_agent",
"get_available_agents",
"get_validator_agent",
"Parameter",
"tool",
"validator_agent",
]
10 changes: 5 additions & 5 deletions backend/src/director.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import json
import logging
from src.utils.scratchpad import clear_scratchpad, update_scratchpad
from src.agents import intent_agent, answer_agent
from src.utils import clear_scratchpad, update_scratchpad
from src.agents import get_intent_agent, get_answer_agent
from src.prompts import PromptEngine
from src.supervisors import solve_all

Expand All @@ -11,9 +11,9 @@
director_prompt = engine.load_prompt("director")


def question(question) -> str:
def question(question: str) -> str:
logging.debug("Received utterance: {question}")
intent = intent_agent.invoke(question)
intent = get_intent_agent().invoke(question)
intent_json = json.loads(intent)
logging.info(f"Intent determined: {intent}")

Expand All @@ -22,7 +22,7 @@ def question(question) -> str:
except Exception as error:
update_scratchpad(error=str(error))

final_answer = answer_agent.invoke(question)
final_answer = get_answer_agent().invoke(question)
logging.info(f"final answer: {final_answer}")

clear_scratchpad()
Expand Down
8 changes: 4 additions & 4 deletions backend/src/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,14 @@
import logging
from src.utils import to_json, Config
from src.prompts import PromptEngine
from src.agents import Agent, agents, agents_details
from src.agents import Agent, get_available_agents, get_agent_details
from src.llm import get_llm

prompt_engine = PromptEngine()
config = Config()

llm = get_llm(config.router_llm)


def build_best_next_step_prompt(task, scratchpad):
agents_details = get_agent_details()
return prompt_engine.load_prompt(
"best-next-step",
task=json.dumps(task, indent=4),
Expand Down Expand Up @@ -41,10 +39,12 @@ def build_plan(task, llm, scratchpad):


def find_agent_from_name(name):
agents = get_available_agents()
return (agent for agent in agents if agent.name == name)


def get_agent_for_task(task, scratchpad) -> Agent | None:
llm = get_llm(config.router_llm)
plan = build_plan(task, llm, scratchpad)
agent = next(find_agent_from_name(plan["agent_name"]), None)

Expand Down
4 changes: 2 additions & 2 deletions backend/src/supervisors/supervisor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from src.utils import get_scratchpad, update_scratchpad
from src.router import get_agent_for_task
from src.agents import validator_agent
from src.agents import get_validator_agent

no_questions_response = "No questions found to solve"
unsolvable_response = "I am sorry, but I was unable to find an answer to this task"
Expand Down Expand Up @@ -39,5 +39,5 @@ def solve_task(task, scratchpad, attempt=0) -> Tuple[str, str]:


def is_valid_answer(answer, task) -> bool:
is_valid = (validator_agent.invoke(f"Task: {task} Answer: {answer}")).lower() == "true"
is_valid = (get_validator_agent().invoke(f"Task: {task} Answer: {answer}")).lower() == "true"
return is_valid
15 changes: 9 additions & 6 deletions backend/tests/router_test.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import json
from src.llm import get_llm
from src.llm import MockLLM
from src.agents import agent_details
from tests.agents import MockAgent, mock_agent_name
from src.router import get_agent_for_task


mock_model = get_llm("mockllm")
mock_model = MockLLM()
mock_agent = MockAgent("mockllm")
mock_agents = [mock_agent]
task = {"summary": "task1"}
Expand All @@ -13,8 +14,9 @@

def test_get_agent_for_task_no_agent_found(mocker):
plan = '{ "agent_name": "this_agent_does_not_exist" }'
mocker.patch("src.router.agents", mock_agents)
mocker.patch("src.router.llm", mock_model)
mocker.patch("src.router.get_llm", return_value=mock_model)
mocker.patch("src.router.get_available_agents", return_value=mock_agents)
mocker.patch("src.router.get_agent_details", return_value=[agent_details(mock_agent)])
mock_model.chat = mocker.MagicMock(return_value=plan)

agent = get_agent_for_task(task, scratchpad)
Expand All @@ -24,8 +26,9 @@ def test_get_agent_for_task_no_agent_found(mocker):

def test_get_agent_for_task_agent_found(mocker):
plan = {"agent_name": mock_agent_name}
mocker.patch("src.router.agents", mock_agents)
mocker.patch("src.router.llm", mock_model)
mocker.patch("src.router.get_llm", return_value=mock_model)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just so I understand, we're not actually using the get_llm function which needs the env file, instead we're instantiating a mock value for llm with 'mockllm' on line 8?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, this is a little confusing, I'll change. Yes, the test is mocking out get_llm so that whenever it is called during the test, the mock_model on line 8 will always be returned. The confuding part is that line 8 is actually using get_llm to get the mock model. It would make more sense if line 8 was:

mock_model = MockLLM()

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes okay, thanks that makes sense

mocker.patch("src.router.get_available_agents", return_value=mock_agents)
mocker.patch("src.router.get_agent_details", return_value=[agent_details(mock_agent)])
mock_model.chat = mocker.MagicMock(return_value=json.dumps(plan))

agent = get_agent_for_task(task, scratchpad)
Expand Down
2 changes: 0 additions & 2 deletions backend/tests/supervisors/supervisor_test.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import pytest
from src.llm import get_llm
from src.utils import get_scratchpad
from tests.agents import MockAgent
from src.supervisors import solve_all, solve_task, no_questions_response, unsolvable_response, no_agent_response
Expand All @@ -25,7 +24,6 @@
],
}

model = get_llm("mockllm")
agent = MockAgent("mockllm")


Expand Down
Loading