langchain-acasandbox
langchain-acasandbox is an async Azure Container Apps Sandbox provider for LangChain Deep Agents. It provisions a restricted sandbox, exposes command and file operations through the Deep Agents BaseSandbox contract, and confirms deletion when the session ends.
The library is intentionally small. A complete hello-world Deep Agent lives in samples/basic.
The Azure Container Apps sandbox SDK is a preview dependency. Pin and test this library before using it in production.
Requirements
- Python 3.13 or later
- An Azure subscription and tenant
- An existing Azure Container Apps sandbox group
- Azure CLI authentication for local development, or workload identity in Azure
- Permission to create, inspect, and delete sandboxes in the group
This project never accepts account keys or client secrets. Credentials stay in the host process and are not forwarded to sandbox commands.
Installation
From PyPI after publication:
uv add langchain-acasandbox
From this checkout:
uv sync --locked
Configuration
Create SandboxConfig directly when values already come from your application configuration:
from langchain_acasandbox import SandboxConfig
config = SandboxConfig(
subscription_id="00000000-0000-0000-0000-000000000000",
resource_group="sandbox-rg",
sandbox_group="agents",
tenant_id="00000000-0000-0000-0000-000000000000",
location="swedencentral",
)
Alternatively, SandboxConfig.from_env() reads these variables:
| Variable | Required | Default | Meaning |
|---|---|---|---|
AZURE_SUBSCRIPTION_ID |
yes | Azure subscription | |
AZURE_TENANT_ID |
yes | Microsoft Entra tenant | |
SANDBOX_RESOURCE_GROUP |
yes | Resource group containing the sandbox group | |
SANDBOX_GROUP_NAME |
yes | Sandbox group name | |
SANDBOX_LOCATION |
no | swedencentral |
Azure region |
AZURE_AUTH_MODE |
no | cli |
cli or workload |
SANDBOX_DISK |
no | ubuntu |
Base disk name when no disk ID is supplied |
SANDBOX_DISK_ID |
no | Existing disk image ID | |
SANDBOX_SNAPSHOT_ID |
no | Existing snapshot ID; mutually exclusive with disk ID | |
SANDBOX_IDLE_TIMEOUT_SECONDS |
no | 600 |
Azure auto-suspend interval |
SANDBOX_AUTO_DELETE_SECONDS |
no | 172800 |
Azure fallback auto-delete interval |
Local authentication uses the selected tenant:
az login --tenant "$AZURE_TENANT_ID"
Basic Usage
Always open manager-owned sandboxes with async with. Exiting the block quarantines the backend, deletes the sandbox, and verifies that Azure reports it absent.
import asyncio
from langchain_acasandbox import SandboxConfig, SandboxManager
async def main() -> None:
manager = SandboxManager(SandboxConfig.from_env())
async with manager.open() as session:
result = await session.backend.aexecute("printf 'hello from Azure'", timeout=20)
if result.exit_code != 0:
raise RuntimeError(result.output)
print(result.output)
asyncio.run(main())
The public backend methods are async-only:
backend = session.backend
uploads = await backend.aupload_files([
("/workspace/input.txt", b"sandbox input\n"),
])
result = await backend.aexecute(
"wc -c /workspace/input.txt > /workspace/count.txt",
timeout=30,
)
downloads = await backend.adownload_files(["/workspace/count.txt"])
if uploads[0].error or result.exit_code or downloads[0].error:
raise RuntimeError("Sandbox operation failed")
print(downloads[0].content.decode())
Paths must be absolute. File operations return one response per requested path, with a normalized error such as file_not_found, invalid_path, is_directory, or permission_denied.
Deep Agents
Pass the backend directly to create_deep_agent and invoke the graph asynchronously:
from deepagents import create_deep_agent
from langchain_openai import ChatOpenAI
from langchain_acasandbox import SandboxConfig, SandboxManager
async def run_agent() -> None:
manager = SandboxManager(SandboxConfig.from_env())
model = ChatOpenAI(model="your-model")
async with manager.open() as session:
agent = create_deep_agent(model=model, backend=session.backend)
result = await agent.ainvoke({
"messages": [{"role": "user", "content": "Create /workspace/result.txt"}]
})
print(result["messages"][-1].content)
The backend implements Deep Agents async execution and file-transfer methods. Its synchronous execute, upload_files, and download_files methods deliberately raise an error.
Lifecycle And Failure Behavior
SandboxManager.open() creates a sandbox with:
- deny-by-default egress with full traffic inspection
- no ingress ports or connections
- auto-suspend after the configured idle interval
- Azure-side auto-delete as a fallback
- ownership and expiry labels used by the janitor
- a prepared
/workspacedirectory
Operations are serialized per backend. The defaults allow a 60-second operation timeout, a 120-second maximum requested timeout, 1 MiB of captured command output, 10 MiB per file, and 100 files per batch.
An unexpected Azure error, cancellation, response-limit breach, or operation timeout quarantines the backend. Later operations fail with SandboxUnavailable; do not reuse a quarantined session. A timeout also means remote command termination is unconfirmed, so the manager proceeds to deletion.
Manager-owned sessions have a 30-minute maximum lifetime and a 10-minute inactivity lifetime. Cleanup is idempotent and deletion must be confirmed. Expiry-task cleanup failures are available as session.cleanup_error; explicit close failures are raised to the caller. Run the janitor after interrupted processes or uncertain cleanup.
If you instantiate AzureContainerAppsSandbox directly around an SDK SandboxClient, the adapter borrows that client. You own the client lifecycle and deletion of the Azure resource.
Operator Commands
Commands use the same environment configuration:
uv run --env-file .env acasandbox smoke
uv run --env-file .env acasandbox janitor
uv run --env-file .env acasandbox inventory
uv run --env-file .env acasandbox image-import
smokeprovisions a sandbox, verifies required tools and binary file transfer, then deletes it.janitordeletes expired sandboxes carrying the library ownership label.inventorylists sandboxes and disk images in the configured group.image-importimports the Python 3.13 image pinned in src/langchain_acasandbox/cli.py. This creates a billable Azure resource.
Development
make check
This runs Ruff, Pyright, and all non-live unit tests. Azure integration tests are opt-in and may incur charges:
RUN_SANDBOX_LIVE=1 uv run pytest tests/integration_tests -q -m live
Infrastructure is under infra. Start with the basic hello-world agent.
Publishing
Releases publish to PyPI through GitHub Actions trusted publishing. Configure a PyPI trusted publisher with:
| Setting | Value |
|---|---|
| Owner | danigian |
| Repository | langchain-acasandbox |
| Workflow | publish.yml |
| Environment | pypi |
No PyPI token is stored in GitHub. To publish, update the version in pyproject.toml, push main, and publish a GitHub release whose tag is exactly v<version>, such as v0.1.0. The workflow runs all checks, builds and validates both distributions, and publishes the tested artifacts only after the pypi environment gate.
Metadata
Release files for langchain-acasandbox 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| langchain_acasandbox-0.1.0.tar.gz | 91.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| langchain_acasandbox-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 107.7 kB
Release files / langchain_acasandbox-0.1.0.tar.gz
| Download URL | langchain_acasandbox-0.1.0.tar.gz |
|---|---|
| Size | 91.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d9d7bcb269ce5dab25ede88b4e57c8967edeb49a05f29fdb6f683c2a55b9b171
|
|
BLAKE2b-256 checksum How to use checksums |
f8ea4250ac45b0c97b3109ef10fd0204bbfd051a3efda666200ea7147238a015
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.
Transparency logRelease files / langchain_acasandbox-0.1.0-py3-none-any.whl
| Download URL | langchain_acasandbox-0.1.0-py3-none-any.whl |
|---|---|
| Size | 15.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4293d52be3de5ac22d21c49b7f4c7ee1d277660746693330986f64d75fb42794
|
|
BLAKE2b-256 checksum How to use checksums |
73e7fb1deff6813c29d087b0ed041ed5112444343b876209fd47ea89afed342c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.
Transparency log