Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
AZURE_AI_PROJECT_ENDPOINT=
AZURE_AI_CONNECTION_ID=
AZURE_AI_MODEL_DEPLOYMENT_NAME=
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.venv/
.env
__pycache__/
65 changes: 65 additions & 0 deletions samples/python/prompt-agents/code-interpreter-custom/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Custom Code Interpreter with Session Pool MCP server

This provides example Bicep code for setting up a Container Apps dynamic session pool
with a custom code interpreter image, as well as Python client code demonstrating
how to use it with a Foundry Hosted Agent.

You will need the following installed to run the sample code:

- The `az` CLI
- Python3
- A Python3 package manager like `uv` or `pip` + `venv`
- If you are using `pip`, make sure `ensurepip` is installed. On Debian/Ubuntu
systems, this would mean running `apt install python3.12-venv`.

## Running code sample

### Enable MCP server for dynamic sessions

This is required to enable the preview feature.

```console
az feature register --namespace Microsoft.App --name SessionPoolsSupportMCP
az provider register -n Microsoft.App
```

### Create a dynamic session pool with a code interpreter image

Using the `az` CLI, deploy with the provided Bicep template file:

```console
az deployment group create \
--name custom-code-interpreter \
--subscription <your_subscription> \
--resource-group <your_resource_group> \
--template-file ./infra.bicep
```

> [!NOTE] This can take a while! Allocating the dynamic session pool
> can take up to 1 hour, depending on the number of standby instances
> requested.

### Use the custom code interpreter in an agent

Copy the [`.env.sample`](./.env.sample) file to `.env` and fill in the values with
the output of the above deployment, which you can find in the Web Portal under the
resource group.

Finally, install Python dependencies and run the script:

```console
# Using uv

uv sync
uv run ./main.py

# Using pip

python3 -m venv .venv
./.venv/bin/pip3 install -r requirements.txt
./.venv/bin/python3 ./main.py
```

## Limitations

File input/output and use of file stores are not directly supported in APIs, so you must use URLs (such as data URLs for small files and Azure Blob Service SAS URLs for large ones) to get data in and out.
231 changes: 231 additions & 0 deletions samples/python/prompt-agents/code-interpreter-custom/infra.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
@description('Suffix for resource names to ensure uniqueness')
@minLength(3)
param suffix string = uniqueString(resourceGroup().id)

@description('Container Apps environment name')
@minLength(3)
param environmentName string = 'aca-env-${suffix}'

@description('Session pool name')
@minLength(3)
param sessionPoolName string = 'sp-${suffix}'

@description('The amount of CPU to provide to each container instance, in vCPU counts')
@minValue(1)
@maxValue(16)
param cpu int = 1

@description('The amount of RAM to provide to each container instance, in GiB')
@minValue(1)
@maxValue(16)
param memory int = 2

@description('Location of all ACA resources.')
@allowed([
'eastus'
'swedencentral'
'northeurope'
])
param location string = 'swedencentral'

@description('Use managed identity for deployment script principal')
param useManagedIdentity bool = true

@description('An image that implements the code interpreter HTTP API')
param image string = 'mcr.microsoft.com/k8se/services/codeinterpreter:0.9.18-python3.12'

@description('Model deployment name')
param modelDeploymentName string = 'my-gpt-4o-mini'

@description('Model to deploy')
param modelName string = 'gpt-4o-mini'

resource environment 'Microsoft.App/managedEnvironments@2025-10-02-preview' = {
name: environmentName
location: location
properties: {
workloadProfiles: [
{
name: 'Consumption'
workloadProfileType: 'Consumption'
}
]
}
}

resource sessionPool 'Microsoft.App/sessionPools@2025-10-02-preview' = {
name: sessionPoolName
location: location
properties: {
environmentId: environment.id
poolManagementType: 'Dynamic'
containerType: 'CustomContainer'
scaleConfiguration: {
maxConcurrentSessions: 10
readySessionInstances: 5
}
dynamicPoolConfiguration: {
lifecycleConfiguration: {
cooldownPeriodInSeconds: 600
lifecycleType: 'Timed'
}
}
customContainerTemplate: {
containers: [
{
name: 'jupyterpython'
image: image
env: [
{
name: 'SYS_RUNTIME_SANDBOX'
value: 'AzureContainerApps-DynamicSessions'
}
{
name: 'AZURE_CODE_EXEC_ENV'
value: 'AzureContainerApps-DynamicSessions-Py3.12'
}
{
name: 'AZURECONTAINERAPPS_SESSIONS_SANDBOX_VERSION'
value: '7758'
}
{
name: 'JUPYTER_TOKEN'
value: 'AzureContainerApps-DynamicSessions'
}
]
resources: {
cpu: cpu
memory: '${memory}Gi'
}
probes: [
{
type: 'Liveness'
httpGet: {
path: '/health'
port: 6000
}
failureThreshold: 4
}
{
type: 'Startup'
httpGet: {
path: '/health'
port: 6000
}
failureThreshold: 30
periodSeconds: 2
}
]
}
]
ingress: {
targetPort: 6000
}
}
mcpServerSettings: {
isMcpServerEnabled: true
}
sessionNetworkConfiguration: {
status: 'egressEnabled'
}
}
}

resource scriptPrincipal 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = if (useManagedIdentity){
name: 'deployScriptIdentity-${suffix}'
location: location
}

resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (useManagedIdentity) {
name: guid(scriptPrincipal!.id, 'apps-sessionpool-contributor')
scope: resourceGroup()
properties: {
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f7669afb-68b2-44b4-9c5f-6d2a47fddda0') // Container Apps SessionPools Contributor
principalId: scriptPrincipal!.properties.principalId
principalType: 'ServicePrincipal'
}
}

resource deployScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = {
name: 'getmcpkey-${suffix}'
location: location
kind: 'AzureCLI'
identity: useManagedIdentity ? {
type: 'UserAssigned'
userAssignedIdentities: {
'${scriptPrincipal!.id}': {}
}
} : null
properties: {
azCliVersion: '2.77.0'
scriptContent: '''
az rest --method post --url "$SESSION_POOL_ID/fetchMCPServerCredentials?api-version=2025-02-02-preview" | jq -c '{"key": .apiKey}' > $AZ_SCRIPTS_OUTPUT_PATH
'''
timeout: 'PT30M'
retentionInterval: 'P1D'
cleanupPreference: 'OnSuccess'
environmentVariables: [
{
name: 'SESSION_POOL_ID'
value: sessionPool.id
}
]
}
}

resource aiAccount 'Microsoft.CognitiveServices/accounts@2025-10-01-preview' = {
name: 'aia-${suffix}'
location: location
kind: 'AIServices'
sku: {
name: 'S0'
}
properties: {
customSubDomainName: 'myaiaccount-${suffix}'
allowProjectManagement: true
}

resource project 'projects' = {
name: 'aip-${suffix}s'
properties: {
description: 'This is my AI project.'
}

resource mcpConn 'connections' = {
name: 'aic-${suffix}'
properties: {
authType: 'CustomKeys'
category: 'RemoteTool'
credentials: {
keys: {
'x-ms-apikey': deployScript.properties.outputs.key
}
}
target: sessionPool.properties.mcpServerSettings.mcpServerEndpoint
}
}
}

resource model 'deployments' = {
name: modelDeploymentName
sku: {
name: 'GlobalStandard'
capacity: 1
}
properties: {
model: {
format: 'OpenAI'
name: modelName
}
}
}
}

@description('Outputs the ID of the project connection for the Code Interpreter MCP Tool')
output AZURE_AI_CONNECTION_ID string = aiAccount::project::mcpConn.id

@description('Model deployment name')
output AZURE_AI_MODEL_DEPLOYMENT_NAME string = aiAccount::model.name

@description('AI Project Endpoint')
output AZURE_AI_PROJECT_ENDPOINT string = aiAccount::project.properties.endpoints['AI Foundry API']
60 changes: 60 additions & 0 deletions samples/python/prompt-agents/code-interpreter-custom/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import os

import dotenv
from azure.ai.projects import AIProjectClient
from azure.ai.projects.models import PromptAgentDefinition, MCPTool
from azure.identity import DefaultAzureCredential

dotenv.load_dotenv()

project_client = AIProjectClient(
endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"],
credential=DefaultAzureCredential(),
)
openai_client = project_client.get_openai_client()

tools = [
MCPTool(
# This is just a placeholder. Connection details are in
# the project connection referenced by `project_connection_id`.
server_url="https://localhost",
server_label="python_tool",
require_approval="never",
allowed_tools=[
"launchShell",
"runPythonCodeInRemoteEnvironment",
],
project_connection_id=os.environ["AZURE_AI_CONNECTION_ID"],
),
]

EXAMPLE_DATA_FILE_URL = "https://raw.githubusercontent.com/Azure-Samples/azureai-samples/refs/heads/main/scenarios/Agents/data/nifty_500_quarterly_results.csv"

with project_client:
agent = project_client.agents.create_version(
agent_name="MyAgent",
definition=PromptAgentDefinition(
model=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"],
instructions="""\
You are a helpful agent that can use a Python code interpreter to assist users. Use the `python_tool` MCP
server to perform any calculations or numerical analyses. ALWAYS call the `launchShell` tool first before
calling the `runPythonCodeInRemoteEnvironment` tool. If you need to provide any non-text data to the user,
always print a data URI with the contents. NEVER provide a path to a file in the remote environment to the user.
""",
temperature=0,
tools=tools,
),
)
print(f"Agent created (id: {agent.id}, name: {agent.name}, version: {agent.version})")

# Use the agent to analyze a CSV file and produce a histogram
response = openai_client.responses.create(
input=f"Please analyze the CSV file at {EXAMPLE_DATA_FILE_URL}. Could you please create bar chart in the TRANSPORTATION sector for the operating profit and provide a file to me?",
extra_body={"agent": {"name": agent.name, "type": "agent_reference"}},
)
print(f"[Response {response.id}]: {response.output_text}")

# Clean up resources by deleting the agent version
# This prevents accumulation of unused agent versions in your project
project_client.agents.delete_version(agent_name=agent.name, agent_version=agent.version)
print("Agent deleted")
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[project]
name = "code-interpreter-custom"
version = "0.1.0"
description = "Basic example for using custom code interpreter session pools."
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"aiohttp>=3.13.2",
"azure-ai-projects==2.0.0b2",
"azure-identity>=1.25.1",
"dotenv>=0.9.9",
"openai>=2.8.1",
]
Loading