ACA Dynamic Sessions Code Executor#
This guide will explain the Azure Container Apps dynamic sessions in Azure Container Apps and show you how to use the Azure Container Code Executor class.
The Azure Container Apps dynamic sessions is a component in the Azure Container Apps service. The environment is hosted on remote Azure instances and will not execute any code locally. The interpreter is capable of executing python code in a jupyter environment with a pre-installed base of commonly used packages. Custom environments can be created by users for their applications. Files can additionally be uploaded to, or downloaded from each session.
The code interpreter can run multiple sessions of code, each of which are delineated by a session identifier string.
Create a Container Apps Session Pool#
In your Azure portal, create a new Container App Session Pool
resource with the pool type set to Python code interpreter
and note the Pool management endpoint
. The format for the endpoint should be something like https://{region}.dynamicsessions.io/subscriptions/{subscription_id}/resourceGroups/{resource_group_name}/sessionPools/{session_pool_name}
.
Alternatively, you can use the Azure CLI to create a session pool.
AzureContainerCodeExecutor#
The 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
Initialization#
First, you will need to find or create a credentialing object that implements the TokenProvider
interface. This is any object that implements the following function
def get_token(
self, *scopes: str, claims: Optional[str] = None, tenant_id: Optional[str] = None, **kwargs: Any
) -> azure.core.credentials.AccessToken
An example of such an object is the azure.identity.DefaultAzureCredential class.
Lets start by installing that
# pip install azure.identity
Next, lets import all the necessary modules and classes for our code
import os
import tempfile
from anyio import open_file
from autogen_core.base import CancellationToken
from autogen_core.components.code_executor import CodeBlock
from autogen_ext.code_executor.aca_dynamic_sessions import AzureContainerCodeExecutor
from azure.identity import DefaultAzureCredential
Now, we create our Azure code executor and run some test code along with verification that it ran correctly. We’ll create the executor with a temporary working directory to ensure a clean environment as we show how to use each feature
cancellation_token = CancellationToken()
POOL_MANAGEMENT_ENDPOINT = "..."
with tempfile.TemporaryDirectory() as temp_dir:
executor = AzureContainerCodeExecutor(
pool_management_endpoint=POOL_MANAGEMENT_ENDPOINT, credential=DefaultAzureCredential(), work_dir=temp_dir
)
code_blocks = [CodeBlock(code="import sys; print('hello world!')", language="python")]
code_result = await executor.execute_code_blocks(code_blocks, cancellation_token)
assert code_result.exit_code == 0 and "hello world!" in code_result.output
Next, lets try uploading some files and verifying their integrity. All files uploaded to the Serverless code interpreter is uploaded into the /mnt/data
directory. All downloadable files must also be placed in the directory. By default, the current working directory for the code executor is set to /mnt/data
.
with tempfile.TemporaryDirectory() as temp_dir:
test_file_1 = "test_upload_1.txt"
test_file_1_contents = "test1 contents"
test_file_2 = "test_upload_2.txt"
test_file_2_contents = "test2 contents"
async with await open_file(os.path.join(temp_dir, test_file_1), "w") as f: # type: ignore[syntax]
await f.write(test_file_1_contents)
async with await open_file(os.path.join(temp_dir, test_file_2), "w") as f: # type: ignore[syntax]
await f.write(test_file_2_contents)
assert os.path.isfile(os.path.join(temp_dir, test_file_1))
assert os.path.isfile(os.path.join(temp_dir, test_file_2))
executor = AzureContainerCodeExecutor(
pool_management_endpoint=POOL_MANAGEMENT_ENDPOINT, credential=DefaultAzureCredential(), work_dir=temp_dir
)
await executor.upload_files([test_file_1, test_file_2], cancellation_token)
file_list = await executor.get_file_list(cancellation_token)
assert test_file_1 in file_list
assert test_file_2 in file_list
code_blocks = [
CodeBlock(
code=f"""
with open("{test_file_1}") as f:
print(f.read())
with open("{test_file_2}") as f:
print(f.read())
""",
language="python",
)
]
code_result = await executor.execute_code_blocks(code_blocks, cancellation_token)
assert code_result.exit_code == 0
assert test_file_1_contents in code_result.output
assert test_file_2_contents in code_result.output
Downloading files works in a similar way.
with tempfile.TemporaryDirectory() as temp_dir:
test_file_1 = "test_upload_1.txt"
test_file_1_contents = "test1 contents"
test_file_2 = "test_upload_2.txt"
test_file_2_contents = "test2 contents"
assert not os.path.isfile(os.path.join(temp_dir, test_file_1))
assert not os.path.isfile(os.path.join(temp_dir, test_file_2))
executor = AzureContainerCodeExecutor(
pool_management_endpoint=POOL_MANAGEMENT_ENDPOINT, credential=DefaultAzureCredential(), work_dir=temp_dir
)
code_blocks = [
CodeBlock(
code=f"""
with open("{test_file_1}", "w") as f:
f.write("{test_file_1_contents}")
with open("{test_file_2}", "w") as f:
f.write("{test_file_2_contents}")
""",
language="python",
),
]
code_result = await executor.execute_code_blocks(code_blocks, cancellation_token)
assert code_result.exit_code == 0
file_list = await executor.get_file_list(cancellation_token)
assert test_file_1 in file_list
assert test_file_2 in file_list
await executor.download_files([test_file_1, test_file_2], cancellation_token)
assert os.path.isfile(os.path.join(temp_dir, test_file_1))
async with await open_file(os.path.join(temp_dir, test_file_1), "r") as f: # type: ignore[syntax]
content = await f.read()
assert test_file_1_contents in content
assert os.path.isfile(os.path.join(temp_dir, test_file_2))
async with await open_file(os.path.join(temp_dir, test_file_2), "r") as f: # type: ignore[syntax]
content = await f.read()
assert test_file_2_contents in content
New Sessions#
Every instance of the AzureContainerCodeExecutor
class will have a unique session ID. Every call to a particular code executor will be executed on the same session until the restart()
function is called on it. Previous sessions cannot be reused.
Here we’ll run some code on the code session, restart it, then verify that a new session has been opened.
executor = AzureContainerCodeExecutor(
pool_management_endpoint=POOL_MANAGEMENT_ENDPOINT, credential=DefaultAzureCredential()
)
code_blocks = [CodeBlock(code="x = 'abcdefg'", language="python")]
code_result = await executor.execute_code_blocks(code_blocks, cancellation_token)
assert code_result.exit_code == 0
code_blocks = [CodeBlock(code="print(x)", language="python")]
code_result = await executor.execute_code_blocks(code_blocks, cancellation_token)
assert code_result.exit_code == 0 and "abcdefg" in code_result.output
await executor.restart()
code_blocks = [CodeBlock(code="print(x)", language="python")]
code_result = await executor.execute_code_blocks(code_blocks, cancellation_token)
assert code_result.exit_code != 0 and "NameError" in code_result.output
Available Packages#
Each code execution instance is pre-installed with most of the commonly used packages. However, the list of available packages and versions are not available outside of the execution environment. The packages list on the environment can be retrieved by calling the get_available_packages()
function on the code executor.
print(executor.get_available_packages(cancellation_token))