You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

test_assistant_agent.py 9.1 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. import asyncio
  2. import json
  3. import logging
  4. from typing import Any, AsyncGenerator, List
  5. import pytest
  6. from autogen_agentchat import EVENT_LOGGER_NAME
  7. from autogen_agentchat.agents import AssistantAgent, Handoff
  8. from autogen_agentchat.base import TaskResult
  9. from autogen_agentchat.logging import FileLogHandler
  10. from autogen_agentchat.messages import (
  11. HandoffMessage,
  12. MultiModalMessage,
  13. TextMessage,
  14. ToolCallMessage,
  15. ToolCallResultMessage,
  16. )
  17. from autogen_core.components import Image
  18. from autogen_core.components.tools import FunctionTool
  19. from autogen_ext.models import OpenAIChatCompletionClient
  20. from openai.resources.chat.completions import AsyncCompletions
  21. from openai.types.chat.chat_completion import ChatCompletion, Choice
  22. from openai.types.chat.chat_completion_chunk import ChatCompletionChunk
  23. from openai.types.chat.chat_completion_message import ChatCompletionMessage
  24. from openai.types.chat.chat_completion_message_tool_call import ChatCompletionMessageToolCall, Function
  25. from openai.types.completion_usage import CompletionUsage
  26. logger = logging.getLogger(EVENT_LOGGER_NAME)
  27. logger.setLevel(logging.DEBUG)
  28. logger.addHandler(FileLogHandler("test_assistant_agent.log"))
  29. class _MockChatCompletion:
  30. def __init__(self, chat_completions: List[ChatCompletion]) -> None:
  31. self._saved_chat_completions = chat_completions
  32. self._curr_index = 0
  33. async def mock_create(
  34. self, *args: Any, **kwargs: Any
  35. ) -> ChatCompletion | AsyncGenerator[ChatCompletionChunk, None]:
  36. await asyncio.sleep(0.1)
  37. completion = self._saved_chat_completions[self._curr_index]
  38. self._curr_index += 1
  39. return completion
  40. def _pass_function(input: str) -> str:
  41. return "pass"
  42. async def _fail_function(input: str) -> str:
  43. return "fail"
  44. async def _echo_function(input: str) -> str:
  45. return input
  46. @pytest.mark.asyncio
  47. async def test_run_with_tools(monkeypatch: pytest.MonkeyPatch) -> None:
  48. model = "gpt-4o-2024-05-13"
  49. chat_completions = [
  50. ChatCompletion(
  51. id="id1",
  52. choices=[
  53. Choice(
  54. finish_reason="tool_calls",
  55. index=0,
  56. message=ChatCompletionMessage(
  57. content=None,
  58. tool_calls=[
  59. ChatCompletionMessageToolCall(
  60. id="1",
  61. type="function",
  62. function=Function(
  63. name="_pass_function",
  64. arguments=json.dumps({"input": "task"}),
  65. ),
  66. )
  67. ],
  68. role="assistant",
  69. ),
  70. )
  71. ],
  72. created=0,
  73. model=model,
  74. object="chat.completion",
  75. usage=CompletionUsage(prompt_tokens=10, completion_tokens=5, total_tokens=0),
  76. ),
  77. ChatCompletion(
  78. id="id2",
  79. choices=[
  80. Choice(finish_reason="stop", index=0, message=ChatCompletionMessage(content="Hello", role="assistant"))
  81. ],
  82. created=0,
  83. model=model,
  84. object="chat.completion",
  85. usage=CompletionUsage(prompt_tokens=10, completion_tokens=5, total_tokens=0),
  86. ),
  87. ChatCompletion(
  88. id="id2",
  89. choices=[
  90. Choice(
  91. finish_reason="stop", index=0, message=ChatCompletionMessage(content="TERMINATE", role="assistant")
  92. )
  93. ],
  94. created=0,
  95. model=model,
  96. object="chat.completion",
  97. usage=CompletionUsage(prompt_tokens=10, completion_tokens=5, total_tokens=0),
  98. ),
  99. ]
  100. mock = _MockChatCompletion(chat_completions)
  101. monkeypatch.setattr(AsyncCompletions, "create", mock.mock_create)
  102. tool_use_agent = AssistantAgent(
  103. "tool_use_agent",
  104. model_client=OpenAIChatCompletionClient(model=model, api_key=""),
  105. tools=[_pass_function, _fail_function, FunctionTool(_echo_function, description="Echo")],
  106. )
  107. result = await tool_use_agent.run(task="task")
  108. assert len(result.messages) == 4
  109. assert isinstance(result.messages[0], TextMessage)
  110. assert result.messages[0].models_usage is None
  111. assert isinstance(result.messages[1], ToolCallMessage)
  112. assert result.messages[1].models_usage is not None
  113. assert result.messages[1].models_usage.completion_tokens == 5
  114. assert result.messages[1].models_usage.prompt_tokens == 10
  115. assert isinstance(result.messages[2], ToolCallResultMessage)
  116. assert result.messages[2].models_usage is None
  117. assert isinstance(result.messages[3], TextMessage)
  118. assert result.messages[3].models_usage is not None
  119. assert result.messages[3].models_usage.completion_tokens == 5
  120. assert result.messages[3].models_usage.prompt_tokens == 10
  121. # Test streaming.
  122. mock._curr_index = 0 # pyright: ignore
  123. index = 0
  124. async for message in tool_use_agent.run_stream(task="task"):
  125. if isinstance(message, TaskResult):
  126. assert message == result
  127. else:
  128. assert message == result.messages[index]
  129. index += 1
  130. @pytest.mark.asyncio
  131. async def test_handoffs(monkeypatch: pytest.MonkeyPatch) -> None:
  132. handoff = Handoff(target="agent2")
  133. model = "gpt-4o-2024-05-13"
  134. chat_completions = [
  135. ChatCompletion(
  136. id="id1",
  137. choices=[
  138. Choice(
  139. finish_reason="tool_calls",
  140. index=0,
  141. message=ChatCompletionMessage(
  142. content=None,
  143. tool_calls=[
  144. ChatCompletionMessageToolCall(
  145. id="1",
  146. type="function",
  147. function=Function(
  148. name=handoff.name,
  149. arguments=json.dumps({}),
  150. ),
  151. )
  152. ],
  153. role="assistant",
  154. ),
  155. )
  156. ],
  157. created=0,
  158. model=model,
  159. object="chat.completion",
  160. usage=CompletionUsage(prompt_tokens=42, completion_tokens=43, total_tokens=85),
  161. ),
  162. ]
  163. mock = _MockChatCompletion(chat_completions)
  164. monkeypatch.setattr(AsyncCompletions, "create", mock.mock_create)
  165. tool_use_agent = AssistantAgent(
  166. "tool_use_agent",
  167. model_client=OpenAIChatCompletionClient(model=model, api_key=""),
  168. tools=[_pass_function, _fail_function, FunctionTool(_echo_function, description="Echo")],
  169. handoffs=[handoff],
  170. )
  171. assert HandoffMessage in tool_use_agent.produced_message_types
  172. result = await tool_use_agent.run(task="task")
  173. assert len(result.messages) == 4
  174. assert isinstance(result.messages[0], TextMessage)
  175. assert result.messages[0].models_usage is None
  176. assert isinstance(result.messages[1], ToolCallMessage)
  177. assert result.messages[1].models_usage is not None
  178. assert result.messages[1].models_usage.completion_tokens == 43
  179. assert result.messages[1].models_usage.prompt_tokens == 42
  180. assert isinstance(result.messages[2], ToolCallResultMessage)
  181. assert result.messages[2].models_usage is None
  182. assert isinstance(result.messages[3], HandoffMessage)
  183. assert result.messages[3].content == handoff.message
  184. assert result.messages[3].target == handoff.target
  185. assert result.messages[3].models_usage is None
  186. # Test streaming.
  187. mock._curr_index = 0 # pyright: ignore
  188. index = 0
  189. async for message in tool_use_agent.run_stream(task="task"):
  190. if isinstance(message, TaskResult):
  191. assert message == result
  192. else:
  193. assert message == result.messages[index]
  194. index += 1
  195. @pytest.mark.asyncio
  196. async def test_multi_modal_task(monkeypatch: pytest.MonkeyPatch) -> None:
  197. model = "gpt-4o-2024-05-13"
  198. chat_completions = [
  199. ChatCompletion(
  200. id="id2",
  201. choices=[
  202. Choice(finish_reason="stop", index=0, message=ChatCompletionMessage(content="Hello", role="assistant"))
  203. ],
  204. created=0,
  205. model=model,
  206. object="chat.completion",
  207. usage=CompletionUsage(prompt_tokens=10, completion_tokens=5, total_tokens=0),
  208. ),
  209. ]
  210. mock = _MockChatCompletion(chat_completions)
  211. monkeypatch.setattr(AsyncCompletions, "create", mock.mock_create)
  212. agent = AssistantAgent(name="assistant", model_client=OpenAIChatCompletionClient(model=model, api_key=""))
  213. # Generate a random base64 image.
  214. img_base64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4//8/AAX+Av4N70a4AAAAAElFTkSuQmCC"
  215. result = await agent.run(task=MultiModalMessage(source="user", content=["Test", Image.from_base64(img_base64)]))
  216. assert len(result.messages) == 2