2024-11-15 10:12:45 -08:00
|
|
|
import asyncio
|
2025-04-25 15:57:36 -07:00
|
|
|
from typing import Sequence
|
2024-11-15 10:12:45 -08:00
|
|
|
|
2024-10-09 09:26:13 -07:00
|
|
|
import pytest
|
2024-11-23 22:07:21 +07:00
|
|
|
from autogen_agentchat.base import TerminatedException
|
2024-12-03 15:24:25 -08:00
|
|
|
from autogen_agentchat.conditions import (
|
2024-11-21 03:25:53 -05:00
|
|
|
ExternalTermination,
|
2025-04-25 15:57:36 -07:00
|
|
|
FunctionalTermination,
|
2025-03-03 19:26:47 -08:00
|
|
|
FunctionCallTermination,
|
2024-11-10 21:38:52 -08:00
|
|
|
HandoffTermination,
|
2024-11-01 15:01:43 -07:00
|
|
|
MaxMessageTermination,
|
2024-11-25 10:09:06 -08:00
|
|
|
SourceMatchTermination,
|
2024-11-01 15:01:43 -07:00
|
|
|
StopMessageTermination,
|
|
|
|
TextMentionTermination,
|
2025-03-03 18:29:25 -05:00
|
|
|
TextMessageTermination,
|
2024-11-15 10:12:45 -08:00
|
|
|
TimeoutTermination,
|
2024-11-01 15:01:43 -07:00
|
|
|
TokenUsageTermination,
|
|
|
|
)
|
2025-03-03 19:26:47 -08:00
|
|
|
from autogen_agentchat.messages import (
|
2025-04-25 15:57:36 -07:00
|
|
|
BaseAgentEvent,
|
|
|
|
BaseChatMessage,
|
2025-03-03 19:26:47 -08:00
|
|
|
HandoffMessage,
|
|
|
|
StopMessage,
|
2025-04-25 15:57:36 -07:00
|
|
|
StructuredMessage,
|
2025-03-03 19:26:47 -08:00
|
|
|
TextMessage,
|
|
|
|
ToolCallExecutionEvent,
|
|
|
|
UserInputRequestedEvent,
|
|
|
|
)
|
|
|
|
from autogen_core.models import FunctionExecutionResult, RequestUsage
|
2025-04-25 15:57:36 -07:00
|
|
|
from pydantic import BaseModel
|
2024-10-09 09:26:13 -07:00
|
|
|
|
|
|
|
|
2024-11-10 21:38:52 -08:00
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_handoff_termination() -> None:
|
|
|
|
termination = HandoffTermination("target")
|
|
|
|
assert await termination([]) is None
|
|
|
|
await termination.reset()
|
|
|
|
assert await termination([TextMessage(content="Hello", source="user")]) is None
|
|
|
|
await termination.reset()
|
|
|
|
assert await termination([HandoffMessage(target="target", source="user", content="Hello")]) is not None
|
|
|
|
assert termination.terminated
|
|
|
|
await termination.reset()
|
|
|
|
assert await termination([HandoffMessage(target="another", source="user", content="Hello")]) is None
|
|
|
|
assert not termination.terminated
|
|
|
|
await termination.reset()
|
|
|
|
assert (
|
|
|
|
await termination(
|
|
|
|
[
|
|
|
|
TextMessage(content="Hello", source="user"),
|
|
|
|
HandoffMessage(target="target", source="user", content="Hello"),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
is not None
|
|
|
|
)
|
|
|
|
assert termination.terminated
|
|
|
|
await termination.reset()
|
|
|
|
|
|
|
|
|
2024-10-09 09:26:13 -07:00
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_stop_message_termination() -> None:
|
|
|
|
termination = StopMessageTermination()
|
|
|
|
assert await termination([]) is None
|
|
|
|
await termination.reset()
|
|
|
|
assert await termination([TextMessage(content="Hello", source="user")]) is None
|
|
|
|
await termination.reset()
|
|
|
|
assert await termination([StopMessage(content="Stop", source="user")]) is not None
|
|
|
|
await termination.reset()
|
|
|
|
assert (
|
|
|
|
await termination([TextMessage(content="Hello", source="user"), TextMessage(content="World", source="agent")])
|
|
|
|
is None
|
|
|
|
)
|
|
|
|
await termination.reset()
|
|
|
|
assert (
|
|
|
|
await termination([TextMessage(content="Hello", source="user"), StopMessage(content="Stop", source="user")])
|
|
|
|
is not None
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2025-03-03 18:29:25 -05:00
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_text_message_termination() -> None:
|
|
|
|
termination = TextMessageTermination()
|
|
|
|
assert await termination([]) is None
|
|
|
|
await termination.reset()
|
|
|
|
assert await termination([StopMessage(content="Hello", source="user")]) is None
|
|
|
|
await termination.reset()
|
|
|
|
assert await termination([TextMessage(content="Hello", source="user")]) is not None
|
|
|
|
assert termination.terminated
|
|
|
|
await termination.reset()
|
|
|
|
assert (
|
|
|
|
await termination([StopMessage(content="Hello", source="user"), TextMessage(content="World", source="agent")])
|
|
|
|
is not None
|
|
|
|
)
|
|
|
|
assert termination.terminated
|
|
|
|
with pytest.raises(TerminatedException):
|
|
|
|
await termination([TextMessage(content="Hello", source="user")])
|
|
|
|
|
|
|
|
termination = TextMessageTermination(source="user")
|
|
|
|
assert await termination([]) is None
|
|
|
|
await termination.reset()
|
|
|
|
assert await termination([TextMessage(content="Hello", source="user")]) is not None
|
|
|
|
assert termination.terminated
|
|
|
|
await termination.reset()
|
|
|
|
|
|
|
|
termination = TextMessageTermination(source="agent")
|
|
|
|
assert await termination([]) is None
|
|
|
|
await termination.reset()
|
|
|
|
assert await termination([TextMessage(content="Hello", source="user")]) is None
|
|
|
|
await termination.reset()
|
|
|
|
assert await termination([TextMessage(content="Hello", source="agent")]) is not None
|
|
|
|
assert termination.terminated
|
|
|
|
|
|
|
|
|
2024-10-09 09:26:13 -07:00
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_max_message_termination() -> None:
|
|
|
|
termination = MaxMessageTermination(2)
|
|
|
|
assert await termination([]) is None
|
|
|
|
await termination.reset()
|
|
|
|
assert await termination([TextMessage(content="Hello", source="user")]) is None
|
|
|
|
await termination.reset()
|
|
|
|
assert (
|
|
|
|
await termination([TextMessage(content="Hello", source="user"), TextMessage(content="World", source="agent")])
|
|
|
|
is not None
|
|
|
|
)
|
|
|
|
|
2025-02-07 12:52:08 -08:00
|
|
|
termination = MaxMessageTermination(2, include_agent_event=True)
|
|
|
|
assert await termination([]) is None
|
|
|
|
await termination.reset()
|
|
|
|
assert await termination([TextMessage(content="Hello", source="user")]) is None
|
|
|
|
await termination.reset()
|
|
|
|
assert (
|
|
|
|
await termination(
|
|
|
|
[TextMessage(content="Hello", source="user"), UserInputRequestedEvent(request_id="1", source="agent")]
|
|
|
|
)
|
|
|
|
is not None
|
|
|
|
)
|
|
|
|
|
2024-10-09 09:26:13 -07:00
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_mention_termination() -> None:
|
|
|
|
termination = TextMentionTermination("stop")
|
|
|
|
assert await termination([]) is None
|
|
|
|
await termination.reset()
|
|
|
|
assert await termination([TextMessage(content="Hello", source="user")]) is None
|
|
|
|
await termination.reset()
|
|
|
|
assert await termination([TextMessage(content="stop", source="user")]) is not None
|
|
|
|
await termination.reset()
|
|
|
|
assert (
|
|
|
|
await termination([TextMessage(content="Hello", source="user"), TextMessage(content="stop", source="user")])
|
|
|
|
is not None
|
|
|
|
)
|
2025-01-20 08:21:29 +01:00
|
|
|
termination = TextMentionTermination("stop", sources=["agent"])
|
|
|
|
assert await termination([TextMessage(content="stop", source="user")]) is None
|
|
|
|
await termination.reset()
|
|
|
|
assert (
|
|
|
|
await termination([TextMessage(content="stop", source="user"), TextMessage(content="stop", source="agent")])
|
|
|
|
is not None
|
|
|
|
)
|
2024-10-09 09:26:13 -07:00
|
|
|
|
|
|
|
|
2024-11-01 15:01:43 -07:00
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_token_usage_termination() -> None:
|
|
|
|
termination = TokenUsageTermination(max_total_token=10)
|
|
|
|
assert await termination([]) is None
|
|
|
|
await termination.reset()
|
|
|
|
assert (
|
|
|
|
await termination(
|
|
|
|
[
|
|
|
|
TextMessage(
|
2024-11-04 09:25:53 -08:00
|
|
|
content="Hello", source="user", models_usage=RequestUsage(prompt_tokens=10, completion_tokens=10)
|
2024-11-01 15:01:43 -07:00
|
|
|
)
|
|
|
|
]
|
|
|
|
)
|
|
|
|
is not None
|
|
|
|
)
|
|
|
|
await termination.reset()
|
|
|
|
assert (
|
|
|
|
await termination(
|
|
|
|
[
|
|
|
|
TextMessage(
|
2024-11-04 09:25:53 -08:00
|
|
|
content="Hello", source="user", models_usage=RequestUsage(prompt_tokens=1, completion_tokens=1)
|
2024-11-01 15:01:43 -07:00
|
|
|
),
|
|
|
|
TextMessage(
|
2024-11-04 09:25:53 -08:00
|
|
|
content="World", source="agent", models_usage=RequestUsage(prompt_tokens=1, completion_tokens=1)
|
2024-11-01 15:01:43 -07:00
|
|
|
),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
is None
|
|
|
|
)
|
|
|
|
await termination.reset()
|
|
|
|
assert (
|
|
|
|
await termination(
|
|
|
|
[
|
|
|
|
TextMessage(
|
2024-11-04 09:25:53 -08:00
|
|
|
content="Hello", source="user", models_usage=RequestUsage(prompt_tokens=5, completion_tokens=0)
|
2024-11-01 15:01:43 -07:00
|
|
|
),
|
|
|
|
TextMessage(
|
2024-11-04 09:25:53 -08:00
|
|
|
content="stop", source="user", models_usage=RequestUsage(prompt_tokens=0, completion_tokens=5)
|
2024-11-01 15:01:43 -07:00
|
|
|
),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
is not None
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2024-10-09 09:26:13 -07:00
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_and_termination() -> None:
|
|
|
|
termination = MaxMessageTermination(2) & TextMentionTermination("stop")
|
|
|
|
assert await termination([]) is None
|
|
|
|
await termination.reset()
|
|
|
|
assert await termination([TextMessage(content="Hello", source="user")]) is None
|
|
|
|
await termination.reset()
|
|
|
|
assert (
|
|
|
|
await termination([TextMessage(content="Hello", source="user"), TextMessage(content="World", source="agent")])
|
|
|
|
is None
|
|
|
|
)
|
|
|
|
await termination.reset()
|
|
|
|
assert (
|
|
|
|
await termination([TextMessage(content="Hello", source="user"), TextMessage(content="stop", source="user")])
|
|
|
|
is not None
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_or_termination() -> None:
|
|
|
|
termination = MaxMessageTermination(3) | TextMentionTermination("stop")
|
|
|
|
assert await termination([]) is None
|
|
|
|
await termination.reset()
|
|
|
|
assert await termination([TextMessage(content="Hello", source="user")]) is None
|
|
|
|
await termination.reset()
|
|
|
|
assert (
|
|
|
|
await termination([TextMessage(content="Hello", source="user"), TextMessage(content="World", source="agent")])
|
|
|
|
is None
|
|
|
|
)
|
|
|
|
await termination.reset()
|
|
|
|
assert (
|
|
|
|
await termination([TextMessage(content="Hello", source="user"), TextMessage(content="stop", source="user")])
|
|
|
|
is not None
|
|
|
|
)
|
|
|
|
await termination.reset()
|
|
|
|
assert (
|
|
|
|
await termination([TextMessage(content="Hello", source="user"), TextMessage(content="Hello", source="user")])
|
|
|
|
is None
|
|
|
|
)
|
|
|
|
await termination.reset()
|
|
|
|
assert (
|
|
|
|
await termination(
|
|
|
|
[
|
|
|
|
TextMessage(content="Hello", source="user"),
|
|
|
|
TextMessage(content="Hello", source="user"),
|
|
|
|
TextMessage(content="Hello", source="user"),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
is not None
|
|
|
|
)
|
|
|
|
await termination.reset()
|
|
|
|
assert (
|
|
|
|
await termination(
|
|
|
|
[
|
|
|
|
TextMessage(content="Hello", source="user"),
|
|
|
|
TextMessage(content="Hello", source="user"),
|
|
|
|
TextMessage(content="stop", source="user"),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
is not None
|
|
|
|
)
|
|
|
|
await termination.reset()
|
|
|
|
assert (
|
|
|
|
await termination(
|
|
|
|
[
|
|
|
|
TextMessage(content="Hello", source="user"),
|
|
|
|
TextMessage(content="Hello", source="user"),
|
|
|
|
TextMessage(content="Hello", source="user"),
|
|
|
|
TextMessage(content="stop", source="user"),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
is not None
|
|
|
|
)
|
2024-11-15 10:12:45 -08:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_timeout_termination() -> None:
|
|
|
|
termination = TimeoutTermination(0.1) # 100ms timeout
|
|
|
|
|
|
|
|
assert await termination([]) is None
|
|
|
|
assert not termination.terminated
|
|
|
|
|
|
|
|
await asyncio.sleep(0.2)
|
|
|
|
|
|
|
|
assert await termination([]) is not None
|
|
|
|
assert termination.terminated
|
|
|
|
|
|
|
|
await termination.reset()
|
|
|
|
assert not termination.terminated
|
|
|
|
assert await termination([]) is None
|
|
|
|
|
|
|
|
assert await termination([TextMessage(content="Hello", source="user")]) is None
|
|
|
|
await asyncio.sleep(0.2)
|
|
|
|
assert await termination([TextMessage(content="World", source="user")]) is not None
|
2024-11-21 03:25:53 -05:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_external_termination() -> None:
|
|
|
|
termination = ExternalTermination()
|
|
|
|
|
|
|
|
assert await termination([]) is None
|
|
|
|
assert not termination.terminated
|
|
|
|
|
|
|
|
termination.set()
|
|
|
|
assert await termination([]) is not None
|
|
|
|
assert termination.terminated
|
|
|
|
|
|
|
|
await termination.reset()
|
|
|
|
assert await termination([]) is None
|
2024-11-23 22:07:21 +07:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_source_match_termination() -> None:
|
|
|
|
termination = SourceMatchTermination(sources=["Assistant"])
|
|
|
|
assert await termination([]) is None
|
|
|
|
|
|
|
|
continue_messages = [TextMessage(content="Hello", source="agent"), TextMessage(content="Hello", source="user")]
|
|
|
|
assert await termination(continue_messages) is None
|
|
|
|
|
|
|
|
terminate_messages = [
|
|
|
|
TextMessage(content="Hello", source="agent"),
|
|
|
|
TextMessage(content="Hello", source="Assistant"),
|
|
|
|
TextMessage(content="Hello", source="user"),
|
|
|
|
]
|
|
|
|
result = await termination(terminate_messages)
|
|
|
|
assert isinstance(result, StopMessage)
|
|
|
|
assert termination.terminated
|
|
|
|
|
|
|
|
with pytest.raises(TerminatedException):
|
|
|
|
await termination([])
|
|
|
|
await termination.reset()
|
|
|
|
assert not termination.terminated
|
2025-03-03 19:26:47 -08:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_function_call_termination() -> None:
|
|
|
|
termination = FunctionCallTermination(function_name="test_function")
|
|
|
|
assert await termination([]) is None
|
|
|
|
await termination.reset()
|
|
|
|
|
|
|
|
assert await termination([TextMessage(content="Hello", source="user")]) is None
|
|
|
|
await termination.reset()
|
|
|
|
|
|
|
|
assert (
|
|
|
|
await termination(
|
|
|
|
[TextMessage(content="Hello", source="user"), ToolCallExecutionEvent(content=[], source="assistant")]
|
|
|
|
)
|
|
|
|
is None
|
|
|
|
)
|
|
|
|
await termination.reset()
|
|
|
|
|
|
|
|
assert (
|
|
|
|
await termination(
|
|
|
|
[
|
|
|
|
TextMessage(content="Hello", source="user"),
|
|
|
|
ToolCallExecutionEvent(
|
|
|
|
content=[FunctionExecutionResult(content="", name="test_function", call_id="")], source="assistant"
|
|
|
|
),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
is not None
|
|
|
|
)
|
|
|
|
assert termination.terminated
|
|
|
|
await termination.reset()
|
|
|
|
|
|
|
|
assert (
|
|
|
|
await termination(
|
|
|
|
[
|
|
|
|
TextMessage(content="Hello", source="user"),
|
|
|
|
ToolCallExecutionEvent(
|
|
|
|
content=[FunctionExecutionResult(content="", name="another_function", call_id="")],
|
|
|
|
source="assistant",
|
|
|
|
),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
is None
|
|
|
|
)
|
|
|
|
assert not termination.terminated
|
|
|
|
await termination.reset()
|
2025-04-25 15:57:36 -07:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_functional_termination() -> None:
|
|
|
|
async def async_termination_func(messages: Sequence[BaseAgentEvent | BaseChatMessage]) -> bool:
|
|
|
|
if len(messages) < 1:
|
|
|
|
return False
|
|
|
|
if isinstance(messages[-1], TextMessage):
|
|
|
|
return messages[-1].content == "stop"
|
|
|
|
return False
|
|
|
|
|
|
|
|
termination = FunctionalTermination(async_termination_func)
|
|
|
|
assert await termination([]) is None
|
|
|
|
await termination.reset()
|
|
|
|
|
|
|
|
assert await termination([TextMessage(content="Hello", source="user")]) is None
|
|
|
|
await termination.reset()
|
|
|
|
|
|
|
|
assert await termination([TextMessage(content="stop", source="user")]) is not None
|
|
|
|
assert termination.terminated
|
|
|
|
await termination.reset()
|
|
|
|
|
|
|
|
assert await termination([TextMessage(content="Hello", source="user")]) is None
|
|
|
|
|
|
|
|
class TestContentType(BaseModel):
|
|
|
|
content: str
|
|
|
|
data: str
|
|
|
|
|
|
|
|
def sync_termination_func(messages: Sequence[BaseAgentEvent | BaseChatMessage]) -> bool:
|
|
|
|
if len(messages) < 1:
|
|
|
|
return False
|
|
|
|
last_message = messages[-1]
|
|
|
|
if isinstance(last_message, StructuredMessage) and isinstance(last_message.content, TestContentType): # type: ignore[reportUnknownMemberType]
|
|
|
|
return last_message.content.data == "stop"
|
|
|
|
return False
|
|
|
|
|
|
|
|
termination = FunctionalTermination(sync_termination_func)
|
|
|
|
assert await termination([]) is None
|
|
|
|
await termination.reset()
|
|
|
|
assert await termination([TextMessage(content="Hello", source="user")]) is None
|
|
|
|
await termination.reset()
|
|
|
|
assert (
|
|
|
|
await termination(
|
|
|
|
[StructuredMessage[TestContentType](content=TestContentType(content="1", data="stop"), source="user")]
|
|
|
|
)
|
|
|
|
is not None
|
|
|
|
)
|
|
|
|
assert termination.terminated
|
|
|
|
await termination.reset()
|
|
|
|
assert await termination([TextMessage(content="Hello", source="user")]) is None
|