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_embedded_ipython_code_executor.py 8.6 kB

Code executors (#1405) * code executor * test * revert to main conversable agent * prepare for pr * kernel * run open ai tests only when it's out of draft status * update workflow file * revert workflow changes * ipython executor * check kernel installed; fix tests * fix tests * fix tests * update system prompt * Update notebook, more tests * notebook * raise instead of return None * allow user provided code executor. * fixing types * wip * refactoring * polishing * fixed failing tests * resolved merge conflict * fixing failing test * wip * local command line executor and embedded ipython executor * revert notebook * fix format * fix merged error * fix lmm test * fix lmm test * move warning * name and description should be part of the agent protocol, reset is not as it is only used for ConversableAgent; removing accidentally commited file * version for dependency * Update autogen/agentchat/conversable_agent.py Co-authored-by: Jack Gerrits <jackgerrits@users.noreply.github.com> * ordering of protocol * description * fix tests * make ipython executor dependency optional * update document optional dependencies * Remove exclude from Agent protocol * Make ConversableAgent consistent with Agent * fix tests * add doc string * add doc string * fix notebook * fix interface * merge and update agents * disable config usage in reply function * description field setter * customize system message update * update doc --------- Co-authored-by: Davor Runje <davor@airt.ai> Co-authored-by: Jack Gerrits <jackgerrits@users.noreply.github.com> Co-authored-by: Aaron <aaronlaptop12@hotmail.com> Co-authored-by: Chi Wang <wang.chi@microsoft.com>
2 years ago
Code executors (#1405) * code executor * test * revert to main conversable agent * prepare for pr * kernel * run open ai tests only when it's out of draft status * update workflow file * revert workflow changes * ipython executor * check kernel installed; fix tests * fix tests * fix tests * update system prompt * Update notebook, more tests * notebook * raise instead of return None * allow user provided code executor. * fixing types * wip * refactoring * polishing * fixed failing tests * resolved merge conflict * fixing failing test * wip * local command line executor and embedded ipython executor * revert notebook * fix format * fix merged error * fix lmm test * fix lmm test * move warning * name and description should be part of the agent protocol, reset is not as it is only used for ConversableAgent; removing accidentally commited file * version for dependency * Update autogen/agentchat/conversable_agent.py Co-authored-by: Jack Gerrits <jackgerrits@users.noreply.github.com> * ordering of protocol * description * fix tests * make ipython executor dependency optional * update document optional dependencies * Remove exclude from Agent protocol * Make ConversableAgent consistent with Agent * fix tests * add doc string * add doc string * fix notebook * fix interface * merge and update agents * disable config usage in reply function * description field setter * customize system message update * update doc --------- Co-authored-by: Davor Runje <davor@airt.ai> Co-authored-by: Jack Gerrits <jackgerrits@users.noreply.github.com> Co-authored-by: Aaron <aaronlaptop12@hotmail.com> Co-authored-by: Chi Wang <wang.chi@microsoft.com>
2 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. import os
  2. import tempfile
  3. from typing import Dict, Union
  4. import uuid
  5. import pytest
  6. from autogen.agentchat.conversable_agent import ConversableAgent
  7. from autogen.coding.base import CodeBlock, CodeExecutor
  8. from autogen.coding.factory import CodeExecutorFactory
  9. from autogen.oai.openai_utils import config_list_from_json
  10. from conftest import skip_openai # noqa: E402
  11. try:
  12. from autogen.coding.embedded_ipython_code_executor import EmbeddedIPythonCodeExecutor
  13. skip = False
  14. skip_reason = ""
  15. except ImportError:
  16. skip = True
  17. skip_reason = "Dependencies for EmbeddedIPythonCodeExecutor not installed."
  18. @pytest.mark.skipif(skip, reason=skip_reason)
  19. def test_create() -> None:
  20. config: Dict[str, Union[str, CodeExecutor]] = {"executor": "ipython-embedded"}
  21. executor = CodeExecutorFactory.create(config)
  22. assert isinstance(executor, EmbeddedIPythonCodeExecutor)
  23. config = {"executor": EmbeddedIPythonCodeExecutor()}
  24. executor = CodeExecutorFactory.create(config)
  25. assert executor is config["executor"]
  26. @pytest.mark.skipif(skip, reason=skip_reason)
  27. def test_init() -> None:
  28. executor = EmbeddedIPythonCodeExecutor(timeout=10, kernel_name="python3", output_dir=".")
  29. assert executor.timeout == 10 and executor.kernel_name == "python3" and executor.output_dir == "."
  30. # Try invalid output directory.
  31. with pytest.raises(ValueError, match="Output directory .* does not exist."):
  32. executor = EmbeddedIPythonCodeExecutor(timeout=111, kernel_name="python3", output_dir="/invalid/directory")
  33. # Try invalid kernel name.
  34. with pytest.raises(ValueError, match="Kernel .* is not installed."):
  35. executor = EmbeddedIPythonCodeExecutor(timeout=111, kernel_name="invalid_kernel_name", output_dir=".")
  36. @pytest.mark.skipif(skip, reason=skip_reason)
  37. def test_execute_code_single_code_block() -> None:
  38. executor = EmbeddedIPythonCodeExecutor()
  39. code_blocks = [CodeBlock(code="import sys\nprint('hello world!')", language="python")]
  40. code_result = executor.execute_code_blocks(code_blocks)
  41. assert code_result.exit_code == 0 and "hello world!" in code_result.output
  42. @pytest.mark.skipif(skip, reason=skip_reason)
  43. def test_execute_code_multiple_code_blocks() -> None:
  44. executor = EmbeddedIPythonCodeExecutor()
  45. code_blocks = [
  46. CodeBlock(code="import sys\na = 123 + 123\n", language="python"),
  47. CodeBlock(code="print(a)", language="python"),
  48. ]
  49. code_result = executor.execute_code_blocks(code_blocks)
  50. assert code_result.exit_code == 0 and "246" in code_result.output
  51. msg = """
  52. def test_function(a, b):
  53. return a + b
  54. """
  55. code_blocks = [
  56. CodeBlock(code=msg, language="python"),
  57. CodeBlock(code="test_function(431, 423)", language="python"),
  58. ]
  59. code_result = executor.execute_code_blocks(code_blocks)
  60. assert code_result.exit_code == 0 and "854" in code_result.output
  61. @pytest.mark.skipif(skip, reason=skip_reason)
  62. def test_execute_code_bash_script() -> None:
  63. executor = EmbeddedIPythonCodeExecutor()
  64. # Test bash script.
  65. code_blocks = [CodeBlock(code='!echo "hello world!"', language="bash")]
  66. code_result = executor.execute_code_blocks(code_blocks)
  67. assert code_result.exit_code == 0 and "hello world!" in code_result.output
  68. @pytest.mark.skipif(skip, reason=skip_reason)
  69. def test_timeout() -> None:
  70. executor = EmbeddedIPythonCodeExecutor(timeout=1)
  71. code_blocks = [CodeBlock(code="import time; time.sleep(10); print('hello world!')", language="python")]
  72. code_result = executor.execute_code_blocks(code_blocks)
  73. assert code_result.exit_code and "Timeout" in code_result.output
  74. @pytest.mark.skipif(skip, reason=skip_reason)
  75. def test_silent_pip_install() -> None:
  76. executor = EmbeddedIPythonCodeExecutor(timeout=600)
  77. code_blocks = [CodeBlock(code="!pip install matplotlib numpy", language="python")]
  78. code_result = executor.execute_code_blocks(code_blocks)
  79. assert code_result.exit_code == 0 and code_result.output.strip() == ""
  80. none_existing_package = uuid.uuid4().hex
  81. code_blocks = [CodeBlock(code=f"!pip install matplotlib_{none_existing_package}", language="python")]
  82. code_result = executor.execute_code_blocks(code_blocks)
  83. assert code_result.exit_code == 0 and "ERROR: " in code_result.output
  84. @pytest.mark.skipif(skip, reason=skip_reason)
  85. def test_restart() -> None:
  86. executor = EmbeddedIPythonCodeExecutor()
  87. code_blocks = [CodeBlock(code="x = 123", language="python")]
  88. code_result = executor.execute_code_blocks(code_blocks)
  89. assert code_result.exit_code == 0 and code_result.output.strip() == ""
  90. executor.restart()
  91. code_blocks = [CodeBlock(code="print(x)", language="python")]
  92. code_result = executor.execute_code_blocks(code_blocks)
  93. assert code_result.exit_code and "NameError" in code_result.output
  94. @pytest.mark.skipif(skip, reason=skip_reason)
  95. def test_save_image() -> None:
  96. with tempfile.TemporaryDirectory() as temp_dir:
  97. executor = EmbeddedIPythonCodeExecutor(output_dir=temp_dir)
  98. # Install matplotlib.
  99. code_blocks = [CodeBlock(code="!pip install matplotlib", language="python")]
  100. code_result = executor.execute_code_blocks(code_blocks)
  101. assert code_result.exit_code == 0 and code_result.output.strip() == ""
  102. # Test saving image.
  103. code_blocks = [
  104. CodeBlock(code="import matplotlib.pyplot as plt\nplt.plot([1, 2, 3, 4])\nplt.show()", language="python")
  105. ]
  106. code_result = executor.execute_code_blocks(code_blocks)
  107. assert code_result.exit_code == 0
  108. assert os.path.exists(code_result.output_files[0])
  109. assert f"Image data saved to {code_result.output_files[0]}" in code_result.output
  110. @pytest.mark.skipif(skip, reason=skip_reason)
  111. def test_save_html() -> None:
  112. with tempfile.TemporaryDirectory() as temp_dir:
  113. executor = EmbeddedIPythonCodeExecutor(output_dir=temp_dir)
  114. # Test saving html.
  115. code_blocks = [
  116. CodeBlock(code="from IPython.display import HTML\nHTML('<h1>Hello, world!</h1>')", language="python")
  117. ]
  118. code_result = executor.execute_code_blocks(code_blocks)
  119. assert code_result.exit_code == 0
  120. assert os.path.exists(code_result.output_files[0])
  121. assert f"HTML data saved to {code_result.output_files[0]}" in code_result.output
  122. @pytest.mark.skipif(skip, reason=skip_reason)
  123. @pytest.mark.skipif(skip_openai, reason="openai not installed OR requested to skip")
  124. def test_conversable_agent_capability() -> None:
  125. KEY_LOC = "notebook"
  126. OAI_CONFIG_LIST = "OAI_CONFIG_LIST"
  127. config_list = config_list_from_json(
  128. OAI_CONFIG_LIST,
  129. file_location=KEY_LOC,
  130. filter_dict={
  131. "model": {
  132. "gpt-3.5-turbo",
  133. "gpt-35-turbo",
  134. },
  135. },
  136. )
  137. llm_config = {"config_list": config_list}
  138. agent = ConversableAgent(
  139. "coding_agent",
  140. llm_config=llm_config,
  141. code_execution_config=False,
  142. )
  143. executor = EmbeddedIPythonCodeExecutor()
  144. executor.user_capability.add_to_agent(agent)
  145. # Test updated system prompt.
  146. assert executor.DEFAULT_SYSTEM_MESSAGE_UPDATE in agent.system_message
  147. # Test code generation.
  148. reply = agent.generate_reply(
  149. [{"role": "user", "content": "print 'hello world' to the console in a single python code block"}],
  150. sender=ConversableAgent("user", llm_config=False, code_execution_config=False),
  151. )
  152. # Test code extraction.
  153. code_blocks = executor.code_extractor.extract_code_blocks(reply) # type: ignore[arg-type]
  154. assert len(code_blocks) == 1 and code_blocks[0].language == "python"
  155. # Test code execution.
  156. code_result = executor.execute_code_blocks(code_blocks)
  157. assert code_result.exit_code == 0 and "hello world" in code_result.output.lower()
  158. @pytest.mark.skipif(skip, reason=skip_reason)
  159. def test_conversable_agent_code_execution() -> None:
  160. agent = ConversableAgent(
  161. "user_proxy",
  162. llm_config=False,
  163. code_execution_config={"executor": "ipython-embedded"},
  164. )
  165. msg = """
  166. Run this code:
  167. ```python
  168. def test_function(a, b):
  169. return a * b
  170. ```
  171. And then this:
  172. ```python
  173. print(test_function(123, 4))
  174. ```
  175. """
  176. with pytest.MonkeyPatch.context() as mp:
  177. mp.setenv("OPENAI_API_KEY", "mock")
  178. reply = agent.generate_reply(
  179. [{"role": "user", "content": msg}],
  180. sender=ConversableAgent("user", llm_config=False, code_execution_config=False),
  181. )
  182. assert "492" in reply # type: ignore[operator]