Skip to content
Closed
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=
50 changes: 50 additions & 0 deletions scenarios/Agents/samples/code-interpreter-custom/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# 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.

- The `az` CLI
- The `uv` or `pip` Python package managers

## 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

Run the following, substituting the appropriate values. Set parameters if you wish.

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

This can take a while!

### 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.

Install the Python dependencies (`uv sync` or `pip install`). Finally, run `./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.

## References

- [Azure Container Apps Dynamic Sessions](/azure/container-apps/sessions)
- [Session Pools with Custom Containers](/azure/container-apps/session-pool#custom-container-pool)
231 changes: 231 additions & 0 deletions scenarios/Agents/samples/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 scenarios/Agents/samples/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")
13 changes: 13 additions & 0 deletions scenarios/Agents/samples/code-interpreter-custom/pyproject.toml
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
Loading