diff --git a/agenthub/micro/README.md b/agenthub/micro/README.md new file mode 100644 index 0000000000..68be0a8a9a --- /dev/null +++ b/agenthub/micro/README.md @@ -0,0 +1,14 @@ +## Introduction + +This package contains definitions of micro-agents. A micro-agent is defined +in the following structure: + +``` +[AgentName] +├── agent.yaml +└── prompt.md +``` + +Note that `prompt.md` could use jinja2 template syntax. During runtime, `prompt.md` +is loaded and rendered, and used together with `agent.yaml` to initialize a +micro-agent. diff --git a/agenthub/micro/coder/prompt.md b/agenthub/micro/coder/prompt.md index bfdb3d8706..3e6028c944 100644 --- a/agenthub/micro/coder/prompt.md +++ b/agenthub/micro/coder/prompt.md @@ -4,9 +4,11 @@ need to modify to complete this task: {{ state.plan.main_goal }} +{% if state.inputs.summary %} Here's a summary of the codebase, as it relates to this task: {{ state.inputs.summary }} +{% endif %} ## Available Actions {{ instructions.actions.run }} diff --git a/tests/unit/test_micro_agents.py b/tests/unit/test_micro_agents.py new file mode 100644 index 0000000000..d08dfbc850 --- /dev/null +++ b/tests/unit/test_micro_agents.py @@ -0,0 +1,74 @@ +import json +import os +from unittest.mock import MagicMock + +import yaml + +from agenthub.micro.registry import all_microagents +from opendevin.agent import Agent +from opendevin.plan import Plan +from opendevin.state import State + + +def test_all_agents_are_loaded(): + full_path = os.path.join('agenthub', 'micro') + agent_names = set() + for root, _, files in os.walk(full_path): + for file in files: + if file == 'agent.yaml': + file_path = os.path.join(root, file) + with open(file_path, 'r') as yaml_file: + data = yaml.safe_load(yaml_file) + agent_names.add(data['name']) + assert agent_names == set(all_microagents.keys()) + + +def test_coder_agent_with_summary(): + """ + Coder agent should render code summary as part of prompt + """ + mock_llm = MagicMock() + content = json.dumps({'action': 'finish', 'args': {}}) + mock_llm.completion.return_value = { + 'choices': [{'message': {'content': content}}] + } + + coder_agent = Agent.get_cls('CoderAgent')(llm=mock_llm) + assert coder_agent is not None + task = 'This is a dummy task' + plan = Plan(task) + summary = 'This is a dummy summary about this repo' + state = State(plan, inputs={'summary': summary}) + coder_agent.step(state) + + mock_llm.completion.assert_called_once() + _, kwargs = mock_llm.completion.call_args + prompt = kwargs['messages'][0]['content'] + assert task in prompt + assert "Here's a summary of the codebase, as it relates to this task" in prompt + assert summary in prompt + + +def test_coder_agent_without_summary(): + """ + When there's no codebase_summary available, there shouldn't be any prompt + about 'code summary' + """ + mock_llm = MagicMock() + content = json.dumps({'action': 'finish', 'args': {}}) + mock_llm.completion.return_value = { + 'choices': [{'message': {'content': content}}] + } + + coder_agent = Agent.get_cls('CoderAgent')(llm=mock_llm) + assert coder_agent is not None + task = 'This is a dummy task' + plan = Plan(task) + state = State(plan) + coder_agent.step(state) + + mock_llm.completion.assert_called_once() + _, kwargs = mock_llm.completion.call_args + prompt = kwargs['messages'][0]['content'] + assert task in prompt + assert "Here's a summary of the codebase, as it relates to this task" not in prompt