--- icon: robot --- # LangChain DeepAgents The SDK provides deepagents `BaseSandbox` implementations for command execution and file transfer. Use `ContreeSandbox` with a sync executor, or `ContreeAsyncSandbox` with an async executor. A session implements the matching contract. :::{note} Async snippets with top-level `await` run inside an async function or a notebook that supports it. For a standalone script, use the `asyncio.run(main())` structure from {doc}`../python_sdk/getting-started`. ::: The examples use the default sandbox from `create_sandbox_session`. Commands and uploads use the same VM. Downloads automatically save its current filesystem before reading; this stops the VM. The next command starts from the saved image. The factory handles connection setup and cleanup. ## Install and test the backend This integration requires Python 3.11 or newer. Install the development API from the repository root, as in {doc}`../python_sdk/getting-started`: ```bash pip install -e ".[langchain,async]" ``` The extra pins deepagents 0.7.10, the version tested with this SDK. Its async file tools delegate to `aexecute` and `aupload_files`. Version 0.6.8 instead calls sync methods from those tools and is incompatible with `ContreeAsyncSandbox`. The sandbox image needs a POSIX shell, Python 3, and the file utilities used by deepagents, including `grep` for search. Replace the example `tag:python:3.12-alpine` with an image available on your endpoint. Configure a connection as described in {ref}`sandbox-connection`. This example exercises the backend directly, so no model credentials are needed. ::::{tab} Sync ```python from contree_sdk import create_sandbox_session from contree_sdk.langchain import ContreeSandbox with create_sandbox_session(image="tag:python:3.12-alpine") as session: sandbox = ContreeSandbox(session) result = sandbox.execute("echo hello") print(result.output) print("exit:", result.exit_code) ``` :::: ::::{tab} Async ```python from contree_sdk import create_sandbox_session from contree_sdk.langchain import ContreeAsyncSandbox async with create_sandbox_session(image="tag:python:3.12-alpine") as session: sandbox = ContreeAsyncSandbox(session) result = await sandbox.aexecute("echo hello") print(result.output) print("exit:", result.exit_code) ``` :::: The adapter runs commands with `disposable=False`. Its next tool call sees the filesystem changes. It does not close the session, client, or store. Use SQLite when the agent needs to resume after a Python restart. ## Connect the backend to an agent Pass the sandbox as `backend=` to `deepagents.create_deep_agent`. Supply the model configured by your application. Keep the client open for the entire agent invocation. The following function accepts that model and uses the connection configured in {ref}`sandbox-connection`: ::::{tab} Sync ```python from deepagents import create_deep_agent from contree_sdk import create_sandbox_session from contree_sdk.langchain import ContreeSandbox def ask_agent(model): with create_sandbox_session(image="tag:python:3.12-alpine") as session: agent = create_deep_agent(model=model, backend=ContreeSandbox(session)) request = {"messages": [{"role": "user", "content": "Create a calculator script and run it."}]} return agent.invoke(request) ``` :::: ::::{tab} Async ```python from deepagents import create_deep_agent from contree_sdk import create_sandbox_session from contree_sdk.langchain import ContreeAsyncSandbox async def ask_agent(model): async with create_sandbox_session(image="tag:python:3.12-alpine") as session: agent = create_deep_agent(model=model, backend=ContreeAsyncSandbox(session)) request = {"messages": [{"role": "user", "content": "Create a calculator script and run it."}]} return await agent.ainvoke(request) ``` :::: Model-provider installation, credentials, and model selection belong to the agent application. The SDK does not choose a provider or supply a model key. ## Advanced: file transfers and executor contracts Uploads call `session.write_file(path, content)`. Its default implementation writes binary stdin with `cat`, creates parent directories, and requests no snapshot. Custom executors can override `write_file` without changing this adapter. Downloads call `session.read_file()`. Lazy sessions read the active VM through live inspect, so downloads preserve running processes and do not force a snapshot. | Sync sandbox | Async sandbox | | ------------------------------- | -------------------------------------- | | `execute(command)` | `await aexecute(command)` | | `upload_files([(path, bytes)])` | `await aupload_files([(path, bytes)])` | | `download_files([path])` | `await adownload_files([path])` | File paths must be absolute. Transfers return per-file responses; inspect `error` before reading their content. The async adapter's synchronous methods raise `NotImplementedError`. Each adapter serializes its own calls, but callers sharing an executor must coordinate other mutations themselves. To use another backend implementation, follow {doc}`../python_sdk/customization`. See {doc}`../python_sdk/reference/langchain` for API signatures.