Lspinheiro/chore/migrate azure executor autogen ext (#3652)

* migrate code executor and tests

* update extras

* update dependencies and examples

* fix imports

* fix uv lock

* add code_executor to toctree

---------

Co-authored-by: Leonardo Pinheiro <lpinheiro@microsoft.com>
Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
This commit is contained in:
Leonardo Pinheiro 2024-10-09 08:05:44 +10:00 committed by GitHub
parent ffb16d560a
commit 53e5951474
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 319 additions and 246 deletions

View File

@ -8,7 +8,7 @@ See [`autogen-core`](./packages/autogen-core/) package for main functionality.
**TL;DR**, run all checks with:
```sh
uv sync
uv sync --all-extras
source .venv/bin/activate
poe check
```
@ -24,7 +24,7 @@ in the current directory,
run:
```sh
uv sync
uv sync --all-extras
source .venv/bin/activate
```

View File

@ -23,7 +23,7 @@
"\n",
"## AzureContainerCodeExecutor\n",
"\n",
"The {py:class}`~autogen_core.components.code_executor.AzureContainerCodeExecutor` class is a python code executor that creates and executes arbitrary python code on a default Serverless code interpreter session. Its interface is as follows\n",
"The {py:class}`~autogen_ext.code_executor.aca_dynamic_sessions.AzureContainerCodeExecutor` class is a python code executor that creates and executes arbitrary python code on a default Serverless code interpreter session. Its interface is as follows\n",
"\n",
"### Initialization\n",
"\n",
@ -65,7 +65,8 @@
"\n",
"from anyio import open_file\n",
"from autogen_core.base import CancellationToken\n",
"from autogen_core.components.code_executor import AzureContainerCodeExecutor, CodeBlock\n",
"from autogen_core.components.code_executor import CodeBlock\n",
"from autogen_ext.code_executor.aca_dynamic_sessions import AzureContainerCodeExecutor\n",
"from azure.identity import DefaultAzureCredential"
]
},

View File

@ -40,6 +40,7 @@ python/autogen_core/autogen_core
python/autogen_ext/autogen_ext
python/autogen_ext/autogen_ext.tools
python/autogen_ext/autogen_ext.code_executor
```
::::{grid} 1 2 2 3

View File

@ -22,7 +22,6 @@ dependencies = [
"grpcio~=1.62.0",
"protobuf~=4.25.1",
"tiktoken",
"azure-core",
"docker~=7.0",
"opentelemetry-api~=1.27.0",
"asyncio_atexit"

View File

@ -1,23 +1,37 @@
from ._base import CodeBlock, CodeExecutor, CodeResult
from ._func_with_reqs import Alias, FunctionWithRequirements, Import, ImportFromModule, with_requirements
from ._impl.azure_container_code_executor import AzureContainerCodeExecutor
from ._func_with_reqs import (
Alias,
FunctionWithRequirements,
FunctionWithRequirementsStr,
Import,
ImportFromModule,
build_python_functions_file,
to_stub,
with_requirements,
)
from ._impl.command_line_code_result import CommandLineCodeResult
from ._impl.docker_command_line_code_executor import DockerCommandLineCodeExecutor
from ._impl.local_commandline_code_executor import LocalCommandLineCodeExecutor
from ._impl.utils import get_required_packages, lang_to_cmd
from ._utils import extract_markdown_code_blocks
__all__ = [
"AzureContainerCodeExecutor",
"LocalCommandLineCodeExecutor",
"CommandLineCodeResult",
"CodeBlock",
"CodeResult",
"CodeExecutor",
"CodeResult",
"Alias",
"ImportFromModule",
"Import",
"FunctionWithRequirements",
"FunctionWithRequirementsStr",
"with_requirements",
"to_stub",
"extract_markdown_code_blocks",
"get_required_packages",
"build_python_functions_file",
"DockerCommandLineCodeExecutor",
"get_required_packages",
"lang_to_cmd",
]

View File

@ -8,13 +8,11 @@ import polars
import pytest
from autogen_core.base import CancellationToken
from autogen_core.components.code_executor import (
AzureContainerCodeExecutor,
CodeBlock,
FunctionWithRequirements,
LocalCommandLineCodeExecutor,
with_requirements,
)
from azure.identity import DefaultAzureCredential
ENVIRON_KEY_AZURE_POOL_ENDPOINT = "AZURE_POOL_ENDPOINT"
@ -78,32 +76,35 @@ print(data['name'][0])"""
assert result.exit_code == 0
@pytest.mark.skipif(
not POOL_ENDPOINT,
reason="do not run if pool endpoint is not defined",
)
@pytest.mark.asyncio
async def test_azure_can_load_function_with_reqs() -> None:
assert POOL_ENDPOINT is not None
cancellation_token = CancellationToken()
azure_executor = AzureContainerCodeExecutor(
pool_management_endpoint=POOL_ENDPOINT, credential=DefaultAzureCredential(), functions=[load_data]
)
# AzureContainerCodeExecutor doesn't use the functions module import
code = """import polars
def test_local_formatted_prompt() -> None:
assert_str = '''def add_two_numbers(a: int, b: int) -> int:
"""Add two numbers together."""
'''
with tempfile.TemporaryDirectory() as temp_dir:
executor = LocalCommandLineCodeExecutor(work_dir=temp_dir, functions=[add_two_numbers])
# Get first row's name
data = load_data()
print(data['name'][0])"""
result = executor.format_functions_for_prompt()
assert assert_str in result
azure_result = await azure_executor.execute_code_blocks(
code_blocks=[
CodeBlock(language="python", code=code),
],
cancellation_token=cancellation_token,
def test_local_formatted_prompt_str_func() -> None:
func = FunctionWithRequirements.from_str(
'''
def add_two_numbers(a: int, b: int) -> int:
"""Add two numbers together."""
return a + b
'''
)
assert azure_result.output == "John\n"
assert azure_result.exit_code == 0
assert_str = '''def add_two_numbers(a: int, b: int) -> int:
"""Add two numbers together."""
'''
with tempfile.TemporaryDirectory() as temp_dir:
executor = LocalCommandLineCodeExecutor(work_dir=temp_dir, functions=[func])
result = executor.format_functions_for_prompt()
assert assert_str in result
@pytest.mark.asyncio
@ -124,31 +125,6 @@ print(add_two_numbers(1, 2))"""
assert result.exit_code == 0
@pytest.mark.skipif(
not POOL_ENDPOINT,
reason="do not run if pool endpoint is not defined",
)
@pytest.mark.asyncio
async def test_azure_can_load_function() -> None:
assert POOL_ENDPOINT is not None
cancellation_token = CancellationToken()
azure_executor = AzureContainerCodeExecutor(
pool_management_endpoint=POOL_ENDPOINT, credential=DefaultAzureCredential(), functions=[add_two_numbers]
)
# AzureContainerCodeExecutor doesn't use the functions module import
code = """print(add_two_numbers(1, 2))"""
azure_result = await azure_executor.execute_code_blocks(
code_blocks=[
CodeBlock(language="python", code=code),
],
cancellation_token=cancellation_token,
)
assert azure_result.output == "3\n"
assert azure_result.exit_code == 0
@pytest.mark.asyncio
async def test_fails_for_function_incorrect_import() -> None:
with tempfile.TemporaryDirectory() as temp_dir:
@ -166,30 +142,6 @@ function_incorrect_import()"""
)
@pytest.mark.skipif(
not POOL_ENDPOINT,
reason="do not run if pool endpoint is not defined",
)
@pytest.mark.asyncio
async def test_azure_fails_for_function_incorrect_import() -> None:
assert POOL_ENDPOINT is not None
cancellation_token = CancellationToken()
azure_executor = AzureContainerCodeExecutor(
pool_management_endpoint=POOL_ENDPOINT,
credential=DefaultAzureCredential(),
functions=[function_incorrect_import],
)
code = """function_incorrect_import()"""
with pytest.raises(ValueError):
await azure_executor.execute_code_blocks(
code_blocks=[
CodeBlock(language="python", code=code),
],
cancellation_token=cancellation_token,
)
@pytest.mark.asyncio
async def test_fails_for_function_incorrect_dep() -> None:
with tempfile.TemporaryDirectory() as temp_dir:
@ -207,73 +159,6 @@ function_incorrect_dep()"""
)
@pytest.mark.skipif(
not POOL_ENDPOINT,
reason="do not run if pool endpoint is not defined",
)
@pytest.mark.asyncio
async def test_azure_fails_for_function_incorrect_dep() -> None:
assert POOL_ENDPOINT is not None
cancellation_token = CancellationToken()
azure_executor = AzureContainerCodeExecutor(
pool_management_endpoint=POOL_ENDPOINT, credential=DefaultAzureCredential(), functions=[function_incorrect_dep]
)
code = """function_incorrect_dep()"""
with pytest.raises(ValueError):
await azure_executor.execute_code_blocks(
code_blocks=[
CodeBlock(language="python", code=code),
],
cancellation_token=cancellation_token,
)
def test_formatted_prompt() -> None:
assert_str = '''def add_two_numbers(a: int, b: int) -> int:
"""Add two numbers together."""
'''
with tempfile.TemporaryDirectory() as temp_dir:
executor = LocalCommandLineCodeExecutor(work_dir=temp_dir, functions=[add_two_numbers])
result = executor.format_functions_for_prompt()
assert assert_str in result
azure_executor = AzureContainerCodeExecutor(
pool_management_endpoint=DUMMY_POOL_ENDPOINT, credential=DefaultAzureCredential(), functions=[add_two_numbers]
)
azure_result = azure_executor.format_functions_for_prompt()
assert assert_str in azure_result
def test_formatted_prompt_str_func() -> None:
func = FunctionWithRequirements.from_str(
'''
def add_two_numbers(a: int, b: int) -> int:
"""Add two numbers together."""
return a + b
'''
)
assert_str = '''def add_two_numbers(a: int, b: int) -> int:
"""Add two numbers together."""
'''
with tempfile.TemporaryDirectory() as temp_dir:
executor = LocalCommandLineCodeExecutor(work_dir=temp_dir, functions=[func])
result = executor.format_functions_for_prompt()
assert assert_str in result
azure_executor = AzureContainerCodeExecutor(
pool_management_endpoint=DUMMY_POOL_ENDPOINT, credential=DefaultAzureCredential(), functions=[func]
)
azure_result = azure_executor.format_functions_for_prompt()
assert assert_str in azure_result
@pytest.mark.asyncio
async def test_can_load_str_function_with_reqs() -> None:
func = FunctionWithRequirements.from_str(
@ -300,36 +185,6 @@ print(add_two_numbers(1, 2))"""
assert result.exit_code == 0
@pytest.mark.skipif(
not POOL_ENDPOINT,
reason="do not run if pool endpoint is not defined",
)
@pytest.mark.asyncio
async def test_azure_can_load_str_function_with_reqs() -> None:
assert POOL_ENDPOINT is not None
cancellation_token = CancellationToken()
func = FunctionWithRequirements.from_str(
'''
def add_two_numbers(a: int, b: int) -> int:
"""Add two numbers together."""
return a + b
'''
)
azure_executor = AzureContainerCodeExecutor(
pool_management_endpoint=POOL_ENDPOINT, credential=DefaultAzureCredential(), functions=[func]
)
code = """print(add_two_numbers(1, 2))"""
azure_result = await azure_executor.execute_code_blocks(
code_blocks=[
CodeBlock(language="python", code=code),
],
cancellation_token=cancellation_token,
)
assert azure_result.output == "3\n"
assert azure_result.exit_code == 0
def test_cant_load_broken_str_function_with_reqs() -> None:
with pytest.raises(ValueError):
_ = FunctionWithRequirements.from_str(
@ -365,35 +220,3 @@ print(add_two_numbers(object(), False))"""
)
assert "TypeError: unsupported operand type(s) for +:" in result.output
assert result.exit_code == 1
@pytest.mark.skipif(
not POOL_ENDPOINT,
reason="do not run if pool endpoint is not defined",
)
@pytest.mark.asyncio
async def test_azure_cant_run_broken_str_function_with_reqs() -> None:
assert POOL_ENDPOINT is not None
cancellation_token = CancellationToken()
func = FunctionWithRequirements.from_str(
'''
def add_two_numbers(a: int, b: int) -> int:
"""Add two numbers together."""
return a + b
'''
)
azure_executor = AzureContainerCodeExecutor(
pool_management_endpoint=POOL_ENDPOINT, credential=DefaultAzureCredential(), functions=[func]
)
code = """print(add_two_numbers(object(), False))"""
azure_result = await azure_executor.execute_code_blocks(
code_blocks=[
CodeBlock(language="python", code=code),
],
cancellation_token=cancellation_token,
)
# result.output = result.output.encode().decode('unicode_escape')
assert "TypeError: unsupported operand type(s) for +:" in azure_result.output
assert azure_result.exit_code == 1

View File

@ -19,7 +19,8 @@ dependencies = [
[project.optional-dependencies]
langchain = ["langchain >= 0.3.1"]
langchain-tools = ["langchain >= 0.3.1"]
azure-code-executor = ["azure-core"]
[tool.hatch.build.targets.wheel]
packages = ["src/autogen_ext"]

View File

@ -0,0 +1,5 @@
from ._azure_container_code_executor import AzureContainerCodeExecutor
__all__ = [
"AzureContainerCodeExecutor",
]

View File

@ -11,20 +11,21 @@ import aiohttp
# async functions shouldn't use open()
from anyio import open_file
from azure.core.credentials import AccessToken
# from azure.mgmt.appcontainers import ContainerAppsAPIClient
from typing_extensions import ParamSpec
from ....base import CancellationToken
from .._base import CodeBlock, CodeExecutor, CodeResult
from .._func_with_reqs import (
from autogen_core.base import CancellationToken
from autogen_core.components.code_executor import (
CodeBlock,
CodeExecutor,
CodeResult,
FunctionWithRequirements,
FunctionWithRequirementsStr,
build_python_functions_file,
get_required_packages,
to_stub,
)
from .utils import PYTHON_VARIANTS, get_required_packages, lang_to_cmd # type: ignore
from azure.core.credentials import AccessToken
from typing_extensions import ParamSpec
PYTHON_VARIANTS = ["python", "Python", "py"]
__all__ = ("AzureContainerCodeExecutor", "TokenProvider")
@ -37,25 +38,6 @@ class TokenProvider(Protocol):
) -> AccessToken: ...
# class FileInfo:
# def __init__(self, filename: str, filesize: int, last_modified: str):
# self._filename = filename
# self._filesize = filesize
# self._last_modified = datetime.fromisoformat(last_modified)
# @property
# def filename(self) -> str:
# return self._filename
# @property
# def filesize(self) -> int:
# return self._filesize
# @property
# def last_modified(self) -> datetime:
# return self._last_modified
#
# def __str__(self):
# return f"{self._filename}; {self._filesize} bytes; {self._last_modified}"
class AzureContainerCodeExecutor(CodeExecutor):
SUPPORTED_LANGUAGES: ClassVar[List[str]] = [
"python",

View File

@ -9,7 +9,8 @@ import tempfile
import pytest
from anyio import open_file
from autogen_core.base import CancellationToken
from autogen_core.components.code_executor import AzureContainerCodeExecutor, CodeBlock
from autogen_core.components.code_executor import CodeBlock
from autogen_ext.code_executor.aca_dynamic_sessions import AzureContainerCodeExecutor
from azure.identity import DefaultAzureCredential
UNIX_SHELLS = ["bash", "sh", "shell"]

View File

@ -0,0 +1,244 @@
# File based from: https://github.com/microsoft/autogen/blob/main/test/coding/test_user_defined_functions.py
# Credit to original authors
import os
import polars
import pytest
from autogen_core.base import CancellationToken
from autogen_core.components.code_executor import (
CodeBlock,
FunctionWithRequirements,
with_requirements,
)
from autogen_ext.code_executor.aca_dynamic_sessions import AzureContainerCodeExecutor
from azure.identity import DefaultAzureCredential
ENVIRON_KEY_AZURE_POOL_ENDPOINT = "AZURE_POOL_ENDPOINT"
DUMMY_POOL_ENDPOINT = "DUMMY_POOL_ENDPOINT"
POOL_ENDPOINT = os.getenv(ENVIRON_KEY_AZURE_POOL_ENDPOINT)
def add_two_numbers(a: int, b: int) -> int:
"""Add two numbers together."""
return a + b
@with_requirements(python_packages=["polars"], global_imports=["polars"])
def load_data() -> polars.DataFrame:
"""Load some sample data.
Returns:
polars.DataFrame: A DataFrame with the following columns: name(str), location(str), age(int)
"""
data = {
"name": ["John", "Anna", "Peter", "Linda"],
"location": ["New York", "Paris", "Berlin", "London"],
"age": [24, 13, 53, 33],
}
return polars.DataFrame(data)
@with_requirements(global_imports=["NOT_A_REAL_PACKAGE"])
def function_incorrect_import() -> "polars.DataFrame":
return polars.DataFrame()
@with_requirements(python_packages=["NOT_A_REAL_PACKAGE"])
def function_incorrect_dep() -> "polars.DataFrame":
return polars.DataFrame()
@pytest.mark.skipif(
not POOL_ENDPOINT,
reason="do not run if pool endpoint is not defined",
)
@pytest.mark.asyncio
async def test_azure_can_load_function_with_reqs() -> None:
assert POOL_ENDPOINT is not None
cancellation_token = CancellationToken()
azure_executor = AzureContainerCodeExecutor(
pool_management_endpoint=POOL_ENDPOINT, credential=DefaultAzureCredential(), functions=[load_data]
)
# AzureContainerCodeExecutor doesn't use the functions module import
code = """import polars
# Get first row's name
data = load_data()
print(data['name'][0])"""
azure_result = await azure_executor.execute_code_blocks(
code_blocks=[
CodeBlock(language="python", code=code),
],
cancellation_token=cancellation_token,
)
assert azure_result.output == "John\n"
assert azure_result.exit_code == 0
@pytest.mark.skipif(
not POOL_ENDPOINT,
reason="do not run if pool endpoint is not defined",
)
@pytest.mark.asyncio
async def test_azure_can_load_function() -> None:
assert POOL_ENDPOINT is not None
cancellation_token = CancellationToken()
azure_executor = AzureContainerCodeExecutor(
pool_management_endpoint=POOL_ENDPOINT, credential=DefaultAzureCredential(), functions=[add_two_numbers]
)
# AzureContainerCodeExecutor doesn't use the functions module import
code = """print(add_two_numbers(1, 2))"""
azure_result = await azure_executor.execute_code_blocks(
code_blocks=[
CodeBlock(language="python", code=code),
],
cancellation_token=cancellation_token,
)
assert azure_result.output == "3\n"
assert azure_result.exit_code == 0
@pytest.mark.skipif(
not POOL_ENDPOINT,
reason="do not run if pool endpoint is not defined",
)
@pytest.mark.asyncio
async def test_azure_fails_for_function_incorrect_import() -> None:
assert POOL_ENDPOINT is not None
cancellation_token = CancellationToken()
azure_executor = AzureContainerCodeExecutor(
pool_management_endpoint=POOL_ENDPOINT,
credential=DefaultAzureCredential(),
functions=[function_incorrect_import],
)
code = """function_incorrect_import()"""
with pytest.raises(ValueError):
await azure_executor.execute_code_blocks(
code_blocks=[
CodeBlock(language="python", code=code),
],
cancellation_token=cancellation_token,
)
@pytest.mark.skipif(
not POOL_ENDPOINT,
reason="do not run if pool endpoint is not defined",
)
@pytest.mark.asyncio
async def test_azure_fails_for_function_incorrect_dep() -> None:
assert POOL_ENDPOINT is not None
cancellation_token = CancellationToken()
azure_executor = AzureContainerCodeExecutor(
pool_management_endpoint=POOL_ENDPOINT, credential=DefaultAzureCredential(), functions=[function_incorrect_dep]
)
code = """function_incorrect_dep()"""
with pytest.raises(ValueError):
await azure_executor.execute_code_blocks(
code_blocks=[
CodeBlock(language="python", code=code),
],
cancellation_token=cancellation_token,
)
def test_azure_formatted_prompt() -> None:
assert_str = '''def add_two_numbers(a: int, b: int) -> int:
"""Add two numbers together."""
'''
azure_executor = AzureContainerCodeExecutor(
pool_management_endpoint=DUMMY_POOL_ENDPOINT, credential=DefaultAzureCredential(), functions=[add_two_numbers]
)
azure_result = azure_executor.format_functions_for_prompt()
assert assert_str in azure_result
def test_azure_formatted_prompt_str_func() -> None:
func = FunctionWithRequirements.from_str(
'''
def add_two_numbers(a: int, b: int) -> int:
"""Add two numbers together."""
return a + b
'''
)
assert_str = '''def add_two_numbers(a: int, b: int) -> int:
"""Add two numbers together."""
'''
azure_executor = AzureContainerCodeExecutor(
pool_management_endpoint=DUMMY_POOL_ENDPOINT, credential=DefaultAzureCredential(), functions=[func]
)
azure_result = azure_executor.format_functions_for_prompt()
assert assert_str in azure_result
@pytest.mark.skipif(
not POOL_ENDPOINT,
reason="do not run if pool endpoint is not defined",
)
@pytest.mark.asyncio
async def test_azure_can_load_str_function_with_reqs() -> None:
assert POOL_ENDPOINT is not None
cancellation_token = CancellationToken()
func = FunctionWithRequirements.from_str(
'''
def add_two_numbers(a: int, b: int) -> int:
"""Add two numbers together."""
return a + b
'''
)
azure_executor = AzureContainerCodeExecutor(
pool_management_endpoint=POOL_ENDPOINT, credential=DefaultAzureCredential(), functions=[func]
)
code = """print(add_two_numbers(1, 2))"""
azure_result = await azure_executor.execute_code_blocks(
code_blocks=[
CodeBlock(language="python", code=code),
],
cancellation_token=cancellation_token,
)
assert azure_result.output == "3\n"
assert azure_result.exit_code == 0
@pytest.mark.skipif(
not POOL_ENDPOINT,
reason="do not run if pool endpoint is not defined",
)
@pytest.mark.asyncio
async def test_azure_cant_run_broken_str_function_with_reqs() -> None:
assert POOL_ENDPOINT is not None
cancellation_token = CancellationToken()
func = FunctionWithRequirements.from_str(
'''
def add_two_numbers(a: int, b: int) -> int:
"""Add two numbers together."""
return a + b
'''
)
azure_executor = AzureContainerCodeExecutor(
pool_management_endpoint=POOL_ENDPOINT, credential=DefaultAzureCredential(), functions=[func]
)
code = """print(add_two_numbers(object(), False))"""
azure_result = await azure_executor.execute_code_blocks(
code_blocks=[
CodeBlock(language="python", code=code),
],
cancellation_token=cancellation_token,
)
# result.output = result.output.encode().decode('unicode_escape')
assert "TypeError: unsupported operand type(s) for +:" in azure_result.output
assert azure_result.exit_code == 1

View File

@ -360,7 +360,6 @@ source = { editable = "packages/autogen-core" }
dependencies = [
{ name = "aiohttp" },
{ name = "asyncio-atexit" },
{ name = "azure-core" },
{ name = "docker" },
{ name = "grpcio" },
{ name = "openai" },
@ -417,7 +416,6 @@ dev = [
requires-dist = [
{ name = "aiohttp" },
{ name = "asyncio-atexit" },
{ name = "azure-core" },
{ name = "docker", specifier = "~=7.0" },
{ name = "grpcio", specifier = "~=1.62.0" },
{ name = "openai", specifier = ">=1.3" },
@ -479,14 +477,18 @@ dependencies = [
]
[package.optional-dependencies]
langchain = [
azure-code-executor = [
{ name = "azure-core" },
]
langchain-tools = [
{ name = "langchain" },
]
[package.metadata]
requires-dist = [
{ name = "autogen-core", editable = "packages/autogen-core" },
{ name = "langchain", marker = "extra == 'langchain'", specifier = ">=0.3.1" },
{ name = "azure-core", marker = "extra == 'azure-code-executor'" },
{ name = "langchain", marker = "extra == 'langchain-tools'", specifier = ">=0.3.1" },
]
[[package]]