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 29 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778
  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
  8. from autogen_agentchat.base import Handoff, TaskResult
  9. from autogen_agentchat.messages import (
  10. ChatMessage,
  11. HandoffMessage,
  12. MemoryQueryEvent,
  13. MultiModalMessage,
  14. TextMessage,
  15. ToolCallExecutionEvent,
  16. ToolCallRequestEvent,
  17. ToolCallSummaryMessage,
  18. )
  19. from autogen_core import FunctionCall, Image
  20. from autogen_core.memory import ListMemory, Memory, MemoryContent, MemoryMimeType, MemoryQueryResult
  21. from autogen_core.model_context import BufferedChatCompletionContext
  22. from autogen_core.models import FunctionExecutionResult, LLMMessage
  23. from autogen_core.models._model_client import ModelFamily
  24. from autogen_core.tools import FunctionTool
  25. from autogen_ext.models.openai import OpenAIChatCompletionClient
  26. from openai.resources.chat.completions import AsyncCompletions
  27. from openai.types.chat.chat_completion import ChatCompletion, Choice
  28. from openai.types.chat.chat_completion_chunk import ChatCompletionChunk
  29. from openai.types.chat.chat_completion_message import ChatCompletionMessage
  30. from openai.types.chat.chat_completion_message_tool_call import (
  31. ChatCompletionMessageToolCall,
  32. Function,
  33. )
  34. from openai.types.completion_usage import CompletionUsage
  35. from utils import FileLogHandler
  36. logger = logging.getLogger(EVENT_LOGGER_NAME)
  37. logger.setLevel(logging.DEBUG)
  38. logger.addHandler(FileLogHandler("test_assistant_agent.log"))
  39. class _MockChatCompletion:
  40. def __init__(self, chat_completions: List[ChatCompletion]) -> None:
  41. self._saved_chat_completions = chat_completions
  42. self.curr_index = 0
  43. self.calls: List[List[LLMMessage]] = []
  44. async def mock_create(
  45. self, *args: Any, **kwargs: Any
  46. ) -> ChatCompletion | AsyncGenerator[ChatCompletionChunk, None]:
  47. self.calls.append(kwargs["messages"]) # Save the call
  48. await asyncio.sleep(0.1)
  49. completion = self._saved_chat_completions[self.curr_index]
  50. self.curr_index += 1
  51. return completion
  52. def _pass_function(input: str) -> str:
  53. return "pass"
  54. async def _fail_function(input: str) -> str:
  55. return "fail"
  56. async def _echo_function(input: str) -> str:
  57. return input
  58. @pytest.mark.asyncio
  59. async def test_run_with_tools(monkeypatch: pytest.MonkeyPatch) -> None:
  60. model = "gpt-4o-2024-05-13"
  61. chat_completions = [
  62. ChatCompletion(
  63. id="id1",
  64. choices=[
  65. Choice(
  66. finish_reason="tool_calls",
  67. index=0,
  68. message=ChatCompletionMessage(
  69. content=None,
  70. tool_calls=[
  71. ChatCompletionMessageToolCall(
  72. id="1",
  73. type="function",
  74. function=Function(
  75. name="_pass_function",
  76. arguments=json.dumps({"input": "task"}),
  77. ),
  78. )
  79. ],
  80. role="assistant",
  81. ),
  82. )
  83. ],
  84. created=0,
  85. model=model,
  86. object="chat.completion",
  87. usage=CompletionUsage(prompt_tokens=10, completion_tokens=5, total_tokens=0),
  88. ),
  89. ChatCompletion(
  90. id="id2",
  91. choices=[
  92. Choice(
  93. finish_reason="stop",
  94. index=0,
  95. message=ChatCompletionMessage(content="pass", role="assistant"),
  96. )
  97. ],
  98. created=0,
  99. model=model,
  100. object="chat.completion",
  101. usage=CompletionUsage(prompt_tokens=10, completion_tokens=5, total_tokens=0),
  102. ),
  103. ChatCompletion(
  104. id="id2",
  105. choices=[
  106. Choice(
  107. finish_reason="stop",
  108. index=0,
  109. message=ChatCompletionMessage(content="TERMINATE", role="assistant"),
  110. )
  111. ],
  112. created=0,
  113. model=model,
  114. object="chat.completion",
  115. usage=CompletionUsage(prompt_tokens=10, completion_tokens=5, total_tokens=0),
  116. ),
  117. ]
  118. mock = _MockChatCompletion(chat_completions)
  119. monkeypatch.setattr(AsyncCompletions, "create", mock.mock_create)
  120. agent = AssistantAgent(
  121. "tool_use_agent",
  122. model_client=OpenAIChatCompletionClient(model=model, api_key=""),
  123. tools=[
  124. _pass_function,
  125. _fail_function,
  126. FunctionTool(_echo_function, description="Echo"),
  127. ],
  128. )
  129. result = await agent.run(task="task")
  130. assert len(result.messages) == 4
  131. assert isinstance(result.messages[0], TextMessage)
  132. assert result.messages[0].models_usage is None
  133. assert isinstance(result.messages[1], ToolCallRequestEvent)
  134. assert result.messages[1].models_usage is not None
  135. assert result.messages[1].models_usage.completion_tokens == 5
  136. assert result.messages[1].models_usage.prompt_tokens == 10
  137. assert isinstance(result.messages[2], ToolCallExecutionEvent)
  138. assert result.messages[2].models_usage is None
  139. assert isinstance(result.messages[3], ToolCallSummaryMessage)
  140. assert result.messages[3].content == "pass"
  141. assert result.messages[3].models_usage is None
  142. # Test streaming.
  143. mock.curr_index = 0 # Reset the mock
  144. index = 0
  145. async for message in agent.run_stream(task="task"):
  146. if isinstance(message, TaskResult):
  147. assert message == result
  148. else:
  149. assert message == result.messages[index]
  150. index += 1
  151. # Test state saving and loading.
  152. state = await agent.save_state()
  153. agent2 = AssistantAgent(
  154. "tool_use_agent",
  155. model_client=OpenAIChatCompletionClient(model=model, api_key=""),
  156. tools=[_pass_function, _fail_function, FunctionTool(_echo_function, description="Echo")],
  157. )
  158. await agent2.load_state(state)
  159. state2 = await agent2.save_state()
  160. assert state == state2
  161. @pytest.mark.asyncio
  162. async def test_run_with_tools_and_reflection(monkeypatch: pytest.MonkeyPatch) -> None:
  163. model = "gpt-4o-2024-05-13"
  164. chat_completions = [
  165. ChatCompletion(
  166. id="id1",
  167. choices=[
  168. Choice(
  169. finish_reason="tool_calls",
  170. index=0,
  171. message=ChatCompletionMessage(
  172. content=None,
  173. tool_calls=[
  174. ChatCompletionMessageToolCall(
  175. id="1",
  176. type="function",
  177. function=Function(
  178. name="_pass_function",
  179. arguments=json.dumps({"input": "task"}),
  180. ),
  181. )
  182. ],
  183. role="assistant",
  184. ),
  185. )
  186. ],
  187. created=0,
  188. model=model,
  189. object="chat.completion",
  190. usage=CompletionUsage(prompt_tokens=10, completion_tokens=5, total_tokens=0),
  191. ),
  192. ChatCompletion(
  193. id="id2",
  194. choices=[
  195. Choice(finish_reason="stop", index=0, message=ChatCompletionMessage(content="Hello", role="assistant"))
  196. ],
  197. created=0,
  198. model=model,
  199. object="chat.completion",
  200. usage=CompletionUsage(prompt_tokens=10, completion_tokens=5, total_tokens=0),
  201. ),
  202. ChatCompletion(
  203. id="id2",
  204. choices=[
  205. Choice(
  206. finish_reason="stop", index=0, message=ChatCompletionMessage(content="TERMINATE", role="assistant")
  207. )
  208. ],
  209. created=0,
  210. model=model,
  211. object="chat.completion",
  212. usage=CompletionUsage(prompt_tokens=10, completion_tokens=5, total_tokens=0),
  213. ),
  214. ]
  215. mock = _MockChatCompletion(chat_completions)
  216. monkeypatch.setattr(AsyncCompletions, "create", mock.mock_create)
  217. agent = AssistantAgent(
  218. "tool_use_agent",
  219. model_client=OpenAIChatCompletionClient(model=model, api_key=""),
  220. tools=[_pass_function, _fail_function, FunctionTool(_echo_function, description="Echo")],
  221. reflect_on_tool_use=True,
  222. )
  223. result = await agent.run(task="task")
  224. assert len(result.messages) == 4
  225. assert isinstance(result.messages[0], TextMessage)
  226. assert result.messages[0].models_usage is None
  227. assert isinstance(result.messages[1], ToolCallRequestEvent)
  228. assert result.messages[1].models_usage is not None
  229. assert result.messages[1].models_usage.completion_tokens == 5
  230. assert result.messages[1].models_usage.prompt_tokens == 10
  231. assert isinstance(result.messages[2], ToolCallExecutionEvent)
  232. assert result.messages[2].models_usage is None
  233. assert isinstance(result.messages[3], TextMessage)
  234. assert result.messages[3].content == "Hello"
  235. assert result.messages[3].models_usage is not None
  236. assert result.messages[3].models_usage.completion_tokens == 5
  237. assert result.messages[3].models_usage.prompt_tokens == 10
  238. # Test streaming.
  239. mock.curr_index = 0 # pyright: ignore
  240. index = 0
  241. async for message in agent.run_stream(task="task"):
  242. if isinstance(message, TaskResult):
  243. assert message == result
  244. else:
  245. assert message == result.messages[index]
  246. index += 1
  247. # Test state saving and loading.
  248. state = await agent.save_state()
  249. agent2 = AssistantAgent(
  250. "tool_use_agent",
  251. model_client=OpenAIChatCompletionClient(model=model, api_key=""),
  252. tools=[
  253. _pass_function,
  254. _fail_function,
  255. FunctionTool(_echo_function, description="Echo"),
  256. ],
  257. )
  258. await agent2.load_state(state)
  259. state2 = await agent2.save_state()
  260. assert state == state2
  261. @pytest.mark.asyncio
  262. async def test_run_with_parallel_tools(monkeypatch: pytest.MonkeyPatch) -> None:
  263. model = "gpt-4o-2024-05-13"
  264. chat_completions = [
  265. ChatCompletion(
  266. id="id1",
  267. choices=[
  268. Choice(
  269. finish_reason="tool_calls",
  270. index=0,
  271. message=ChatCompletionMessage(
  272. content=None,
  273. tool_calls=[
  274. ChatCompletionMessageToolCall(
  275. id="1",
  276. type="function",
  277. function=Function(
  278. name="_pass_function",
  279. arguments=json.dumps({"input": "task1"}),
  280. ),
  281. ),
  282. ChatCompletionMessageToolCall(
  283. id="2",
  284. type="function",
  285. function=Function(
  286. name="_pass_function",
  287. arguments=json.dumps({"input": "task2"}),
  288. ),
  289. ),
  290. ChatCompletionMessageToolCall(
  291. id="3",
  292. type="function",
  293. function=Function(
  294. name="_echo_function",
  295. arguments=json.dumps({"input": "task3"}),
  296. ),
  297. ),
  298. ],
  299. role="assistant",
  300. ),
  301. )
  302. ],
  303. created=0,
  304. model=model,
  305. object="chat.completion",
  306. usage=CompletionUsage(prompt_tokens=10, completion_tokens=5, total_tokens=0),
  307. ),
  308. ChatCompletion(
  309. id="id2",
  310. choices=[
  311. Choice(
  312. finish_reason="stop",
  313. index=0,
  314. message=ChatCompletionMessage(content="pass", role="assistant"),
  315. )
  316. ],
  317. created=0,
  318. model=model,
  319. object="chat.completion",
  320. usage=CompletionUsage(prompt_tokens=10, completion_tokens=5, total_tokens=0),
  321. ),
  322. ChatCompletion(
  323. id="id2",
  324. choices=[
  325. Choice(
  326. finish_reason="stop",
  327. index=0,
  328. message=ChatCompletionMessage(content="TERMINATE", role="assistant"),
  329. )
  330. ],
  331. created=0,
  332. model=model,
  333. object="chat.completion",
  334. usage=CompletionUsage(prompt_tokens=10, completion_tokens=5, total_tokens=0),
  335. ),
  336. ]
  337. mock = _MockChatCompletion(chat_completions)
  338. monkeypatch.setattr(AsyncCompletions, "create", mock.mock_create)
  339. agent = AssistantAgent(
  340. "tool_use_agent",
  341. model_client=OpenAIChatCompletionClient(model=model, api_key=""),
  342. tools=[
  343. _pass_function,
  344. _fail_function,
  345. FunctionTool(_echo_function, description="Echo"),
  346. ],
  347. )
  348. result = await agent.run(task="task")
  349. assert len(result.messages) == 4
  350. assert isinstance(result.messages[0], TextMessage)
  351. assert result.messages[0].models_usage is None
  352. assert isinstance(result.messages[1], ToolCallRequestEvent)
  353. assert result.messages[1].content == [
  354. FunctionCall(id="1", arguments=r'{"input": "task1"}', name="_pass_function"),
  355. FunctionCall(id="2", arguments=r'{"input": "task2"}', name="_pass_function"),
  356. FunctionCall(id="3", arguments=r'{"input": "task3"}', name="_echo_function"),
  357. ]
  358. assert result.messages[1].models_usage is not None
  359. assert result.messages[1].models_usage.completion_tokens == 5
  360. assert result.messages[1].models_usage.prompt_tokens == 10
  361. assert isinstance(result.messages[2], ToolCallExecutionEvent)
  362. expected_content = [
  363. FunctionExecutionResult(call_id="1", content="pass"),
  364. FunctionExecutionResult(call_id="2", content="pass"),
  365. FunctionExecutionResult(call_id="3", content="task3"),
  366. ]
  367. for expected in expected_content:
  368. assert expected in result.messages[2].content
  369. assert result.messages[2].models_usage is None
  370. assert isinstance(result.messages[3], ToolCallSummaryMessage)
  371. assert result.messages[3].content == "pass\npass\ntask3"
  372. assert result.messages[3].models_usage is None
  373. # Test streaming.
  374. mock.curr_index = 0 # Reset the mock
  375. index = 0
  376. async for message in agent.run_stream(task="task"):
  377. if isinstance(message, TaskResult):
  378. assert message == result
  379. else:
  380. assert message == result.messages[index]
  381. index += 1
  382. # Test state saving and loading.
  383. state = await agent.save_state()
  384. agent2 = AssistantAgent(
  385. "tool_use_agent",
  386. model_client=OpenAIChatCompletionClient(model=model, api_key=""),
  387. tools=[_pass_function, _fail_function, FunctionTool(_echo_function, description="Echo")],
  388. )
  389. await agent2.load_state(state)
  390. state2 = await agent2.save_state()
  391. assert state == state2
  392. @pytest.mark.asyncio
  393. async def test_handoffs(monkeypatch: pytest.MonkeyPatch) -> None:
  394. handoff = Handoff(target="agent2")
  395. model = "gpt-4o-2024-05-13"
  396. chat_completions = [
  397. ChatCompletion(
  398. id="id1",
  399. choices=[
  400. Choice(
  401. finish_reason="tool_calls",
  402. index=0,
  403. message=ChatCompletionMessage(
  404. content=None,
  405. tool_calls=[
  406. ChatCompletionMessageToolCall(
  407. id="1",
  408. type="function",
  409. function=Function(
  410. name=handoff.name,
  411. arguments=json.dumps({}),
  412. ),
  413. )
  414. ],
  415. role="assistant",
  416. ),
  417. )
  418. ],
  419. created=0,
  420. model=model,
  421. object="chat.completion",
  422. usage=CompletionUsage(prompt_tokens=42, completion_tokens=43, total_tokens=85),
  423. ),
  424. ]
  425. mock = _MockChatCompletion(chat_completions)
  426. monkeypatch.setattr(AsyncCompletions, "create", mock.mock_create)
  427. tool_use_agent = AssistantAgent(
  428. "tool_use_agent",
  429. model_client=OpenAIChatCompletionClient(model=model, api_key=""),
  430. tools=[
  431. _pass_function,
  432. _fail_function,
  433. FunctionTool(_echo_function, description="Echo"),
  434. ],
  435. handoffs=[handoff],
  436. )
  437. assert HandoffMessage in tool_use_agent.produced_message_types
  438. result = await tool_use_agent.run(task="task")
  439. assert len(result.messages) == 4
  440. assert isinstance(result.messages[0], TextMessage)
  441. assert result.messages[0].models_usage is None
  442. assert isinstance(result.messages[1], ToolCallRequestEvent)
  443. assert result.messages[1].models_usage is not None
  444. assert result.messages[1].models_usage.completion_tokens == 43
  445. assert result.messages[1].models_usage.prompt_tokens == 42
  446. assert isinstance(result.messages[2], ToolCallExecutionEvent)
  447. assert result.messages[2].models_usage is None
  448. assert isinstance(result.messages[3], HandoffMessage)
  449. assert result.messages[3].content == handoff.message
  450. assert result.messages[3].target == handoff.target
  451. assert result.messages[3].models_usage is None
  452. # Test streaming.
  453. mock.curr_index = 0 # pyright: ignore
  454. index = 0
  455. async for message in tool_use_agent.run_stream(task="task"):
  456. if isinstance(message, TaskResult):
  457. assert message == result
  458. else:
  459. assert message == result.messages[index]
  460. index += 1
  461. @pytest.mark.asyncio
  462. async def test_multi_modal_task(monkeypatch: pytest.MonkeyPatch) -> None:
  463. model = "gpt-4o-2024-05-13"
  464. chat_completions = [
  465. ChatCompletion(
  466. id="id2",
  467. choices=[
  468. Choice(
  469. finish_reason="stop",
  470. index=0,
  471. message=ChatCompletionMessage(content="Hello", role="assistant"),
  472. )
  473. ],
  474. created=0,
  475. model=model,
  476. object="chat.completion",
  477. usage=CompletionUsage(prompt_tokens=10, completion_tokens=5, total_tokens=0),
  478. ),
  479. ]
  480. mock = _MockChatCompletion(chat_completions)
  481. monkeypatch.setattr(AsyncCompletions, "create", mock.mock_create)
  482. agent = AssistantAgent(
  483. name="assistant",
  484. model_client=OpenAIChatCompletionClient(model=model, api_key=""),
  485. )
  486. # Generate a random base64 image.
  487. img_base64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4//8/AAX+Av4N70a4AAAAAElFTkSuQmCC"
  488. result = await agent.run(task=MultiModalMessage(source="user", content=["Test", Image.from_base64(img_base64)]))
  489. assert len(result.messages) == 2
  490. @pytest.mark.asyncio
  491. async def test_invalid_model_capabilities() -> None:
  492. model = "random-model"
  493. model_client = OpenAIChatCompletionClient(
  494. model=model,
  495. api_key="",
  496. model_info={"vision": False, "function_calling": False, "json_output": False, "family": ModelFamily.UNKNOWN},
  497. )
  498. with pytest.raises(ValueError):
  499. agent = AssistantAgent(
  500. name="assistant",
  501. model_client=model_client,
  502. tools=[
  503. _pass_function,
  504. _fail_function,
  505. FunctionTool(_echo_function, description="Echo"),
  506. ],
  507. )
  508. with pytest.raises(ValueError):
  509. agent = AssistantAgent(name="assistant", model_client=model_client, handoffs=["agent2"])
  510. with pytest.raises(ValueError):
  511. agent = AssistantAgent(name="assistant", model_client=model_client)
  512. # Generate a random base64 image.
  513. img_base64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4//8/AAX+Av4N70a4AAAAAElFTkSuQmCC"
  514. await agent.run(task=MultiModalMessage(source="user", content=["Test", Image.from_base64(img_base64)]))
  515. @pytest.mark.asyncio
  516. async def test_list_chat_messages(monkeypatch: pytest.MonkeyPatch) -> None:
  517. model = "gpt-4o-2024-05-13"
  518. chat_completions = [
  519. ChatCompletion(
  520. id="id1",
  521. choices=[
  522. Choice(
  523. finish_reason="stop",
  524. index=0,
  525. message=ChatCompletionMessage(content="Response to message 1", role="assistant"),
  526. )
  527. ],
  528. created=0,
  529. model=model,
  530. object="chat.completion",
  531. usage=CompletionUsage(prompt_tokens=10, completion_tokens=5, total_tokens=15),
  532. ),
  533. ]
  534. mock = _MockChatCompletion(chat_completions)
  535. monkeypatch.setattr(AsyncCompletions, "create", mock.mock_create)
  536. agent = AssistantAgent(
  537. "test_agent",
  538. model_client=OpenAIChatCompletionClient(model=model, api_key=""),
  539. )
  540. # Create a list of chat messages
  541. messages: List[ChatMessage] = [
  542. TextMessage(content="Message 1", source="user"),
  543. TextMessage(content="Message 2", source="user"),
  544. ]
  545. # Test run method with list of messages
  546. result = await agent.run(task=messages)
  547. assert len(result.messages) == 3 # 2 input messages + 1 response message
  548. assert isinstance(result.messages[0], TextMessage)
  549. assert result.messages[0].content == "Message 1"
  550. assert result.messages[0].source == "user"
  551. assert isinstance(result.messages[1], TextMessage)
  552. assert result.messages[1].content == "Message 2"
  553. assert result.messages[1].source == "user"
  554. assert isinstance(result.messages[2], TextMessage)
  555. assert result.messages[2].content == "Response to message 1"
  556. assert result.messages[2].source == "test_agent"
  557. assert result.messages[2].models_usage is not None
  558. assert result.messages[2].models_usage.completion_tokens == 5
  559. assert result.messages[2].models_usage.prompt_tokens == 10
  560. # Test run_stream method with list of messages
  561. mock.curr_index = 0 # Reset mock index using public attribute
  562. index = 0
  563. async for message in agent.run_stream(task=messages):
  564. if isinstance(message, TaskResult):
  565. assert message == result
  566. else:
  567. assert message == result.messages[index]
  568. index += 1
  569. @pytest.mark.asyncio
  570. async def test_model_context(monkeypatch: pytest.MonkeyPatch) -> None:
  571. model = "gpt-4o-2024-05-13"
  572. chat_completions = [
  573. ChatCompletion(
  574. id="id1",
  575. choices=[
  576. Choice(
  577. finish_reason="stop",
  578. index=0,
  579. message=ChatCompletionMessage(content="Response to message 3", role="assistant"),
  580. )
  581. ],
  582. created=0,
  583. model=model,
  584. object="chat.completion",
  585. usage=CompletionUsage(prompt_tokens=10, completion_tokens=5, total_tokens=15),
  586. ),
  587. ]
  588. mock = _MockChatCompletion(chat_completions)
  589. monkeypatch.setattr(AsyncCompletions, "create", mock.mock_create)
  590. model_context = BufferedChatCompletionContext(buffer_size=2)
  591. agent = AssistantAgent(
  592. "test_agent",
  593. model_client=OpenAIChatCompletionClient(model=model, api_key=""),
  594. model_context=model_context,
  595. )
  596. messages = [
  597. TextMessage(content="Message 1", source="user"),
  598. TextMessage(content="Message 2", source="user"),
  599. TextMessage(content="Message 3", source="user"),
  600. ]
  601. await agent.run(task=messages)
  602. # Check if the mock client is called with only the last two messages.
  603. assert len(mock.calls) == 1
  604. # 2 message from the context + 1 system message
  605. assert len(mock.calls[0]) == 3
  606. @pytest.mark.asyncio
  607. async def test_run_with_memory(monkeypatch: pytest.MonkeyPatch) -> None:
  608. model = "gpt-4o-2024-05-13"
  609. chat_completions = [
  610. ChatCompletion(
  611. id="id1",
  612. choices=[
  613. Choice(
  614. finish_reason="stop",
  615. index=0,
  616. message=ChatCompletionMessage(content="Hello", role="assistant"),
  617. )
  618. ],
  619. created=0,
  620. model=model,
  621. object="chat.completion",
  622. usage=CompletionUsage(prompt_tokens=10, completion_tokens=5, total_tokens=0),
  623. ),
  624. ]
  625. b64_image_str = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4//8/AAX+Av4N70a4AAAAAElFTkSuQmCC"
  626. mock = _MockChatCompletion(chat_completions)
  627. monkeypatch.setattr(AsyncCompletions, "create", mock.mock_create)
  628. # Test basic memory properties and empty context
  629. memory = ListMemory(name="test_memory")
  630. assert memory.name == "test_memory"
  631. empty_context = BufferedChatCompletionContext(buffer_size=2)
  632. empty_results = await memory.update_context(empty_context)
  633. assert len(empty_results.memories.results) == 0
  634. # Test various content types
  635. memory = ListMemory()
  636. await memory.add(MemoryContent(content="text content", mime_type=MemoryMimeType.TEXT))
  637. await memory.add(MemoryContent(content={"key": "value"}, mime_type=MemoryMimeType.JSON))
  638. await memory.add(MemoryContent(content=Image.from_base64(b64_image_str), mime_type=MemoryMimeType.IMAGE))
  639. # Test query functionality
  640. query_result = await memory.query(MemoryContent(content="", mime_type=MemoryMimeType.TEXT))
  641. assert isinstance(query_result, MemoryQueryResult)
  642. # Should have all three memories we added
  643. assert len(query_result.results) == 3
  644. # Test clear and cleanup
  645. await memory.clear()
  646. empty_query = await memory.query(MemoryContent(content="", mime_type=MemoryMimeType.TEXT))
  647. assert len(empty_query.results) == 0
  648. await memory.close() # Should not raise
  649. # Test invalid memory type
  650. with pytest.raises(TypeError):
  651. AssistantAgent(
  652. "test_agent",
  653. model_client=OpenAIChatCompletionClient(model=model, api_key=""),
  654. memory="invalid", # type: ignore
  655. )
  656. # Test with agent
  657. memory2 = ListMemory()
  658. await memory2.add(MemoryContent(content="test instruction", mime_type=MemoryMimeType.TEXT))
  659. agent = AssistantAgent(
  660. "test_agent", model_client=OpenAIChatCompletionClient(model=model, api_key=""), memory=[memory2]
  661. )
  662. result = await agent.run(task="test task")
  663. assert len(result.messages) > 0
  664. memory_event = next((msg for msg in result.messages if isinstance(msg, MemoryQueryEvent)), None)
  665. assert memory_event is not None
  666. assert len(memory_event.content) > 0
  667. assert isinstance(memory_event.content[0], MemoryContent)
  668. # Test memory protocol
  669. class BadMemory:
  670. pass
  671. assert not isinstance(BadMemory(), Memory)
  672. assert isinstance(ListMemory(), Memory)
  673. @pytest.mark.asyncio
  674. async def test_assistant_agent_declarative(monkeypatch: pytest.MonkeyPatch) -> None:
  675. model = "gpt-4o-2024-05-13"
  676. chat_completions = [
  677. ChatCompletion(
  678. id="id1",
  679. choices=[
  680. Choice(
  681. finish_reason="stop",
  682. index=0,
  683. message=ChatCompletionMessage(content="Response to message 3", role="assistant"),
  684. )
  685. ],
  686. created=0,
  687. model=model,
  688. object="chat.completion",
  689. usage=CompletionUsage(prompt_tokens=10, completion_tokens=5, total_tokens=15),
  690. ),
  691. ]
  692. mock = _MockChatCompletion(chat_completions)
  693. monkeypatch.setattr(AsyncCompletions, "create", mock.mock_create)
  694. model_context = BufferedChatCompletionContext(buffer_size=2)
  695. agent = AssistantAgent(
  696. "test_agent",
  697. model_client=OpenAIChatCompletionClient(model=model, api_key=""),
  698. model_context=model_context,
  699. )
  700. agent_config = agent.dump_component()
  701. assert agent_config.provider == "autogen_agentchat.agents.AssistantAgent"
  702. agent2 = AssistantAgent.load_component(agent_config)
  703. assert agent2.name == agent.name
  704. agent3 = AssistantAgent(
  705. "test_agent",
  706. model_client=OpenAIChatCompletionClient(model=model, api_key=""),
  707. model_context=model_context,
  708. tools=[
  709. _pass_function,
  710. _fail_function,
  711. FunctionTool(_echo_function, description="Echo"),
  712. ],
  713. )
  714. with pytest.raises(NotImplementedError):
  715. agent3.dump_component()