# deepagents-azure-blob-backend

[![CI](https://github.com/oddrationale/deepagents-azure-blob-backend/actions/workflows/ci.yml/badge.svg)](https://github.com/oddrationale/deepagents-azure-blob-backend/actions/workflows/ci.yml)
[![codecov](https://codecov.io/gh/oddrationale/deepagents-azure-blob-backend/graph/badge.svg)](https://codecov.io/gh/oddrationale/deepagents-azure-blob-backend)
[![PyPI Version](https://img.shields.io/pypi/v/deepagents-azure-blob-backend.svg)](https://pypi.python.org/pypi/deepagents-azure-blob-backend)
[![Supported Python Versions](https://img.shields.io/pypi/pyversions/deepagents-azure-blob-backend.svg)](https://pypi.python.org/pypi/deepagents-azure-blob-backend)
[![API Docs](https://img.shields.io/badge/docs-API%20reference-brightgreen)](https://oddrationale.github.io/deepagents-azure-blob-backend)
[![autofix.ci](https://img.shields.io/badge/autofix.ci-enabled-success)](https://github.com/oddrationale/deepagents-azure-blob-backend/actions/workflows/autofix.ci.yml)

Azure Blob Storage filesystem backend for [LangChain Deep Agents](https://github.com/langchain-ai/deepagents).

Deep Agents exposes a `BackendProtocol` — a pluggable interface for file operations (`read`, `write`, `edit`, `ls`, `glob`, `grep`) that the agent uses as its virtual filesystem. This package provides an Azure Blob Storage implementation of that interface.

## Installation

```bash
pip install deepagents-azure-blob-backend
```

Or with [uv](https://docs.astral.sh/uv/):

```bash
uv add deepagents-azure-blob-backend
```

## Quick Start

```python
import asyncio
from deepagents import create_deep_agent
from deepagents_azure_blob_backend import AzureBlobBackend, AzureBlobConfig

async def main():
    config = AzureBlobConfig(
        account_url="https://<your-account>.blob.core.windows.net",
        container_name="agent-workspace",
        prefix="session-001/",
    )
    backend = AzureBlobBackend(config)

    agent = create_deep_agent(backend=backend)

    result = await agent.ainvoke(
        {"messages": [{"role": "user", "content": "Create a hello world script at /hello.py"}]},
    )
    print(result["messages"][-1].content)
    await backend.close()

asyncio.run(main())
```

## Configuration

```python
from deepagents_azure_blob_backend import AzureBlobConfig

config = AzureBlobConfig(
    account_url="https://<account>.blob.core.windows.net",
    container_name="my-container",
    prefix="agent-workspace/",     # Namespace isolation for multi-agent setups
    max_concurrency=8,             # Parallel blob ops for grep/glob
    encoding="utf-8",
)
```

### Authentication

`AzureBlobConfig` supports five mutually exclusive authentication methods. Set at most one credential source — if none is provided, `DefaultAzureCredential` is used automatically. `account_url` is required for all methods except connection string:

```python
# 1. Connection string (e.g., Azurite or Azure Portal)
config = AzureBlobConfig(
    container_name="test",
    connection_string="UseDevelopmentStorage=true",
)

# 2. Account key
config = AzureBlobConfig(
    account_url="https://<account>.blob.core.windows.net",
    container_name="my-container",
    account_key="your-storage-account-key",
)

# 3. SAS token
config = AzureBlobConfig(
    account_url="https://<account>.blob.core.windows.net",
    container_name="my-container",
    sas_token="sv=2021-06-08&ss=b&srt=co&sp=rwdlacitfx&se=...",
)

# 4. Credential object (any Azure credential)
from azure.identity.aio import ClientSecretCredential
config = AzureBlobConfig(
    account_url="https://<account>.blob.core.windows.net",
    container_name="my-container",
    credential=ClientSecretCredential(tenant_id, client_id, client_secret),
)

# 5. Default (AAD) — omit all credential fields
config = AzureBlobConfig(
    account_url="https://<account>.blob.core.windows.net",
    container_name="my-container",
)
```

The default path uses `DefaultAzureCredential`, which supports `az login`, managed identity, workload identity federation (OIDC), and environment variables.

## Supported Operations

All methods from `BackendProtocol`:

| Method | Async | Description |
|--------|-------|-------------|
| `ls_info(path)` | `als_info` | List directory with synthesized subdirectories |
| `read(path, offset, limit)` | `aread` | Read file with line numbers |
| `write(path, content)` | `awrite` | Create new file (errors if exists) |
| `edit(path, old, new)` | `aedit` | String replacement editing |
| `glob_info(pattern, path)` | `aglob_info` | Glob pattern file matching |
| `grep_raw(pattern, path, glob)` | `agrep_raw` | Literal text search across files |
| `upload_files(files)` | `aupload_files` | Batch binary upload |
| `download_files(paths)` | `adownload_files` | Batch binary download |

## Development

```bash
# Install dev dependencies
uv sync --group dev

# Run unit tests
uv run pytest tests/test_backend_unit.py -v

# Run integration tests (requires Azurite)
docker run -p 10000:10000 mcr.microsoft.com/azure-storage/azurite azurite-blob --skipApiVersionCheck --blobHost 0.0.0.0
uv run pytest tests/test_backend_integration.py -v

# Lint and format
uv run ruff check .
uv run ruff format .

# Type check
uv run ty check
```

See the [examples/](examples/) folder for runnable scripts with setup instructions.

## License

MIT
