Files
semantic-kernel/python/tests/unit/agents/test_kernel_function_termination_strategy.py
T
Evan Mattson ee40e88c13 Python: Introduce Agent group chat functionality (#8184)
### Motivation and Context

In the next phase of the agent work in Python, we are to introduce the
ability to have agents interact with each other. Agent group chat allows
developers to create richer and more dynamic agent interactions.

<!-- Thank you for your contribution to the semantic-kernel repo!
Please help reviewers and future users, providing the following
information:
  1. Why is this change required?
  2. What problem does it solve?
  3. What scenario does it contribute to?
  4. If it fixes an open issue, please link to the issue here.
-->

### Description

This PR introduces the following:
- Agent group chat functionality via an `agent_chat` base class that
forms the foundation for group chats. An `agent_group_chat` class
extends that underlying functionality and provides an entry point to
kick off the group chat either via invoking the chat for the defined
agents, or specifying if a provided agent is to join the existing chat.
- The required channels for the chat completion agent and assistant
agent to properly chat together
- Adds more concept samples and getting started with agent samples to
enrich the onboarding experiencing for those wanting to see the
capability of agents and agent group chat.
- Adds unit test for near 100% coverage
- Updates some existing imports for agents, which try and make the
required import experience more intuitive.
- Introduces assistant agent abstractions for working with files and
vector stores so we don't directly expose the underlying client types.
- Closes #7966 
- Closes #7577

<!-- Describe your changes, the overall approach, the underlying design.
These notes will help understanding how your code works. Thanks! -->

### Contribution Checklist

<!-- Before submitting this PR, please make sure: -->

- [X] The code builds clean without any errors or warnings
- [X] The PR follows the [SK Contribution
Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md)
and the [pre-submission formatting
script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts)
raises no violations
- [X] All unit tests pass, and I have added new tests where possible
- [ ] I didn't break anyone 😄
2024-08-19 18:04:44 +00:00

142 lines
4.6 KiB
Python

# Copyright (c) Microsoft. All rights reserved.
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from semantic_kernel.agents.agent import Agent
from semantic_kernel.agents.channels.agent_channel import AgentChannel
from semantic_kernel.agents.strategies import KernelFunctionTerminationStrategy
from semantic_kernel.contents.chat_message_content import ChatMessageContent
from semantic_kernel.functions.kernel_arguments import KernelArguments
from semantic_kernel.functions.kernel_function import KernelFunction
from semantic_kernel.kernel import Kernel
class MockAgent(Agent):
"""A mock agent for testing purposes."""
def __init__(self, id: str = None, name: str = "Test Agent", description: str = "A test agent"):
args = {
"name": name,
"description": description,
}
if id is not None:
args["id"] = id
super().__init__(**args)
def get_channel_keys(self) -> list[str]:
return ["key1", "key2"]
async def create_channel(self) -> AgentChannel:
return AsyncMock(spec=AgentChannel)
@pytest.mark.asyncio
async def test_should_agent_terminate_with_result_true():
agent = MockAgent(id="test-agent-id")
history = [MagicMock(spec=ChatMessageContent)]
mock_function = AsyncMock(spec=KernelFunction)
mock_function.invoke.return_value = MagicMock(value=True)
mock_kernel = MagicMock(spec=Kernel)
strategy = KernelFunctionTerminationStrategy(
agents=[agent], function=mock_function, kernel=mock_kernel, result_parser=lambda result: result.value
)
result = await strategy.should_agent_terminate(agent, history)
assert result is True
mock_function.invoke.assert_awaited_once()
@pytest.mark.asyncio
async def test_should_agent_terminate_with_result_false():
agent = MockAgent(id="test-agent-id")
history = [MagicMock(spec=ChatMessageContent)]
mock_function = AsyncMock(spec=KernelFunction)
mock_function.invoke.return_value = MagicMock(value=False)
mock_kernel = MagicMock(spec=Kernel)
strategy = KernelFunctionTerminationStrategy(
agents=[agent], function=mock_function, kernel=mock_kernel, result_parser=lambda result: result.value
)
result = await strategy.should_agent_terminate(agent, history)
assert result is False
mock_function.invoke.assert_awaited_once()
@pytest.mark.asyncio
async def test_should_agent_terminate_with_none_result():
agent = MockAgent(id="test-agent-id")
history = [MagicMock(spec=ChatMessageContent)]
mock_function = AsyncMock(spec=KernelFunction)
mock_function.invoke.return_value = None
mock_kernel = MagicMock(spec=Kernel)
strategy = KernelFunctionTerminationStrategy(
agents=[agent],
function=mock_function,
kernel=mock_kernel,
result_parser=lambda result: result.value if result else False,
)
result = await strategy.should_agent_terminate(agent, history)
assert result is False
mock_function.invoke.assert_awaited_once()
@pytest.mark.asyncio
async def test_should_agent_terminate_custom_arguments():
agent = MockAgent(id="test-agent-id")
history = [MagicMock(spec=ChatMessageContent)]
mock_function = AsyncMock(spec=KernelFunction)
mock_function.invoke.return_value = MagicMock(value=True)
mock_kernel = MagicMock(spec=Kernel)
custom_args = KernelArguments(execution_settings={"some_setting": MagicMock(model_dump=lambda: {"key": "value"})})
strategy = KernelFunctionTerminationStrategy(
agents=[agent],
function=mock_function,
kernel=mock_kernel,
arguments=custom_args,
result_parser=lambda result: result.value,
)
with patch.object(KernelArguments, "__init__", return_value=None) as mock_init:
result = await strategy.should_agent_terminate(agent, history)
mock_init.assert_called_once()
assert result is True
mock_function.invoke.assert_awaited_once()
@pytest.mark.asyncio
async def test_should_agent_terminate_result_parser_awaitable():
agent = MockAgent(id="test-agent-id")
history = [MagicMock(spec=ChatMessageContent)]
mock_function = AsyncMock(spec=KernelFunction)
mock_function.invoke.return_value = MagicMock(value=True)
mock_kernel = MagicMock(spec=Kernel)
async def mock_result_parser(result):
return result.value
strategy = KernelFunctionTerminationStrategy(
agents=[agent], function=mock_function, kernel=mock_kernel, result_parser=mock_result_parser
)
result = await strategy.should_agent_terminate(agent, history)
assert result is True
mock_function.invoke.assert_awaited_once()